Hi,
On 10-3-1 下午4:25, Björn Döbel wrote:
I am thinking of using a lock to simulate cli/sti. When local_irq_enable() or local_irq_save() is called, we hold the lock; when local_irq_restore() or local_irq_disable(), we release the lock. We can even provide nested locking support if local_irq_enable() or local_irq_save() is allowed to be called multiple times in the same thread.
A lock does not work - been there, done that. The problem is that you can call local_irq_disable() several times and then only call local_irq_enable() only once to reset everything, so the semantics are different from recursive locks.
I guess you have used locks, but somehow later removed the code, though I didn't know the reason.
Can't we simulate the same semantics with a lock? If the same thread already holds the lock, local_irq_disable() does nothing. My implementation for now works pretty well, at least for the pcnet32 driver. Maybe I miss something important. Here is my code: int raw_irqs_disabled_flags(unsigned long flags) { return ((int)flags > 0); }
/* If it does lock operation successfully, return > 0. Otherwise, 0. */ static int nested_lock(ddekit_lock_t lock) { int do_lock = 0;
if (ddekit_lock_try_lock(&lock)) { /* if we cannot lock */ /* check who hold the lock. */ if (_ddekit_lock_owner(&lock) != (int) ddekit_thread_myself()) { /* Someone else holds the lock, * or by the time I try to lock again, * the person has release the lock. */ ddekit_lock_lock(&lock); do_lock = 1; } /* If I hold the lock myself, I don't need to worry * the lock will be released somewhere before I do it. */ } else do_lock = 2;
return do_lock; }
/* Store the current flags state. * * This is done by returning the current refcnt. * * XXX: Up to now, flags was always 0 at this point and * I assume that this is always the case. Prove? */ unsigned long __raw_local_save_flags(void) { unsigned long flags; int do_lock = 0;
if (cli_lock == NULL) ddekit_lock_init_unlocked(&cli_lock); /* It's important to do lock here. * Otherwise, a thread might not get correct flags. */ do_lock = nested_lock(cli_lock); flags = (unsigned long)atomic_read(&_refcnt); if (do_lock) ddekit_lock_unlock(&cli_lock); return flags; }
/* Restore IRQ state. */ void raw_local_irq_restore(unsigned long flags) { Assert(cli_lock != NULL); atomic_set(&_refcnt, flags); if (flags == 0) ddekit_lock_unlock(&cli_lock); }
/* Disable IRQs by grabbing the IRQ lock. */ void raw_local_irq_disable(void) { struct ddekit_thread *helder; int is_print = 0;
if (cli_lock == NULL) ddekit_lock_init_unlocked(&cli_lock);
nested_lock(cli_lock); atomic_inc(&_refcnt); }
/* Unlock the IRQ lock until refcnt is 0. */ void raw_local_irq_enable(void) { Assert(cli_lock != NULL); atomic_set(&_refcnt, 0); ddekit_lock_unlock(&cli_lock); }
L4Linux uses a threaded lock implementation for that reason, so we'll discuss reusing this idea.
What is a threaded lock?
Best regards, Zheng Da