Now consider the case where cache_find
can be
called from interrupt context: either a hardware interrupt or a
softirq. An example would be a timer which deletes object from the
cache.
The change is shown below, in standard patch format: the - are lines which are taken away, and the + are lines which are added.
--- cache.c.usercontext 2003-12-09 13:58:54.000000000 +1100 +++ cache.c.interrupt 2003-12-09 14:07:49.000000000 +1100 @@ -12,7 +12,7 @@ int popularity; }; -static DECLARE_MUTEX(cache_lock); +static spinlock_t cache_lock = SPIN_LOCK_UNLOCKED; static LIST_HEAD(cache); static unsigned int cache_num = 0; #define MAX_CACHE_SIZE 10 @@ -55,6 +55,7 @@ int cache_add(int id, const char *name) { struct object *obj; + unsigned long flags; if ((obj = kmalloc(sizeof(*obj), GFP_KERNEL)) == NULL) return -ENOMEM; @@ -63,30 +64,33 @@ obj->id = id; obj->popularity = 0; - down(&cache_lock); + spin_lock_irqsave(&cache_lock, flags); __cache_add(obj); - up(&cache_lock); + spin_unlock_irqrestore(&cache_lock, flags); return 0; } void cache_delete(int id) { - down(&cache_lock); + unsigned long flags; + + spin_lock_irqsave(&cache_lock, flags); __cache_delete(__cache_find(id)); - up(&cache_lock); + spin_unlock_irqrestore(&cache_lock, flags); } int cache_find(int id, char *name) { struct object *obj; int ret = -ENOENT; + unsigned long flags; - down(&cache_lock); + spin_lock_irqsave(&cache_lock, flags); obj = __cache_find(id); if (obj) { ret = 0; strcpy(name, obj->name); } - up(&cache_lock); + spin_unlock_irqrestore(&cache_lock, flags); return ret; }
Note that the spin_lock_irqsave
will turn off
interrupts if they are on, otherwise does nothing (if we are already
in an interrupt handler), hence these functions are safe to call from
any context.
Unfortunately, cache_add
calls
kmalloc
with the GFP_KERNEL
flag, which is only legal in user context. I have assumed that
cache_add
is still only called in user context,
otherwise this should become a parameter to
cache_add
.