deadlock avoidance?

Johannes Erdfelt (jerdfelt@sventech.com)
Tue, 7 Dec 1999 14:50:07 -0500


I have a situation where an interrupt handler needs to walk a list of
structures (using struct list_head, etc) and call an function which can
then change the list (could possibly delete the current structure in the
list or add others or delete others). Also, the list can be modified from
within a system call as well.

The problem is I acquire a spin lock before walking the list and release
it when finished.

I also acquire the same spin lock before modifying the list and release
it when done. This is what causes the deadlock. I can possibly attempt
to acquire the spin lock when it's already acquired inside the interrupt
handler.

Here's some sample code:

spinlock_t lock = SPIN_LOCK_UNLOCKED;
LIST_HEAD(list);

void add_entry(struct foo *f)
{
unsigned long flags;

spin_lock_irqsave(&lock, flags);
list_add(&f->list, &list);
spin_unlock_irqsave(&lock, flags);
}

/* remove_entry() is similar to add_entry but uses list_del, etc */

void something(struct foo *f)
{
...
remove_entry(f);
...
add_entry(fnew);
...
}

void interrupt(int irq, void *bar, struct pt_regs *regs)
{
struct list_head *tmp, *head = &list;

spin_lock(&lock);
tmp = head->next;
while (tmp != head) {
struct *foo = list_entry(tmp, struct foo, list);

something(foo);
}
spin_unlock(&lock);
}

Now, something() can be called at any time (from an interrupt, system
call, etc). Also, something() calls other completion handlers which can
then call other functions which can call add_entry.

I can come up with one of two solutions:

1) Duplicate the something() function for the interrupt handler case but
don't acquire the lock (since it's already guaranteed to be acquired)
2) add_entry adds the structure to a different list (call it new_list)
which the interrupt handler then walks and moves to the master list
(since it's the only one that walks list) before it walks the master
list. Do the same for the remove case

Now the downsides to both:

1) The something() code and the associated functions it calls is
complicated and it's a lot of code that would need to be duplicated
2) All of the extra memory and code needed to handle this. It seems more
of a kludge or hack than an actual solution

I'm leaning towards 2 since it'll result in the least amount of code,
but it's still not optimal. Am I missing something or can anyone come
up with a better solution?

JE

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/