The many flavours of spin locks, and when to use them?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

I am writing a USB driver for Linux 2.4 and Linux 2.6,
and I am currently using a single spin lock
exclusively with spin_lock_irqsave() and
spin_unlock_irqrestore(). However, I am wondering if
it is possible to optimise this approach to locking,
please?

I am currently using my lock in the following places:
- user context
- timer functions
- tasklets
- USB URB callback functions

There are also a lot of utility functions which can be
called from any of the above. I understand that these
utility functions *must* use the irqsave/irqrestore
variants because they can have no idea whether
interrupts are enabled or not. Therefore my question
is really about the timer, tasklet and URB callback
functions *themselves*.

E.g.

tasklet_init(&tasklet, do_task, context);

void
do_task(unsigned long data)
{
        unsigned long flags;
        spin_lock_irqsave("lock", flags);
...
        // do stuff
...
        spin_unlock_irqrestore("lock", flags);
}

Is this overkill? Or can I get away with using
spin_lock_irq() and spin_unlock_irq()?

Similarly for timers: can I use spin_lock_irq() and
spin_unlock_irq() in the top-level timer function?

I understand that the URB callback function is called
in hard IRQ context. Does this mean that I only need
spin_lock() and spin_unlock()? E.g.

void
urb_callback(struct urb *urb, struct pt_regs *regs)
{
        spin_lock("lock");
...
        // do stuff
...
        spin_unlock("lock");
}


Finally, I recently tripped over an "interesting" bug
in my driver. I was using spin_lock_irqsave and
spin_unlock_irqrestore exclusively throughout my
driver, and decided to move one of my tasklets onto a
work-queue instead. This was fine for a short time,
but suddenly my PS2 keyboard stopped responding! My
PS2 mouse kept on working, so interrupts were
obviously still enabled. (This was on a UP machine,
BTW.) Turning the task back into a tasklet fixed the
problem immediately. However, the function wasn't
exactly rocket-science:

void
do_task(unsigned long data)
{
    unsigned long flags;
    spin_lock_irqsave("lock", flags);

    for each entry in list {
        if (entry->callback != NULL) {

            // Just use spin_unlock() and
            // spin_lock() here instead?
            spin_unlock_irqrestore("lock", flags);
            entry->callback(entry->context);
            spin_lock_irqsave("lock", flags);

            entry->callback = NULL;
        }
...
        // do other things
...
        entry->wake_me_up = 1;
    }

    spin_unlock_irqrestore();

    // Wake everyone up!
    wake_event_interruptible("queue");

    if (task_flag)
        tasklet_schedule(another task);
}

Isn't the only difference between a tasklet and a
work-queue that one runs in interrupt context and the
other runs in user context? Can anyone think why this
function might have had such an effect on my keyboard,
please?

Thanks for any advice here,
Cheers,
Chris



	
	
		
___________________________________________________________ 
ALL-NEW Yahoo! Messenger - all new features - even more fun! http://uk.messenger.yahoo.com

--
Kernelnewbies: Help each other learn about the Linux kernel.
Archive:       http://mail.nl.linux.org/kernelnewbies/
FAQ:           http://kernelnewbies.org/faq/


[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux