以下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
条件吗?这意味着锁定应该在读取标志时保持,对吗?
答案 0 :(得分:2)
是的,你需要一把锁。使用给定的示例(使用int
并且未提及特定的arch),访问interrupt_flag
时可能会中断进程上下文。从IRQ返回后,它可能会继续,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回答中的'正确对齐的整数)。
现在,如果我们可以更改代码并使用atomic_t
而不是int
标志,那么就不需要锁定。
答案 1 :(得分:2)
在给出的示例中不需要锁定。在存储标志之后和加载之前需要内存屏障 - 以确保标志的可见性 - 但wait_event_ *和wake_up_ *函数提供了这些。请参阅本文档中标题为“睡眠和唤醒功能”的部分:https://www.kernel.org/doc/Documentation/memory-barriers.txt
在添加锁之前,请考虑受保护的内容。如果您要设置两个或多个单独的数据,并且需要确保另一个cpu / core没有看到不完整的中间状态(在您启动之后但在完成之前),则通常需要锁定。在这种情况下,保护标志值的存储/加载没有意义,因为正确对齐的整数的存储和加载总是原子的。
因此,根据您的驱动程序正在执行的操作,您很可能需要锁定,但您提供的代码段不需要锁定。