6.2. Accessing From Interrupt Context

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.