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

Linux kernel 此Linux设备驱动程序代码中是否需要自旋锁?,linux-kernel,linux-device-driver,embedded-linux,Linux Kernel,Linux Device Driver,Embedded Linux,以下Linux设备驱动程序代码是否安全,或者我是否需要使用自旋锁保护对中断\u标志的访问 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;

以下Linux设备驱动程序代码是否安全,或者我是否需要使用自旋锁保护对
中断\u标志的访问

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\u lock()
?我认为
spin\u lock\u irq()
是合适的,但是当我尝试时,我得到了一个关于启用中断的irq处理程序的警告


wait\u event\u interruptable
评估
interrupt\u标志!=0
条件?这意味着锁应该在读取标志时保持,对吗?

是的,您需要一个锁。对于给定的示例(使用
int
且未提及特定的arch),在访问
interrupt\u标志时,可能会中断进程上下文。从IRQ返回后,它可能继续,并且
中断\u标志
可能处于不一致状态

试试这个:

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;
}
IMHO,代码必须在不做任何arch或编译器相关假设的情况下编写(如Gil Hamilton answer中的“正确对齐的整数”)


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

在给出的示例中不需要锁。存储标志之后和加载之前需要内存屏障,以确保标志的可见性,但wait_event_*和wake_up_*函数提供了这些功能。请参阅本文档中标题为“睡眠和唤醒功能”的部分:

在添加锁之前,考虑正在保护的内容。通常,如果要设置两个或多个独立的数据段,并且需要确保另一个cpu/内核不会看到不完整的中间状态(在启动后但在完成之前),则需要锁定。在这种情况下,没有必要保护标志值的存储/加载,因为正确对齐的整数的存储和加载始终是原子的


因此,根据驱动程序正在执行的其他操作,很可能确实需要锁,但您提供的代码段不需要锁。

试试
spin\u lock\u irqsave()
它几乎肯定需要保护。但是,如果没有更多信息,就无法确定旋转锁或不同的锁定机制是否最合适。为什么?锁可以防止什么竞争?Linux内核运行的体系结构中没有在文件范围内声明int变量会导致未对齐的变量,也没有对对齐int的访问不是原子的体系结构。参考“正确对齐整数的存储和加载始终是原子的”部分,如果没有
wait.*
wake.*
函数,这是否意味着在进程上下文和内核上下文之间共享
int
时不需要锁定?SMP呢?没错。如果您只共享一个int,则不需要锁。SMP不会改变这一点。假设var为0。假设core0在某个点将var设置为1。假设core1正在查看var。在任何时间点,core1都会将var视为0(更改前)或1(更改后)。在设置(或检查)锁的周围抓住锁并不会改变这一点。core1仍然可以看到0或1。锁定无法完成任何事情。我同意这对大多数拱门来说是正确的。我浏览了
atomic\u t
(它只是
int
的包装)和
atomic\u set()
。在几乎所有ARCH上,
atomic_set()
是一个直接将内部
int
设置为所需值的宏。除了sparc、parisc和metag之外,在sparc、parisc和metag上,此操作受锁保护,有时禁用irq(例如,请参阅arch/sparc/lib/atomic32.c)。我不确定这种保护的原因,但我想这使你的答案对所有的执政官都是正确的,除了那三个。还是我错过了什么?谢谢。这样做是为了在不在硬件中实现它的架构上更好地处理其他“原子”操作(如atomic_xchg)。例如,如果您想将一个值设置为1,但前提是该值当前不是零(例如),并且您没有像Intel(或其他等效机制,如LL/SC)那样的原子XCHG指令,则自旋锁是实现这一点的一种方法,但您必须确保原子_集也拥有锁。有关示例,请参见metag拱门中原子_集的注释。这是一系列相关操作,不得以中间状态出现。