我阅读了这篇文章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已完成的中断。
谢谢。
答案 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_interruptible
和wake_up
。它们很简单,很容易将它们用于代码中。您可以在网上找到它的详细信息和利用。