Linux:IRQ处理线程运行时遇同IRQ,结束后如何重新触发thread_fn?
int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id);
问题场景
当IRQ处理线程(执行thread_fn)运行期间,同一IRQ再次触发,且硬IRQ处理程序(执行handler)返回IRQ_WAKE_THREAD时,如何确保处理线程结束后再次调用thread_fn?
当前存在的问题:当IRQ处理线程处于退出流程中时,若同一IRQ再次触发且硬IRQ处理程序返回IRQ_WAKE_THREAD,该中断请求会被忽略,直到下一次相同IRQ触发才会被处理。
解决方案
1. 硬中断处理函数中显式标记中断挂起
在handler返回IRQ_WAKE_THREAD前,调用irq_set_thread_irqpending(irq)手动设置线程的中断挂起状态。该函数会强制标记IRQ对应的线程需要被唤醒,即使线程处于退出流程,也会在退出前检查此状态并重新调度thread_fn。
示例代码:
irqreturn_t handler(int irq, void *dev_id) { // 完成硬件中断确认、状态读取等必要操作 ... // 标记线程需被唤醒 irq_set_thread_irqpending(irq); return IRQ_WAKE_THREAD; }
2. 将线程改为循环等待模式
修改thread_fn的实现,让线程在循环中运行,直到收到明确的退出信号(如设备卸载)。循环内处理完中断后,通过等待函数挂起,直到下一次中断触发,避免线程频繁进入退出流程。
示例框架:
irqreturn_t thread_fn(int irq, void *dev_id) { struct my_dev_data *data = dev_id; while (!kthread_should_stop()) { wait_event_interruptible(data->wait_queue, data->irq_pending || kthread_should_stop()); if (kthread_should_stop()) break; // 处理中断对应的业务逻辑 ... // 清除挂起标记 data->irq_pending = false; } return IRQ_HANDLED; }
同时在handler中设置data->irq_pending = true并唤醒等待队列,替代单纯返回IRQ_WAKE_THREAD。
3. 退出路径检查挂起请求
若必须允许线程退出,可在thread_fn的退出路径中,通过irq_thread_has_pending(irq)检查是否存在未处理的挂起请求,若有则再次执行处理逻辑或重新调度线程。
示例:
irqreturn_t thread_fn(int irq, void *dev_id) { do { // 处理中断逻辑 ... // 检查是否有新的挂起请求 } while (irq_thread_has_pending(irq) && !kthread_should_stop()); return IRQ_HANDLED; }
原理说明
Linux内核线程化中断机制中,当线程处于退出流程时,IRQ_WAKE_THREAD的唤醒信号会被内核忽略——因为内核默认认为线程即将终止,无需处理新请求。通过手动设置挂起状态或修改线程运行模式,可让内核感知到未处理的中断请求,确保thread_fn被再次调用。
内容的提问来源于stack exchange,提问作者hurryman2212

