Linux内核中断处理程序互斥保护?
我是否需要保护我的中断处理程序被同一中断多次调用?
鉴于以下代码,我不确定应该进行的系统调用。我在当前的实现中遇到了罕见的随机死锁:-
void interrupt_handler(void)
{
down_interruptible(&sem); // or use a lock here ?
clear_intr(); // clear interrupt source on H/W
wake_up_interruptible(...);
up(&sem); // unlock?
return IRQ_HANDLED;
}
void set/clear_intr()
{
spin_lock_irq(&lock);
RMW(x); // set/clear a bit by read/modify/write the H/W interrupt routing register
spin_unlock_irq(&lock);
}
void read()
{
set_intr(); // same as clear_intr, but sets a bit
wait_event_interruptible(...);
}
interrupt_handler
:down_interruptible
应该是spin_lock_irq
/spin_lock_irqsave
> /local_irq_disable
?set/clear_intr
:spin_lock_irq
应该是spin_lock_irqsave
/local_irq_disable
吗?- 它(硬件 -> 内核 -> 驱动程序处理程序)能否继续生成/获取中断直到其被清除?
interrupt_handler
可以在其中继续被调用吗? - 如果当前实现的中断处理程序是可重入的,那么它会在
down_interruptible
上阻塞吗?
来自LDD3:-
必须是可重入的——它必须能够同时在多个上下文中运行。
编辑1)经过一些很好的帮助后,建议是:-
- 从
interrupt_handler
中删除down_interruptible
- 将
spin_lock_irq
移到set/clear方法之外(不需要< code>spin_lock_irqsave 你说?)我真的没有看到这样做的好处?!
代码:-
void interrupt_handler(void)
{
read_reg(y); // eg of other stuff in the handler
spin_lock_irq(&lock);
clear_intr(); // clear interrupt source on H/W
spin_unlock_irq(&lock);
wake_up_interruptible(...);
return IRQ_HANDLED;
}
void set/clear_intr()
{
RMW(x);
}
void read()
{
error_checks(); // eg of some other stuff in the read method
spin_lock_irq(&lock);
set_intr(); // same as clear_intr, but sets a bit
spin_unlock_irq(&lock);
wait_event_interruptible(...);
// more code here...
}
Edit2)在阅读了更多SO帖子后:阅读为什么在中断上下文中执行的内核代码/线程无法休眠?链接到 Robert Loves 文章,我读了这个:
一些中断处理程序(在 Linux 作为快速中断处理程序)运行 所有中断都在本地 处理器已禁用。这样做是为了 确保中断处理程序运行 不间断地,尽快 可能的。更何况,全部中断 处理程序以其当前运行 所有中断线均禁用 处理器。这确保了两个 相同的中断处理程序 中断线不运行 同时。它还可以防止设备 驱动程序编写者不必处理 递归中断,这使情况变得复杂 编程。
我启用了快速中断(SA_INTERRUPT)!所以不需要互斥/锁/信号量/旋转/等待/睡眠/等等!
Do I need to protect my interrupt handler being called many times for the same interrupt?
Given the following code, I am not sure on the system calls I should make. I am getting rare, random dead-locks with this current implementation :-
void interrupt_handler(void)
{
down_interruptible(&sem); // or use a lock here ?
clear_intr(); // clear interrupt source on H/W
wake_up_interruptible(...);
up(&sem); // unlock?
return IRQ_HANDLED;
}
void set/clear_intr()
{
spin_lock_irq(&lock);
RMW(x); // set/clear a bit by read/modify/write the H/W interrupt routing register
spin_unlock_irq(&lock);
}
void read()
{
set_intr(); // same as clear_intr, but sets a bit
wait_event_interruptible(...);
}
- Should
interrupt_handler
:down_interruptible
bespin_lock_irq
/spin_lock_irqsave
/local_irq_disable
? - Should
set/clear_intr
:spin_lock_irq
bespin_lock_irqsave
/local_irq_disable
? - Can it (H/W -> kernel -> driver handler) keep generating/getting interrupts until its cleared? Can the
interrupt_handler
keep getting called while within it? - If as currently implemented the interrupt handler is reentrant then will it block on the
down_interruptible
?
From LDD3 :-
must be reentrant—it must be capable of running in more than one context at the same time.
Edit 1) after some nice help, suggestions are :-
- remove
down_interruptible
from withininterrupt_handler
- Move
spin_lock_irq
outside set/clear methods (no need forspin_lock_irqsave
you say?) I really don't see the benefit to this?!
Code :-
void interrupt_handler(void)
{
read_reg(y); // eg of other stuff in the handler
spin_lock_irq(&lock);
clear_intr(); // clear interrupt source on H/W
spin_unlock_irq(&lock);
wake_up_interruptible(...);
return IRQ_HANDLED;
}
void set/clear_intr()
{
RMW(x);
}
void read()
{
error_checks(); // eg of some other stuff in the read method
spin_lock_irq(&lock);
set_intr(); // same as clear_intr, but sets a bit
spin_unlock_irq(&lock);
wait_event_interruptible(...);
// more code here...
}
Edit2) After reading some more SO posts : reading Why kernel code/thread executing in interrupt context cannot sleep? which links to Robert Loves article, I read this :
some interrupt handlers (known in
Linux as fast interrupt handlers) run
with all interrupts on the local
processor disabled. This is done to
ensure that the interrupt handler runs
without interruption, as quickly as
possible. More so, all interrupt
handlers run with their current
interrupt line disabled on all
processors. This ensures that two
interrupt handlers for the same
interrupt line do not run
concurrently. It also prevents device
driver writers from having to handle
recursive interrupts, which complicate
programming.
And I have fast interrupts enabled (SA_INTERRUPT)! So no need for mutex/locks/semaphores/spins/waits/sleeps/etc/etc!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
不要在中断上下文中使用信号量,而是使用
spin_lock_irqsave
。引用LDD3:至于第2点,让你的
set_intr
和clear_intr
要求调用者锁定自旋锁,否则你会发现你的代码死锁。再次来自LDD3:Don't use semaphores in interrupt context, use
spin_lock_irqsave
instead. quoting LDD3:As for point 2, make your
set_intr
andclear_intr
require the caller to lock the spinlock, otherwise you'll find your code deadlocking. Again from LDD3:在中断上下文中使用自旋锁,因为如果没有获取锁,您不想在中断上下文中休眠。
Use spinlock in interrupt context because you don't want to sleep in interrupt context if you didn't acquired a lock.
您发布的代码看起来不像设备驱动程序 irq 处理程序。
内核驱动程序中的 irq 处理程序返回 irqreturn_t 并接受 int irq_no, void * data 作为参数。
您还没有指定是否要注册线程处理程序或非线程处理程序。
无论您是否持有任何自旋锁,非线程 irq 处理程序都不能有任何休眠调用。
wait_event、互斥锁、信号量等都是休眠调用,不得在非线程 irq 处理程序中使用。但是,您可以持有自旋锁以防止中断处理程序中断。这将确保可屏蔽 irq 和调度程序不会在中间中断您的 irq 处理程序。
在线程 irq 处理程序中,可以使用睡眠调用(等待队列、互斥体等)之类的东西,但仍然不建议使用。
The code you posted does not look like a device driver irq handler.
The irq handlers in kernel drivers return irqreturn_t and take in int irq_no, void * data as arguements.
You have also not specified if you are registering a threaded handler or a non threaded handler.
A non threaded irq handler cannot have any sleeping calls whether or not you hold any spinlocks.
wait_event, mutex, semaphore, etc, are all sleeping calls and must not be used in a non threaded irq handler. You can however, hold a spinlock to prevent interruption to your interrupt handler. This will ensure that maskable irqs and scheduler do not interrupt your irq handler in the middle.
In a threaded irq handler, such things as sleeping calls (wait queues, mutex, etc) can be used but are still not recommended.