此 Linux 设备驱动程序代码中是否需要自旋锁?

Is a spinlock necessary in this Linux device driver code?

以下 Linux 设备驱动程序代码是否安全,或者我是否需要使用自旋锁来保护对 interrupt_flag 的访问?

static DECLARE_WAIT_QUEUE_HEAD(wq_head);
static int interrupt_flag = 0;

static ssize_t my_write(struct file* filp, const char* __user buffer, size_t length, loff_t* offset)
{
   interrupt_flag = 0;   
   wait_event_interruptible(wq_head, interrupt_flag != 0);
}

static irqreturn_t handler(int irq, void* dev_id)
{
   interrupt_flag = 1;
   wake_up_interruptible(&wq_head);
   return IRQ_HANDLED;
}

基本上,我在 my_write() 中启动一些事件并等待中断指示它完成。

如果可以,我需要使用哪种形式的 spin_lock()?我认为 spin_lock_irq() 是合适的,但是当我尝试这样做时,我收到了有关启用中断的 IRQ 处理程序的警告。

wait_event_interruptible 不评估 interrupt_flag != 0 条件吗?这意味着在读取标志时应该保持锁定,对吗?

是的,你需要一把锁。对于给定的示例(使用 int 并且未提及特定架构),进程上下文可能会在访问 interrupt_flag 时中断。根据 IRQ return,它可能会继续并且 interrupt_flag 可能会处于不一致状态。

试试这个:

static DECLARE_WAIT_QUEUE_HEAD(wq_head);
static int interrupt_flag = 0;
DEFINE_SPINLOCK(lock);

static ssize_t my_write(struct file* filp, const char* __user buffer, size_t length, loff_t* offset)
{
   /* spin_lock_irq() or spin_lock_irqsave() is OK here */
   spin_lock_irq(&lock);
   interrupt_flag = 0;
   spin_unlock_irq(&lock);

   wait_event_interruptible(wq_head, interrupt_flag != 0);
}

static irqreturn_t handler(int irq, void* dev_id)
{
   unsigned long flags;

   spin_lock_irqsave(&lock, flags);
   interrupt_flag = 1;
   spin_unlock_irqrestore(&lock, flags);

   wake_up_interruptible(&wq_head);
   return IRQ_HANDLED;
}

恕我直言,代码必须在不做任何架构或编译器相关假设的情况下编写(如 Gil Hamilton 答案中的 'properly aligned integer')。

现在如果我们可以更改代码并使用 atomic_t 而不是 int 标志,那么就不需要锁了。

在给出的例子中不需要锁。在标志存储之后和加载之前需要内存屏障——以确保标志的可见性——但 wait_event_* 和 wake_up_* 函数提供了这些。请参阅本文档中标题为 "Sleep and wake-up functions" 的部分:https://www.kernel.org/doc/Documentation/memory-barriers.txt

在加锁之前,考虑一下被保护的是什么。如果您要设置两个或多个单独的数据片段并且您需要确保另一个 cpu/core 不会看到不完整的中间状态(在您开始之后但在您完成之前),通常需要锁定。在这种情况下,保护标志值的存储/加载没有意义,因为正确对齐的整数的存储和加载始终是原子的。

因此,根据您的 driver 还在做什么,您很可能确实需要锁,但您提供的代码段不需要锁。