spin_lock_irqsave 与 spin_lock_irq

发布于 2024-08-27 12:19:52 字数 149 浏览 8 评论 0 原文

在 SMP 机器上,我们必须使用中断上下文中的 spin_lock_irqsave 而不是 spin_lock_irq

为什么我们要保存标志(其中包含 IF)?

是否还有另一个中断例程可以打断我们?

On an SMP machine we must use spin_lock_irqsave and not spin_lock_irq from interrupt context.

Why would we want to save the flags (which contain the IF)?

Is there another interrupt routine that could interrupt us?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(7

冷心人i 2024-09-03 12:19:52

spin_lock_irqsave 主要用于在获取自旋锁之前保存中断状态,这是因为当在中断上下文中获取锁时,自旋锁会禁用中断,并在解锁时重新启用它。保存中断状态,以便再次恢复中断。

示例:

  1. 假设在获取自旋锁之前禁用了中断 x,
  2. spin_lock_irq 将禁用中断 x 并获取锁,
  3. spin_unlock_irq 将启用中断 x。

因此,在释放锁后的第三步中,我们将启用中断 x,该中断在获取锁之前已被禁用。

因此,只有当您确定中断未被禁用时,您才应该spin_lock_irq,否则您应该始终使用spin_lock_irqsave

spin_lock_irqsave is basically used to save the interrupt state before taking the spin lock, this is because spin lock disables the interrupt, when the lock is taken in interrupt context, and re-enables it when while unlocking. The interrupt state is saved so that it should reinstate the interrupts again.

Example:

  1. Lets say interrupt x was disabled before spin lock was acquired
  2. spin_lock_irq will disable the interrupt x and take the the lock
  3. spin_unlock_irq will enable the interrupt x.

So in the 3rd step above after releasing the lock we will have interrupt x enabled which was earlier disabled before the lock was acquired.

So only when you are sure that interrupts are not disabled only then you should spin_lock_irq otherwise you should always use spin_lock_irqsave.

↘人皮目录ツ 2024-09-03 12:19:52

如果在代码开始锁定之前中断已被禁用,那么当您调用 spin_unlock_irq 时,您将以一种可能不需要的方式强制重新启用中断。相反,如果您还通过 spin_lock_irqsave 将当前中断启用状态保存在 flags 中,则在释放锁后尝试使用相同的 flags 重新启用中断,该函数只会恢复之前的状态(因此不一定启用中断)。

spin_lock_irqsave 示例:

spinlock_t mLock = SPIN_LOCK_UNLOCK;
unsigned long flags;

spin_lock_irqsave(&mLock, flags); // Save the state of interrupt enable in flags and then disable interrupts
// Critical section
spin_unlock_irqrestore(&mLock, flags); // Return to the previous state saved in flags

spin_lock_irq 示例(不带 irqsave):

spinlock_t mLock = SPIN_LOCK_UNLOCK;
unsigned long flags;

spin_lock_irq(&mLock); // Does not know if interrupts are already disabled
// Critical section
spin_unlock_irq(&mLock); // Could result in an unwanted interrupt re-enable...

If interrupts are already disabled before your code starts locking, when you call spin_unlock_irq you will forcibly re-enable interrupts in a potentially unwanted manner. If instead you also save the current interrupt enable state in flags through spin_lock_irqsave, attempting to re-enable interrupts with the same flags after releasing the lock, the function will just restore the previous state (thus not necessarily enabling interrupts).

Example with spin_lock_irqsave:

spinlock_t mLock = SPIN_LOCK_UNLOCK;
unsigned long flags;

spin_lock_irqsave(&mLock, flags); // Save the state of interrupt enable in flags and then disable interrupts
// Critical section
spin_unlock_irqrestore(&mLock, flags); // Return to the previous state saved in flags

Example with spin_lock_irq( without irqsave ):

spinlock_t mLock = SPIN_LOCK_UNLOCK;
unsigned long flags;

spin_lock_irq(&mLock); // Does not know if interrupts are already disabled
// Critical section
spin_unlock_irq(&mLock); // Could result in an unwanted interrupt re-enable...
儭儭莪哋寶赑 2024-09-03 12:19:52

除了 spin_lock_irq 之外,还需要 spin_lock_irqsave,这与除了 local_irq_disable 之外还需要 local_irq_save(flags) 的原因非常相似。以下是 Robert Love 所著的《Linux 内核开发第二版》中对该要求的一个很好的解释。

如果中断被执行,local_irq_disable()例程是危险的
在调用之前已经禁用。相应的调用
local_irq_enable() 无条件地启用中断,尽管
事实上他们一开始就出发了。相反,需要一种机制
将中断恢复到之前的状态。这是大家普遍关心的问题
因为内核中给定的代码路径可以通过 和 到达
不启用中断,具体取决于调用链。例如,
想象一下前面的代码片段是一个更大函数的一部分。
想象一下这个函数被另外两个函数调用,其中一个函数
禁用中断和不禁用中断。因为它正在成为
随着内核大小和复杂性的增加,了解所有代码变得更加困难
通向函数的路径,保存函数的状态要安全得多
禁用中断系统之前。然后,当你准备好时
重新启用中断,您只需将它们恢复到原始状态即可:

unsigned long flags;

local_irq_save(flags);    /* interrupts are now disabled */ /* ... */
local_irq_restore(flags); /* interrupts are restored to their previous
state */

请注意,这些方法至少部分是作为宏实现的,因此
flags 参数(必须定义为 unsigned long)是
看起来是按值传递的。该参数包含
包含中断状态的体系结构特定数据
系统。因为至少一种受支持的架构包含
将信息堆栈到值中(咳咳,SPARC),不能传递标志
到另一个函数(具体来说,它必须保留在同一个堆栈上
框架)。因此,保存的调用和恢复的调用
中断必须发生在同一函数中。

所有前面的函数都可以从中断和中断中调用
进程上下文。

The need for spin_lock_irqsave besides spin_lock_irq is quite similar to the reason local_irq_save(flags) is needed besides local_irq_disable. Here is a good explanation of this requirement taken from Linux Kernel Development Second Edition by Robert Love.

The local_irq_disable() routine is dangerous if interrupts were
already disabled prior to its invocation. The corresponding call to
local_irq_enable() unconditionally enables interrupts, despite the
fact that they were off to begin with. Instead, a mechanism is needed
to restore interrupts to a previous state. This is a common concern
because a given code path in the kernel can be reached both with and
without interrupts enabled, depending on the call chain. For example,
imagine the previous code snippet is part of a larger function.
Imagine that this function is called by two other functions, one which
disables interrupts and one which does not. Because it is becoming
harder as the kernel grows in size and complexity to know all the code
paths leading up to a function, it is much safer to save the state of
the interrupt system before disabling it. Then, when you are ready to
reenable interrupts, you simply restore them to their original state:

unsigned long flags;

local_irq_save(flags);    /* interrupts are now disabled */ /* ... */
local_irq_restore(flags); /* interrupts are restored to their previous
state */

Note that these methods are implemented at least in part as macros, so
the flags parameter (which must be defined as an unsigned long) is
seemingly passed by value. This parameter contains
architecture-specific data containing the state of the interrupt
systems. Because at least one supported architecture incorporates
stack information into the value (ahem, SPARC), flags cannot be passed
to another function (specifically, it must remain on the same stack
frame). For this reason, the call to save and the call to restore
interrupts must occur in the same function.

All the previous functions can be called from both interrupt and
process context.

还给你自由 2024-09-03 12:19:52

阅读 为什么在中断上下文中执行的内核代码/线程无法休眠? 链接到 Robert Loves 文章,我读了这个:

一些中断处理程序(在
Linux 作为快速中断处理程序)运行
所有中断都在本地
处理器已禁用。这样做是为了
确保中断处理程序运行
不间断,尽快
可能的。更何况,全部中断
处理程序以其当前运行
所有中断线均禁用
处理器。这确保了两个
相同的中断处理程序
中断线不运行
同时。它还可以防止设备
驱动程序编写者不必处理
递归中断,这使情况变得复杂
编程。

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.

Hello爱情风 2024-09-03 12:19:52

下面是linux内核4.15.18中的部分代码,其中显示spiin_lock_irq()将调用__raw_spin_lock_irq()。但是,它不会保存任何标志,如下面的代码部分所示,但会禁用中断。

  static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
    {
        local_irq_disable();
        preempt_disable();
        spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
        LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
    }

下面的代码显示了 spin_lock_irqsave(),它保存当前阶段的标志,然后抢占禁用。

static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
    unsigned long flags;

    local_irq_save(flags);
    preempt_disable();
    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    /*
     * On lockdep we dont want the hand-coded irq-enable of
     * do_raw_spin_lock_flags() code, because lockdep assumes
     * that interrupts are not re-enabled during lock-acquire:
     */
#ifdef CONFIG_LOCKDEP
    LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
#else
    do_raw_spin_lock_flags(lock, &flags);
#endif
    return flags;
}

Below is part of code in linux kernel 4.15.18, which shows that spiin_lock_irq() will call __raw_spin_lock_irq(). However, it will not save any flags as you can see below part of the code but disable the interrupt.

  static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
    {
        local_irq_disable();
        preempt_disable();
        spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
        LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
    }

Below code shows spin_lock_irqsave() which saves the current stage of flag and then preempt disable.

static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
    unsigned long flags;

    local_irq_save(flags);
    preempt_disable();
    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    /*
     * On lockdep we dont want the hand-coded irq-enable of
     * do_raw_spin_lock_flags() code, because lockdep assumes
     * that interrupts are not re-enabled during lock-acquire:
     */
#ifdef CONFIG_LOCKDEP
    LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
#else
    do_raw_spin_lock_flags(lock, &flags);
#endif
    return flags;
}
晚风撩人 2024-09-03 12:19:52
On an SMP machine we must use  spin_lock_irqsave and not spin_lock_irq from interrupt context.

我认为这种说法根本不真实。对于硬中断和软中断之间的共享,可以在软中断中使用 spin_lock_irq 。

每当您想在持有锁时防止硬中断或软中断抢占时,就可以使用 _irq 或 _irqsave。 _irqsave 还保存 EFLAGS 以供以后恢复。

spin_lock_irq 在 irq 处理程序中使用总是错误的,因为解锁会重新启用 IRQ,而这应该由调用者完成。根据kernel/irq/handle.c:__handle_irq_event_percpu,如果您的处理程序在启用 IRQ 的情况下返回,这将生成警告

spin_lock_irqsave 是通用的,但我很难找到一个您不能使用其他任何东西的特定用例。当您想要将解锁/锁定放入可能从硬中断和非硬中断调用的函数中时,它可能很有用,因为在这种情况下,您不能使用 spin_lock_irq 或 spin_lock(_bh)

On an SMP machine we must use  spin_lock_irqsave and not spin_lock_irq from interrupt context.

I don't think this statement is true at all. For sharing between hardirq and softirq, it's fine to just use spin_lock_irq in the softirq.

you use _irq or _irqsave whenever you want to prevent preemption by EITHER hard OR softirq while holding the lock. the _irqsave one also saves EFLAGS for later restoration.

spin_lock_irq will always be wrong to use in an irq handler since unlocking re-enables IRQ which should be done by the caller. And according to kernel/irq/handle.c:__handle_irq_event_percpu, This will generate a warning if your handler returns with IRQ enabled

spin_lock_irqsave is universal but I struggle to find a specific use case where you can use nothing else. Maybe it's useful when you want to put un/locking into a function that might be called from both hardirq AND non-hardirq because in this case, you cannot use spin_lock_irq or spin_lock(_bh)

表情可笑 2024-09-03 12:19:52

这个问题从错误的断言开始:

On an SMP machine we must use spin_lock_irqsave and not spin_lock_irq from interrupt context.

这些都不应该从中断中使用
上下文,在 SMP 或 UP 上。也就是说,spin_lock_irqsave()
可以从中断上下文中使用,因为更通用
(它可以在中断和正常上下文中使用),但是
您应该从中断上下文中使用 spin_lock()
以及来自正常上下文的 spin_lock_irq()spin_lock_irqsave()
使用 spin_lock_irq() 几乎总是错误的
在中断上下文中执行此操作,即 SMP 或 UP。它可能会起作用
因为大多数中断处理程序在本地启用 IRQ 的情况下运行,
但你不应该这样做。

更新:由于有些人误读了这个答案,让我澄清一下
它只解释了什么是中断,什么不是中断
上下文锁定。这里没有声明 spin_lock() 应该
在中断上下文中使用。可以在一个进程中使用
上下文也是如此,例如如果不需要锁定中断
语境。

This question starts from the false assertion:

On an SMP machine we must use spin_lock_irqsave and not spin_lock_irq from interrupt context.

Neither of these should be used from interrupt
context, on SMP or on UP. That said, spin_lock_irqsave()
may be used from interrupt context, as being more universal
(it can be used in both interrupt and normal contexts), but
you are supposed to use spin_lock() from interrupt context,
and spin_lock_irq() or spin_lock_irqsave() from normal context.
The use of spin_lock_irq() is almost always the wrong thing
to do in interrupt context, being this SMP or UP. It may work
because most interrupt handlers run with IRQs locally enabled,
but you shouldn't try that.

UPDATE: as some people misread this answer, let me clarify that
it only explains what is for and what is not for an interrupt
context locking. There is no claim here that spin_lock() should
only be used in interrupt context. It can be used in a process
context too, for example if there is no need to lock in interrupt
context.

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