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

时间:2014-02-01 08:00:31

标签: linux-kernel kernel linux-device-driver

我阅读了这篇文章http://www.linuxjournal.com/article/5833以了解螺旋锁。我试着在我的内核驱动程序中使用它。

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

void f1() {
spin_lock(&mylock);
// write hardware 
REG_ADDR += FLAG_A;

}

void f2() {
spin_lock(&mylock);
//...
}

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

我的问题是如果我打电话 F1() f2()//我想要阻塞,直到中断返回说设置REG_ADDR完成。

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

如何重新编写代码,以便内核认为我没有死锁?

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

谢谢。

2 个答案:

答案 0 :(得分:2)

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

我首先使用mutex来锁定对相关硬件寄存器的访问,这样其他内核线程就无法同时修改寄存器。允许互斥锁进入睡眠模式,因此如果它无法获取锁定,则线程可以进入睡眠状态直到它可以。

然后,我会使用wait queue来阻塞线程,直到中断到来并发出该位已完成设置的信号。

另外,另外,我注意到您尝试使用以下表达式REG_ADDR += FLAG_A;来访问外设。在内核中,这不是正确的方法。它似乎可行,但会破坏一些架构。你应该使用read{b,w,l} and write{b,w,l} macros之类的

unsigned long reg;
reg = readl(REG_ADDR);
reg |= FLAG_A;
writel(reg, REG_ADDR);

其中REG_ADDR是您从ioremap获得的地址。

答案 1 :(得分:0)

我同意Michael的观点,当任何资源(内存/变量/代码片段)有可能在内核/用户线程之间共享时,必须使用Spinlock,Semaphores,Mutex(或任何其他锁定机制) 。 我建议使用内核中可用的其他睡眠功能,而不是使用任何可用的锁定原语,如wait_event_interruptiblewake_up。它们很简单,很容易将它们用于代码中。您可以在网上找到它的详细信息和利用。