michael
michael

Reputation: 110730

Using spinlock to synchronize between kernel driver and an interrupt handler

I read this article http://www.linuxjournal.com/article/5833 to learn about spinlock. I try this to use it in my kernel driver.

Here is what my driver code needs to do: In f1(), it will get the spin lock, and caller can call f2() will wait for the lock since the spin lock is not being unlock. The spin lock will be unlock in my interrupt handler (triggered by the HW).

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

}

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

The hardware will send the application an interrupt and my interrupt handler will call spin_unlock(&mylock);

My question is if I call f1() f2() // i want this to block until the interrupt return saying setting REG_ADDR is done.

when I run this, I get an exception in kernel saying a deadlock " INFO: possible recursive locking detected"

How can I re-write my code so that kernel does not think I have a deadlock?

I want my driver code to wait until HW sends me an interrupt saying setting REG_ADDR is done.

Thank you.

Upvotes: 0

Views: 1546

Answers (2)

Sumeet_Jain
Sumeet_Jain

Reputation: 477

I will agree with Michael that Spinlock, Semaphores, Mutex ( Or any other Locking Mechanisms) must be used when any of the resources(Memory/variable/piece of code) has the probability of getting shared among the kernel/user threads. Instead of using any of the Locking primitives available I would suggest using other sleeping functionalities available in kernel like wait_event_interruptibleand wake_up. They are simple and easy to exploit them into your code. You can find its details and exploitation on net.

Upvotes: 0

tangrs
tangrs

Reputation: 9940

First, since you'll be expecting to block while waiting for the interrupt, you shouldn't be using spinlocks to lock the hardware as you'll probably be holding the lock for a long time. Using a spinlock in this case will waste a lot of CPU cycles if that function is called frequently.

I would first use a mutex to lock access to the hardware register in question so other kernel threads can't simultaneously modify the register. A mutex is allowed to sleep so if it can't acquire the lock, the thread is able to go to sleep until it can.

Then, I'd use a wait queue to block the thread until the interrupt arrives and signals that the bit has finished setting.

Also, as an aside, I noticed you're trying to access your peripheral by using the following expression REG_ADDR += FLAG_A;. In the kernel, that's not the correct way to do it. It may seem to work but will break on some architectures. You should be using the read{b,w,l} and write{b,w,l} macros like

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

where REG_ADDR is an address you obtained from ioremap.

Upvotes: 2

Related Questions