In continuation of the previous text 第10章:中断处理-11: Workqueues, let's GO ahead.
Interrupt Sharing
The notion of an IRQ conflict is almost synonymous with the PC architecture. In the past, IRQ lines on the PC have not been able to serve more than one device, and there have never been enough of them. As a result, frustrated users have often spent much time with their computer case open, trying to find a way to make all of their peripherals play well together.
Modern hardware, of course, has been designed to allow the sharing of interrupts; the PCI bus requires it. Therefore, the Linux kernel supports interrupt sharing on all buses, even those (such as the ISA bus) where sharing has traditionally not been supported. Device drivers for the 2.6 kernel should be written to work with shared interrupts if the target hardware can support that mode of operation. Fortunately, working with shared interrupts is easy, most of the time.
中断冲突的概念几乎是 PC 架构的代名词。过去,PC 上的 IRQ 线无法同时为多个设备服务,且数量一直不足。这导致用户常常不得不打开机箱,花费大量时间尝试让所有外设正常协同工作,十分困扰。
当然,现代硬件在设计时已支持中断共享 ——PCI 总线更是强制要求这一特性。因此,Linux 内核在所有总线上都支持中断共享,甚至包括传统上不支持共享的总线(如 ISA 总线)。针对 2.6 内核编写的设备驱动,若目标硬件支持中断共享模式,就应适配这一功能。幸运的是,大多数情况下,实现中断共享并不复杂。
补充说明:
-
中断共享的核心意义
解决 IRQ 资源紧张问题,让多个设备复用同一条中断线,尤其适配 PCI 等现代总线的设计规范,减少硬件资源浪费。
-
支持中断共享的硬件要求
设备需具备两个关键能力:一是能识别自身是否触发中断(通过硬件状态寄存器判断),二是不会在未被确认时持续占用中断线,避免干扰其他共享设备。
-
驱动适配中断共享的核心步骤
-
注册中断时添加
SA_SHIRQ标志; -
dev_id参数不可为NULL(用于内核区分共享同一 IRQ 的不同设备); -
中断处理程序需先检查是否为自身设备触发的中断,非自身中断则返回
IRQ_NONE。
-
-
ISA 总线的中断共享限制
尽管内核支持,但多数老式 ISA 设备硬件设计不支持中断共享(无独立中断确认机制),因此实际中 ISA 设备仍常使用独占 IRQ。
Installing a Shared Handler
Shared interrupts are installed through request_irq just like nonshared ones, but there are two differences:
• The SA_SHIRQ bit must be specified in the flags argument when requesting the interrupt.
• The dev_id argument must be unique. Any pointer into the module’s address space will do, but dev_id definitely cannot be set to NULL.
共享中断通过 request_irq 安装,流程与非共享中断类似,但有两个关键区别:
・请求中断时,必须在 flags 参数中指定 SA_SHIRQ 标志位。
・dev_id 参数必须唯一。可以使用模块地址空间内的任意指针,但 dev_id 绝对不能设为 NULL。
The kernel keeps a list of shared handlers associated with the interrupt, and dev_id can be thought of as the signature that differentiates between them. If two drivers were to register NULL as their signature on the same interrupt, things might get mixed up at unload time, causing the kernel to oops when an interrupt arrived. For this reason, modern kernels complain loudly if passed a NULL dev_id when registering shared interrupts. When a shared interrupt is requested, request_irq succeeds if one of the following is true:
• The interrupt line is free.
• All handlers already registered for that line have also specified that the IRQ is to be shared.
内核会维护一个与该中断关联的共享处理程序列表,dev_id 可视为区分这些处理程序的 “标识”。若两个驱动在同一中断上注册时都将 dev_id 设为 NULL,卸载时可能出现混乱,导致中断触发时内核崩溃(oops)。因此,现代内核在注册共享中断时若收到 NULL 的 dev_id,会直接报错。
当请求共享中断时,满足以下任一条件,request_irq 会执行成功:
・该中断线当前空闲。
・该中断线上已注册的所有处理程序均指定了中断共享(即都带了 SA_SHIRQ 标志)。
Whenever two or more drivers are sharing an interrupt line and the hardware interrupts the processor on that line, the kernel invokes every handler registered for that interrupt, passing each its own dev_id. Therefore, a shared handler must be able to recognize its own interrupts and should quickly exit when its own device has not interrupted. Be sure to return IRQ_NONE whenever your handler is called and finds that the device is not interrupting.
当两个或多个驱动共享一条中断线,且硬件触发该中断时,内核会调用所有已注册的处理程序,并向每个程序传递其专属的 dev_id。因此,共享中断处理程序必须能识别自身设备的中断 —— 若发现不是自身设备触发的,应快速退出,并返回 IRQ_NONE。
If you need to probe for your device before requesting the IRQ line, the kernel can’t help you. No probing function is available for shared handlers. The standard probing mechanism works if the line being used is free, but if the line is already held by another driver with sharing capabilities, the probe fails, even if your driver would have worked perfectly. Fortunately, most hardware designed for interrupt sharing is also able to tell the processor which interrupt it is using, thus eliminating the need for explicit probing.
若需要在请求中断线前探测设备,内核无法提供支持 —— 共享处理程序没有对应的探测函数。标准探测机制仅在中断线空闲时有效;若该中断线已被其他支持共享的驱动占用,探测会失败,即便你的驱动本可正常工作。幸运的是,大多数支持中断共享的硬件,都能主动告知处理器其使用的中断号,因此无需显式探测。
Releasing the handler is performed in the normal way, using free_irq. Here the dev_id argument is used to select the correct handler to release from the list of shared handlers for the interrupt. That’s why the dev_id pointer must be unique.
卸载处理程序的操作与常规方式一致,使用 free_irq 即可。此时 dev_id 参数用于从该中断的共享处理程序列表中,精准选择要卸载的处理程序 —— 这也是 dev_id 必须唯一的原因。
A driver using a shared handler needs to be careful about one more thing: it can’t play with enable_irq or disable_irq. If it does, things might go haywire for other devices sharing the line; disabling another device’s interrupts for even a short time may create latencies that are problematic for that device and it’s user. Generally, the programmer must remember that his driver doesn’t own the IRQ, and its behavior should be more “social” than is necessary if one owns the interrupt line.
使用共享处理程序的驱动还需注意一点:不能调用 enable_irq 或 disable_irq。若执行这些操作,可能导致共享该中断线的其他设备出现异常;即使短暂禁用其他设备的中断,也可能造成其延迟超标,影响设备使用和用户体验。通常,开发者必须牢记:驱动并不 “独占” 该 IRQ,相比独占中断线的场景,其行为需更 “友好”,避免影响其他设备。
补充说明:
-
最佳实践是将驱动的设备结构体指针作为dev_id的合理选择dev_id,既保证唯一性,又能在中断处理程序中直接通过dev_id访问设备信息,无需额外查找。示例:// 分配设备结构体 struct my_dev *dev = devm_kzalloc(...); // 以设备结构体为dev_id request_irq(irq, my_handler, SA_SHIRQ, "my_dev", dev); -
共享中断处理程序的核心逻辑
处理程序必须先通过硬件寄存器判断 “是否为本设备触发中断”(如检查中断挂起位),非自身中断立即返回IRQ_NONE,避免无效执行消耗 CPU 资源。示例:irqreturn_t my_handler(int irq, void *dev_id, struct pt_regs *regs) { struct my_dev *dev = dev_id; if (!(readl(&dev->regs->status) & IRQ_PENDING)) // 检查本设备中断标志 return IRQ_NONE; // 非本设备中断,直接返回 // 处理本设备中断逻辑 return IRQ_HANDLED; } -
共享中断的禁用替代方案
无法使用
disable_irq,若需临时屏蔽本设备中断,应通过硬件层面实现(如写入设备寄存器禁用中断),而非操作全局中断线,避免影响其他共享设备。 -
中断共享的兼容性注意
若中断线上已有不支持共享的处理程序(无
SA_SHIRQ标志),新驱动请求共享中断会失败。因此,适配共享中断的驱动需在文档中明确说明,且优先使用硬件自动上报 IRQ 的能力,避免依赖探测。
Running the Handler
As suggested earlier, when the kernel receives an interrupt, all the registered handlers are invoked. A shared handler must be able to distinguish between interrupts that it needs to handle and interrupts generated by other devices.
如前所述,当内核收到中断时,会调用所有已注册的处理程序。共享中断处理程序必须能够区分 “自身需要处理的中断” 和 “其他设备产生的中断”。
Loading short with the option shared=1 installs the following handler instead of the default:
以 shared=1 选项加载 short 模块时,会安装以下处理程序替代默认版本:
irqreturn_t short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
int value, written;
struct timeval tv;
/* If it wasn't short, return immediately */
value = inb(short_base);
if (!(value & 0x80))
return IRQ_NONE;
/* clear the interrupting bit */
outb(value & 0x7F, short_base);
/* the rest is unchanged */
do_gettimeofday(&tv);
written = sprintf((char *)short_head,"%08u.%06u\n",
(int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));
short_incr_bp(&short_head, written);
wake_up_interruptible(&short_queue); /* awake any reading process */
return IRQ_HANDLED;
}
An explanation is due here. Since the parallel port has no “interrupt-pending” bit to check, the handler uses the ACK bit for this purpose. If the bit is high, the interrupt being reported is for short, and the handler clears the bit.
The handler resets the bit by zeroing the high bit of the parallel interface’s data port—short assumes that pins 9 and 10 are connected together. If one of the other devices sharing the IRQ with short generates an interrupt, short sees that its own line is still inactive and does nothing.
由于并行端口没有可检查的 “中断挂起位”,该处理程序通过 ACK 位(数据寄存器最高位,0x80)判断是否为自身设备的中断:
-
读取并行端口数据寄存器的值,检查最高位是否为 1;
-
若为 1,说明是
short设备触发的中断,需先清除该位(将最高位置 0); -
若为 0,说明是共享同一 IRQ 的其他设备触发的中断,直接返回
IRQ_NONE,不执行后续处理。
清除中断位的原理:short 模块假设并行端口的 9 号和 10 号引脚已短接,通过将数据寄存器最高位置 0,可终止中断触发信号,避免内核重复响应。
A full-featured driver probably splits the workinto top and bottom halves, of course, but that’s easy to add and does not have any impact on the code that implements sharing. A real driver would also likely use the dev_id argument to determine which, of possibly many, devices might be interrupting.
完整驱动通常会将 “清除中断位、记录时间戳” 作为上半部快速执行,将 “数据处理、唤醒进程” 交给下半部(tasklet 或工作队列),既保证中断响应速度,又不影响共享设备的中断处理。
Note that if you are using a printer (instead of the jumper wire) to test interrupt management with short, this shared handler won’t workas advertised, because the printer protocol doesn’t allow for sharing, and the driver can’t know whether the interrupt was from the printer.
若使用打印机(而非短接引脚)测试 short 模块的共享中断,该处理程序无法正常工作:打印机协议不支持中断共享,且驱动无法通过硬件状态区分 “打印机中断” 和 “short 设备中断”。
补充说明:
-
共享中断处理的核心原则
必须先通过硬件状态判断 “是否为本设备中断”,非自身中断需快速返回
IRQ_NONE,避免占用 CPU 资源、干扰其他设备。判断依据通常是设备的 “中断挂起位”(如多数 PCI 设备),short模块因硬件限制使用 ACK 位替代。
-1
2473

被折叠的 条评论
为什么被折叠?



