此 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 还在做什么,您很可能确实需要锁,但您提供的代码段不需要锁。
以下 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 还在做什么,您很可能确实需要锁,但您提供的代码段不需要锁。