Linux kernel 使用spinlock在内核驱动程序和中断处理程序之间进行同步

Linux kernel 使用spinlock在内核驱动程序和中断处理程序之间进行同步,linux-kernel,kernel,linux-device-driver,Linux Kernel,Kernel,Linux Device Driver,我读这篇文章是为了了解spinlock。我尝试在我的内核驱动程序中使用它 以下是我的驱动程序代码需要执行的操作: 在f1()中,它将获得旋转锁,调用方可以调用f2()将等待锁定,因为旋转锁未被解锁。旋转锁将在我的中断处理程序中解锁(由硬件触发) 硬件将向应用程序发送一个中断,我的中断处理程序将调用spin_unlock(&mylock) 我的问题是如果我打电话 f1() f2()//我想阻止它,直到中断返回setting REG_ADDR完成 当我运行这个程序时,我在内核中得到一个异常,称为死锁

我读这篇文章是为了了解spinlock。我尝试在我的内核驱动程序中使用它

以下是我的驱动程序代码需要执行的操作: 在f1()中,它将获得旋转锁,调用方可以调用f2()将等待锁定,因为旋转锁未被解锁。旋转锁将在我的中断处理程序中解锁(由硬件触发)

硬件将向应用程序发送一个中断,我的中断处理程序将调用spin_unlock(&mylock)

我的问题是如果我打电话 f1() f2()//我想阻止它,直到中断返回setting REG_ADDR完成

当我运行这个程序时,我在内核中得到一个异常,称为死锁“INFO:检测到可能的递归锁定”

如何重新编写代码,使内核不认为我有死锁

我希望我的驱动程序代码等待,直到HW向我发送一个中断,说明设置REG_ADDR已完成


谢谢。

首先,由于您希望在等待中断时阻塞,因此不应使用自旋锁锁定硬件,因为您可能会长时间持有该锁。如果频繁调用该函数,在这种情况下使用自旋锁将浪费大量CPU周期

我将首先使用一个锁来锁定对所讨论的硬件寄存器的访问,这样其他内核线程就不能同时修改寄存器。互斥锁被允许休眠,因此如果它不能获得锁,线程可以一直休眠,直到它能够获得锁为止

然后,我将使用一个函数来阻塞线程,直到中断到达并发出位已完成设置的信号

另外,作为旁白,我注意到您正试图使用以下表达式访问外围设备
REG\u ADDR+=FLAG\u A。在内核中,这不是正确的方法。它似乎可以工作,但在某些架构上会中断。你应该使用类似的工具


其中
REG_ADDR
是您从
ioremap

获得的地址,我同意Michael的观点,即当任何资源(内存/变量/代码段)有可能在内核/用户线程之间共享时,必须使用自旋锁、信号量、互斥锁(或任何其他锁定机制)。
我建议不要使用任何可用的锁定原语,而是使用内核中可用的其他睡眠功能,如
wait\u event\u interruptable
wake\u up
。它们很简单,很容易在代码中利用它们。您可以在网上找到它的详细信息和漏洞。

我想您不了解自旋锁是如何工作的以及它们的用途。自旋锁通常用于有效地保护不太可能发生争用的代码段(锁只保留很短的时间)。你到底想实现什么?我想在HW寄存器1中设置一个位。当设置寄存器完成时,HW将发送一个中断。我希望我的驱动程序代码阻塞,直到中断从硬件发送。如果两个内核线程同时设置相同的位,会发生什么?谁从中断处理程序获得通知?在我的原始代码中,它在设置寄存器之前锁定一个自旋锁,因此没有两个内核线程可以同时设置相同的位。
void f1() {
spin_lock(&mylock);
// write hardware 
REG_ADDR += FLAG_A;

}

void f2() {
spin_lock(&mylock);
//...
}
unsigned long reg;
reg = readl(REG_ADDR);
reg |= FLAG_A;
writel(reg, REG_ADDR);