Linux内核中断处理程序互斥保护?

发布于 2024-11-18 03:26:16 字数 2576 浏览 5 评论 0原文

我是否需要保护我的中断处理程序被同一中断多次调用?

鉴于以下代码,我不确定应该进行的系统调用。我在当前的实现中遇到了罕见的随机死锁:-

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(...);
}
  1. interrupt_handler:down_interruptible 应该是 spin_lock_irq / spin_lock_irqsave > / local_irq_disable
  2. set/clear_intr:spin_lock_irq 应该是 spin_lock_irqsave / local_irq_disable 吗?
  3. 它(硬件 -> 内核 -> 驱动程序处理程序)能否继续生成/获取中断直到其被清除? interrupt_handler 可以在其中继续被调用吗?
  4. 如果当前实现的中断处理程序是可重入的,那么它会在 down_interruptible 上阻塞吗?

来自LDD3:-

必须是可重入的——它必须能够同时在多个上下文中运行。


编辑1)经过一些很好的帮助后,建议是:-

  1. interrupt_handler中删除down_interruptible
  2. 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(...);
}
  1. Should interrupt_handler:down_interruptible be spin_lock_irq / spin_lock_irqsave / local_irq_disable?
  2. Should set/clear_intr:spin_lock_irq be spin_lock_irqsave / local_irq_disable?
  3. Can it (H/W -> kernel -> driver handler) keep generating/getting interrupts until its cleared? Can the interrupt_handler keep getting called while within it?
  4. 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 :-

  1. remove down_interruptible from within interrupt_handler
  2. Move spin_lock_irq outside set/clear methods (no need for spin_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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

酷炫老祖宗 2024-11-25 03:26:16

不要在中断上下文中使用信号量,而是使用spin_lock_irqsave。引用LDD3:

如果你有一个自旋锁,可以
由运行在(硬件
或软件)中断上下文,你
必须使用 spin_lock 的一种形式
禁用中断。正在做
否则可能导致系统死锁,
迟早。如果您不访问
你的锁在硬件中断中
处理程序,但您可以通过软件来完成
中断(在代码中运行完
tasklet,例如,涵盖的主题
在第 7 章中),您可以使用
spin_lock_bh 以安全地避免死锁
同时仍然允许硬件
要服务的中断。

至于第2点,让你的set_intrclear_intr要求调用者锁定自旋锁,否则你会发现你的代码死锁。再次来自LDD3:

为了使您的锁定正常工作,
你必须写一些函数
假设他们的来电者有
已经获取了相关的锁。
通常,只有你内部的、静态的
函数可以这样写;
从外部调用的函数必须
显式处理锁定。当你
编写内部函数,使
关于锁定的假设,自己做
(以及与您一起工作的任何其他人
代码)一个忙并记录这些
明确的假设。它可以是非常
几个月后很难再回来
弄清楚你是否需要持有
锁定以调用特定函数或
不是。

Don't use semaphores in interrupt context, use spin_lock_irqsave instead. quoting LDD3:

If you have a spinlock that can be
taken by code that runs in (hardware
or software) interrupt context, you
must use one of the forms of spin_lock
that disables interrupts. Doing
otherwise can deadlock the system,
sooner or later. If you do not access
your lock in a hardware interrupt
handler, but you do via software
interrupts (in code that runs out of a
tasklet, for example, a topic covered
in Chapter 7), you can use
spin_lock_bh to safely avoid deadlocks
while still allowing hardware
interrupts to be serviced.

As for point 2, make your set_intr and clear_intr require the caller to lock the spinlock, otherwise you'll find your code deadlocking. Again from LDD3:

To make your locking work properly,
you have to write some functions with
the assumption that their caller has
already acquired the relevant lock(s).
Usually, only your internal, static
functions can be written in this way;
functions called from outside must
handle locking explicitly. When you
write internal functions that make
assumptions about locking, do yourself
(and anybody else who works with your
code) a favor and document those
assumptions explicitly. It can be very
hard to come back months later and
figure out whether you need to hold a
lock to call a particular function or
not.

依 靠 2024-11-25 03:26:16

在中断上下文中使用自旋锁,因为如果没有获取锁,您不想在中断上下文中休眠。

Use spinlock in interrupt context because you don't want to sleep in interrupt context if you didn't acquired a lock.

じее 2024-11-25 03:26:16

您发布的代码看起来不像设备驱动程序 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文