.. _kernel_hacking_lock: ===========================Unreliable Guide To Locking=========================== :Author: Rusty Russell Introduction============ Welcome, to Rusty's Remarkably Unreliable Guide to Kernel Lockingissues. This document describes the locking systems in the Linux Kernelin 2.6. With the wide availability of HyperThreading, and preemption in theLinux Kernel, everyone hacking on the kernel needs to know thefundamentals of concurrency and locking for SMP. The Problem With Concurrency============================ (Skip this if you know what a Race Condition is). In a normal program, you can increment a counter like so: :: very_important_count++; This is what they would expect to happen: .. table:: Expected Results +------------------------------------+------------------------------------+ | Instance 1 | Instance 2 | +====================================+====================================+ | read very_important_count (5) | | +------------------------------------+------------------------------------+ | add 1 (6) | | +------------------------------------+------------------------------------+ | write very_important_count (6) | | +------------------------------------+------------------------------------+ | | read very_important_count (6) | +------------------------------------+------------------------------------+ | | add 1 (7) | +------------------------------------+------------------------------------+ | | write very_important_count (7) | +------------------------------------+------------------------------------+ This is what might happen: .. table:: Possible Results +------------------------------------+------------------------------------+ | Instance 1 | Instance 2 | +====================================+====================================+ | read very_important_count (5) | | +------------------------------------+------------------------------------+ | | read very_important_count (5) | +------------------------------------+------------------------------------+ | add 1 (6) | | +------------------------------------+------------------------------------+ | | add 1 (6) | +------------------------------------+------------------------------------+ | write very_important_count (6) | | +------------------------------------+------------------------------------+ | | write very_important_count (6) | +------------------------------------+------------------------------------+ Race Conditions and Critical Regions------------------------------------ This overlap, where the result depends on the relative timing ofmultiple tasks, is called a race condition. The piece of code containingthe concurrency issue is called a critical region. And especially sinceLinux starting running on SMP machines, they became one of the majorissues in kernel design and implementation. Preemption can have the same effect, even if there is only one CPU: bypreempting one task during the critical region, we have exactly the samerace condition. In this case the thread which preempts might run thecritical region itself. The solution is to recognize when these simultaneous accesses occur, anduse locks to make sure that only one instance can enter the criticalregion at any time. There are many friendly primitives in the Linuxkernel to help you do this. And then there are the unfriendlyprimitives, but I'll pretend they don't exist. Locking in the Linux Kernel=========================== If I could give you one piece of advice on locking: **keep it simple**. Be reluctant to introduce new locks. Two Main Types of Kernel Locks: Spinlocks and Mutexes----------------------------------------------------- There are two main types of kernel locks. The fundamental type is thespinlock (``include/asm/spinlock.h``), which is a very simplesingle-holder lock: if you can't get the spinlock, you keep trying(spinning) until you can. Spinlocks are very small and fast, and can beused anywhere. The second type is a mutex (``include/linux/mutex.h``): it is like aspinlock, but you may block holding a mutex. If you can't lock a mutex,your task will suspend itself, and be woken up when the mutex isreleased. This means the CPU can do something else while you arewaiting. There are many cases when you simply can't sleep (see`What Functions Are Safe To Call From Interrupts?`_),and so have to use a spinlock instead. Neither type of lock is recursive: see`Deadlock: Simple and Advanced`_. Locks and Uniprocessor Kernels------------------------------ For kernels compiled without ``CONFIG_SMP``, and without``CONFIG_PREEMPT`` spinlocks do not exist at all. This is an excellentdesign decision: when no-one else can run at the same time, there is noreason to have a lock. If the kernel is compiled without ``CONFIG_SMP``, but ``CONFIG_PREEMPT``is set, then spinlocks simply disable preemption, which is sufficient toprevent any races. For most purposes, we can think of preemption asequivalent to SMP, and not worry about it separately. You should always test your locking code with ``CONFIG_SMP`` and``CONFIG_PREEMPT`` enabled, even if you don't have an SMP test box,because it will still catch some kinds of locking bugs. Mutexes still exist, because they are required for synchronizationbetween user contexts, as we will see below. Locking Only In User Context---------------------------- If you have a data structure which is only ever accessed from usercontext, then you can use a simple mutex (``include/linux/mutex.h``) toprotect it. This is the most trivial case: you initialize the mutex.Then you can call mutex_lock_interruptible() to grab themutex, and mutex_unlock() to release it. There is also amutex_lock(), which should be avoided, because it willnot return if a signal is received. Example: ``net/netfilter/nf_sockopt.c`` allows registration of newsetsockopt() and getsockopt() calls, withnf_register_sockopt(). Registration and de-registrationare only done on module load and unload (and boot time, where there isno concurrency), and the list of registrations is only consulted for anunknown setsockopt() or getsockopt() systemcall. The ``nf_sockopt_mutex`` is perfect to protect this, especiallysince the setsockopt and getsockopt calls may well sleep. Locking Between User Context and Softirqs----------------------------------------- If a softirq shares data with user context, you have two problems.Firstly, the current user context can be interrupted by a softirq, andsecondly, the critical region could be entered from another CPU. This iswhere spin_lock_bh() (``include/linux/spinlock.h``) isused. It disables softirqs on that CPU, then grabs the lock.spin_unlock_bh() does the reverse. (The '_bh' suffix isa historical reference to "Bottom Halves", the old name for softwareinterrupts. It should really be called spin_lock_softirq()' in aperfect world). Note that you can also use spin_lock_irq() orspin_lock_irqsave() here, which stop hardware interruptsas well: see `Hard IRQ Context`_. This works perfectly for UP as well: the spin lock vanishes, and thismacro simply becomes local_bh_disable()(``include/linux/interrupt.h``), which protects you from the softirqbeing run. Locking Between User Context and Tasklets----------------------------------------- This is exactly the same as above, because tasklets are actually runfrom a softirq. Locking Between User Context and Timers--------------------------------------- This, too, is exactly the same as above, because timers are actually runfrom a softirq. From a locking point of view, tasklets and timers areidentical. Locking Between Tasklets/Timers------------------------------- Sometimes a tasklet or timer might want to share data with anothertasklet or timer. The Same Tasklet/Timer~~~~~~~~~~~~~~~~~~~~~~ Since a tasklet is never run on two CPUs at once, you don't need toworry about your tasklet being reentrant (running twice at once), evenon SMP. Different Tasklets/Timers~~~~~~~~~~~~~~~~~~~~~~~~~ If another tasklet/timer wants to share data with your tasklet or timer, you will both need to use spin_lock() andspin_unlock() calls. spin_lock_bh() isunnecessary here, as you are already in a tasklet, and none will be runon the same CPU. Locking Between Softirqs------------------------ Often a softirq might want to share data with itself or a tasklet/timer. The Same Softirq~~~~~~~~~~~~~~~~ The same softirq can run on the other CPUs: you can use a per-CPU array(see `Per-CPU Data`_) for better performance. If you'regoing so far as to use a softirq, you probably care about scalableperformance enough to justify the extra complexity. You'll need to use spin_lock() andspin_unlock() for shared data. Different Softirqs~~~~~~~~~~~~~~~~~~ You'll need to use spin_lock() andspin_unlock() for shared data, whether it be a timer,tasklet, different softirq or the same or another softirq: any of themcould be running on a different CPU. Hard IRQ Context================ Hardware interrupts usually communicate with a tasklet or softirq.Frequently this involves putting work in a queue, which the softirq willtake out. Locking Between Hard IRQ and Softirqs/Tasklets---------------------------------------------- If a hardware irq handler shares data with a softirq, you have twoconcerns. Firstly, the softirq processing can be interrupted by ahardware interrupt, and secondly, the critical region could be enteredby a hardware interrupt on another CPU. This is wherespin_lock_irq() is used. It is defined to disableinterrupts on that cpu, then grab the lock.spin_unlock_irq() does the reverse. The irq handler does not need to use spin_lock_irq(), becausethe softirq cannot run while the irq handler is running: it can usespin_lock(), which is slightly faster. The only exceptionwould be if a different hardware irq handler uses the same lock:spin_lock_irq() will stop that from interrupting us. This works perfectly for UP as well: the spin lock vanishes, and thismacro simply becomes local_irq_disable()(``include/asm/smp.h``), which protects you from the softirq/tasklet/BHbeing run. spin_lock_irqsave() (``include/linux/spinlock.h``) is avariant which saves whether interrupts were on or off in a flags word,which is passed to spin_unlock_irqrestore(). This meansthat the same code can be used inside an hard irq handler (whereinterrupts are already off) and in softirqs (where the irq disabling isrequired). Note that softirqs (and hence tasklets and timers) are run on returnfrom hardware interrupts, so spin_lock_irq() also stopsthese. In that sense, spin_lock_irqsave() is the mostgeneral and powerful locking function. Locking Between Two Hard IRQ Handlers------------------------------------- It is rare to have to share data between two IRQ handlers, but if youdo, spin_lock_irqsave() should be used: it isarchitecture-specific whether all interrupts are disabled inside irqhandlers themselves. Cheat Sheet For Locking======================= Pete Zaitcev gives the following summary: - If you are in a process context (any syscall) and want to lock other process out, use a mutex. You can take a mutex and sleep (``copy_from_user()`` or ``kmalloc(x,GFP_KERNEL)``). - Otherwise (== data can be touched in an interrupt), use spin_lock_irqsave() and spin_unlock_irqrestore(). - Avoid holding spinlock for more than 5 lines of code and across any function call (except accessors like readb()). Table of Minimum Requirements----------------------------- The following table lists the **minimum** locking requirements betweenvarious contexts. In some cases, the same context can only be running onone CPU at a time, so no locking is required for that context (eg. aparticular thread can only run on one CPU at a time, but if it needsshares data with another thread, locking is required). Remember the advice above: you can always usespin_lock_irqsave(), which is a superset of all otherspinlock primitives. ============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ==============. IRQ Handler A IRQ Handler B Softirq A Softirq B Tasklet A Tasklet B Timer A Timer B User Context A User Context B============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ==============IRQ Handler A NoneIRQ Handler B SLIS NoneSoftirq A SLI SLI SLSoftirq B SLI SLI SL SLTasklet A SLI SLI SL SL NoneTasklet B SLI SLI SL SL SL NoneTimer A SLI SLI SL SL SL SL NoneTimer B SLI SLI SL SL SL SL SL NoneUser Context A SLI SLI SLBH SLBH SLBH SLBH SLBH SLBH NoneUser Context B SLI SLI SLBH SLBH SLBH SLBH SLBH SLBH MLI None============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ============== Table: Table of Locking Requirements +--------+----------------------------+| SLIS | spin_lock_irqsave |+--------+----------------------------+| SLI | spin_lock_irq |+--------+----------------------------+| SL | spin_lock |+--------+----------------------------+| SLBH | spin_lock_bh |+--------+----------------------------+| MLI | mutex_lock_interruptible |+--------+----------------------------+ Table: Legend for Locking Requirements Table The trylock Functions===================== There are functions that try to acquire a lock only once and immediatelyreturn a value telling about success or failure to acquire the lock.They can be used if you need no access to the data protected with thelock when some other thread is holding the lock. You should acquire thelock later if you then need access to the data protected with the lock. spin_trylock() does not spin but returns non-zero if itacquires the spinlock on the first try or 0 if not. This function can beused in all contexts like spin_lock(): you must havedisabled the contexts that might interrupt you and acquire the spinlock. mutex_trylock() does not suspend your task but returnsnon-zero if it could lock the mutex on the first try or 0 if not. Thisfunction cannot be safely used in hardware or software interruptcontexts despite not sleeping. Common Examples=============== Let's step through a simple example: a cache of number to name mappings.The cache keeps a count of how often each of the objects is used, andwhen it gets full, throws out the least used one. All In User Context------------------- For our first example, we assume that all operations are in user context(ie. from system calls), so we can sleep. This means we can use a mutexto protect the cache and all the objects within it. Here's the code:: #include <linux/list.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/mutex.h> #include <asm/errno.h> struct object { struct list_head list; int id; char name[32]; int popularity; }; /* Protects the cache, cache_num, and the objects within it */ static DEFINE_MUTEX(cache_lock); static LIST_HEAD(cache); static unsigned int cache_num = 0; #define MAX_CACHE_SIZE 10 /* Must be holding cache_lock */ static struct object *__cache_find(int id) { struct object *i; list_for_each_entry(i, &cache, list) if (i->id == id) { i->popularity++; return i; } return NULL; } /* Must be holding cache_lock */ static void __cache_delete(struct object *obj) { BUG_ON(!obj); list_del(&obj->list); kfree(obj); cache_num--; } /* Must be holding cache_lock */ static void __cache_add(struct object *obj) { list_add(&obj->list, &cache); if (++cache_num > MAX_CACHE_SIZE) { struct object *i, *outcast = NULL; list_for_each_entry(i, &cache, list) { if (!outcast || i->popularity < outcast->popularity) outcast = i; } __cache_delete(outcast); } } int cache_add(int id, const char *name) { struct object *obj; if ((obj = kmalloc(sizeof(*obj), GFP_KERNEL)) == NULL) return -ENOMEM; strscpy(obj->name, name, sizeof(obj->name)); obj->id = id; obj->popularity = 0; mutex_lock(&cache_lock); __cache_add(obj); mutex_unlock(&cache_lock); return 0; } void cache_delete(int id) { mutex_lock(&cache_lock); __cache_delete(__cache_find(id)); mutex_unlock(&cache_lock); } int cache_find(int id, char *name) { struct object *obj; int ret = -ENOENT; mutex_lock(&cache_lock); obj = __cache_find(id); if (obj) { ret = 0; strcpy(name, obj->name); } mutex_unlock(&cache_lock); return ret; } Note that we always make sure we have the cache_lock when we add,delete, or look up the cache: both the cache infrastructure itself andthe contents of the objects are protected by the lock. In this case it'seasy, since we copy the data for the user, and never let them access theobjects directly. There is a slight (and common) optimization here: incache_add() we set up the fields of the object beforegrabbing the lock. This is safe, as no-one else can access it until weput it in cache. Accessing From Interrupt Context-------------------------------- Now consider the case where cache_find() can be calledfrom interrupt context: either a hardware interrupt or a softirq. Anexample would be a timer which deletes object from the cache. The change is shown below, in standard patch format: the ``-`` are lineswhich are taken away, and the ``+`` are lines which are added. Lines 1–500 of 1454. Load more
.. _kernel_hacking_lock: =========================== Unreliable Guide To Locking =========================== :Author: Rusty Russell Introduction ============ Welcome, to Rusty's Remarkably Unreliable Guide to Kernel Locking issues. This document describes the locking systems in the Linux Kernel in 2.6. With the wide availability of HyperThreading, and preemption in the Linux Kernel, everyone hacking on the kernel needs to know the fundamentals of concurrency and locking for SMP. The Problem With Concurrency ============================ (Skip this if you know what a Race Condition is). In a normal program, you can increment a counter like so: :: very_important_count++; This is what they would expect to happen: .. table:: Expected Results +------------------------------------+------------------------------------+ | Instance 1 | Instance 2 | +====================================+====================================+ | read very_important_count (5) | | +------------------------------------+------------------------------------+ | add 1 (6) | | +------------------------------------+------------------------------------+ | write very_important_count (6) | | +------------------------------------+------------------------------------+ | | read very_important_count (6) | +------------------------------------+------------------------------------+ | | add 1 (7) | +------------------------------------+------------------------------------+ | | write very_important_count (7) | +------------------------------------+------------------------------------+ This is what might happen: .. table:: Possible Results +------------------------------------+------------------------------------+ | Instance 1 | Instance 2 | +====================================+====================================+ | read very_important_count (5) | | +------------------------------------+------------------------------------+ | | read very_important_count (5) | +------------------------------------+------------------------------------+ | add 1 (6) | | +------------------------------------+------------------------------------+ | | add 1 (6) | +------------------------------------+------------------------------------+ | write very_important_count (6) | | +------------------------------------+------------------------------------+ | | write very_important_count (6) | +------------------------------------+------------------------------------+ Race Conditions and Critical Regions ------------------------------------ This overlap, where the result depends on the relative timing of multiple tasks, is called a race condition. The piece of code containing the concurrency issue is called a critical region. And especially since Linux starting running on SMP machines, they became one of the major issues in kernel design and implementation. Preemption can have the same effect, even if there is only one CPU: by preempting one task during the critical region, we have exactly the same race condition. In this case the thread which preempts might run the critical region itself. The solution is to recognize when these simultaneous accesses occur, and use locks to make sure that only one instance can enter the critical region at any time. There are many friendly primitives in the Linux kernel to help you do this. And then there are the unfriendly primitives, but I'll pretend they don't exist. Locking in the Linux Kernel =========================== If I could give you one piece of advice on locking: **keep it simple**. Be reluctant to introduce new locks. Two Main Types of Kernel Locks: Spinlocks and Mutexes ----------------------------------------------------- There are two main types of kernel locks. The fundamental type is the spinlock (``include/asm/spinlock.h``), which is a very simple single-holder lock: if you can't get the spinlock, you keep trying (spinning) until you can. Spinlocks are very small and fast, and can be used anywhere. The second type is a mutex (``include/linux/mutex.h``): it is like a spinlock, but you may block holding a mutex. If you can't lock a mutex, your task will suspend itself, and be woken up when the mutex is released. This means the CPU can do something else while you are waiting. There are many cases when you simply can't sleep (see `What Functions Are Safe To Call From Interrupts?`_), and so have to use a spinlock instead. Neither type of lock is recursive: see `Deadlock: Simple and Advanced`_. Locks and Uniprocessor Kernels ------------------------------ For kernels compiled without ``CONFIG_SMP``, and without ``CONFIG_PREEMPT`` spinlocks do not exist at all. This is an excellent design decision: when no-one else can run at the same time, there is no reason to have a lock. If the kernel is compiled without ``CONFIG_SMP``, but ``CONFIG_PREEMPT`` is set, then spinlocks simply disable preemption, which is sufficient to prevent any races. For most purposes, we can think of preemption as equivalent to SMP, and not worry about it separately. You should always test your locking code with ``CONFIG_SMP`` and ``CONFIG_PREEMPT`` enabled, even if you don't have an SMP test box, because it will still catch some kinds of locking bugs. Mutexes still exist, because they are required for synchronization between user contexts, as we will see below. Locking Only In User Context ---------------------------- If you have a data structure which is only ever accessed from user context, then you can use a simple mutex (``include/linux/mutex.h``) to protect it. This is the most trivial case: you initialize the mutex. Then you can call mutex_lock_interruptible() to grab the mutex, and mutex_unlock() to release it. There is also a mutex_lock(), which should be avoided, because it will not return if a signal is received. Example: ``net/netfilter/nf_sockopt.c`` allows registration of new setsockopt() and getsockopt() calls, with nf_register_sockopt(). Registration and de-registration are only done on module load and unload (and boot time, where there is no concurrency), and the list of registrations is only consulted for an unknown setsockopt() or getsockopt() system call. The ``nf_sockopt_mutex`` is perfect to protect this, especially since the setsockopt and getsockopt calls may well sleep. Locking Between User Context and Softirqs ----------------------------------------- If a softirq shares data with user context, you have two problems. Firstly, the current user context can be interrupted by a softirq, and secondly, the critical region could be entered from another CPU. This is where spin_lock_bh() (``include/linux/spinlock.h``) is used. It disables softirqs on that CPU, then grabs the lock. spin_unlock_bh() does the reverse. (The '_bh' suffix is a historical reference to "Bottom Halves", the old name for software interrupts. It should really be called spin_lock_softirq()' in a perfect world). Note that you can also use spin_lock_irq() or spin_lock_irqsave() here, which stop hardware interrupts as well: see `Hard IRQ Context`_. This works perfectly for UP as well: the spin lock vanishes, and this macro simply becomes local_bh_disable() (``include/linux/interrupt.h``), which protects you from the softirq being run. Locking Between User Context and Tasklets ----------------------------------------- This is exactly the same as above, because tasklets are actually run from a softirq. Locking Between User Context and Timers --------------------------------------- This, too, is exactly the same as above, because timers are actually run from a softirq. From a locking point of view, tasklets and timers are identical. Locking Between Tasklets/Timers ------------------------------- Sometimes a tasklet or timer might want to share data with another tasklet or timer. The Same Tasklet/Timer ~~~~~~~~~~~~~~~~~~~~~~ Since a tasklet is never run on two CPUs at once, you don't need to worry about your tasklet being reentrant (running twice at once), even on SMP. Different Tasklets/Timers ~~~~~~~~~~~~~~~~~~~~~~~~~ If another tasklet/timer wants to share data with your tasklet or timer , you will both need to use spin_lock() and spin_unlock() calls. spin_lock_bh() is unnecessary here, as you are already in a tasklet, and none will be run on the same CPU. Locking Between Softirqs ------------------------ Often a softirq might want to share data with itself or a tasklet/timer. The Same Softirq ~~~~~~~~~~~~~~~~ The same softirq can run on the other CPUs: you can use a per-CPU array (see `Per-CPU Data`_) for better performance. If you're going so far as to use a softirq, you probably care about scalable performance enough to justify the extra complexity. You'll need to use spin_lock() and spin_unlock() for shared data. Different Softirqs ~~~~~~~~~~~~~~~~~~ You'll need to use spin_lock() and spin_unlock() for shared data, whether it be a timer, tasklet, different softirq or the same or another softirq: any of them could be running on a different CPU. Hard IRQ Context ================ Hardware interrupts usually communicate with a tasklet or softirq. Frequently this involves putting work in a queue, which the softirq will take out. Locking Between Hard IRQ and Softirqs/Tasklets ---------------------------------------------- If a hardware irq handler shares data with a softirq, you have two concerns. Firstly, the softirq processing can be interrupted by a hardware interrupt, and secondly, the critical region could be entered by a hardware interrupt on another CPU. This is where spin_lock_irq() is used. It is defined to disable interrupts on that cpu, then grab the lock. spin_unlock_irq() does the reverse. The irq handler does not need to use spin_lock_irq(), because the softirq cannot run while the irq handler is running: it can use spin_lock(), which is slightly faster. The only exception would be if a different hardware irq handler uses the same lock: spin_lock_irq() will stop that from interrupting us. This works perfectly for UP as well: the spin lock vanishes, and this macro simply becomes local_irq_disable() (``include/asm/smp.h``), which protects you from the softirq/tasklet/BH being run. spin_lock_irqsave() (``include/linux/spinlock.h``) is a variant which saves whether interrupts were on or off in a flags word, which is passed to spin_unlock_irqrestore(). This means that the same code can be used inside an hard irq handler (where interrupts are already off) and in softirqs (where the irq disabling is required). Note that softirqs (and hence tasklets and timers) are run on return from hardware interrupts, so spin_lock_irq() also stops these. In that sense, spin_lock_irqsave() is the most general and powerful locking function. Locking Between Two Hard IRQ Handlers ------------------------------------- It is rare to have to share data between two IRQ handlers, but if you do, spin_lock_irqsave() should be used: it is architecture-specific whether all interrupts are disabled inside irq handlers themselves. Cheat Sheet For Locking ======================= Pete Zaitcev gives the following summary: - If you are in a process context (any syscall) and want to lock other process out, use a mutex. You can take a mutex and sleep (``copy_from_user()`` or ``kmalloc(x,GFP_KERNEL)``). - Otherwise (== data can be touched in an interrupt), use spin_lock_irqsave() and spin_unlock_irqrestore(). - Avoid holding spinlock for more than 5 lines of code and across any function call (except accessors like readb()). Table of Minimum Requirements ----------------------------- The following table lists the **minimum** locking requirements between various contexts. In some cases, the same context can only be running on one CPU at a time, so no locking is required for that context (eg. a particular thread can only run on one CPU at a time, but if it needs shares data with another thread, locking is required). Remember the advice above: you can always use spin_lock_irqsave(), which is a superset of all other spinlock primitives. ============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ============== . IRQ Handler A IRQ Handler B Softirq A Softirq B Tasklet A Tasklet B Timer A Timer B User Context A User Context B ============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ============== IRQ Handler A None IRQ Handler B SLIS None Softirq A SLI SLI SL Softirq B SLI SLI SL SL Tasklet A SLI SLI SL SL None Tasklet B SLI SLI SL SL SL None Timer A SLI SLI SL SL SL SL None Timer B SLI SLI SL SL SL SL SL None User Context A SLI SLI SLBH SLBH SLBH SLBH SLBH SLBH None User Context B SLI SLI SLBH SLBH SLBH SLBH SLBH SLBH MLI None ============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ============== Table: Table of Locking Requirements +--------+----------------------------+ | SLIS | spin_lock_irqsave | +--------+----------------------------+ | SLI | spin_lock_irq | +--------+----------------------------+ | SL | spin_lock | +--------+----------------------------+ | SLBH | spin_lock_bh | +--------+----------------------------+ | MLI | mutex_lock_interruptible | +--------+----------------------------+ Table: Legend for Locking Requirements Table The trylock Functions ===================== There are functions that try to acquire a lock only once and immediately return a value telling about success or failure to acquire the lock. They can be used if you need no access to the data protected with the lock when some other thread is holding the lock. You should acquire the lock later if you then need access to the data protected with the lock. spin_trylock() does not spin but returns non-zero if it acquires the spinlock on the first try or 0 if not. This function can be used in all contexts like spin_lock(): you must have disabled the contexts that might interrupt you and acquire the spin lock. mutex_trylock() does not suspend your task but returns non-zero if it could lock the mutex on the first try or 0 if not. This function cannot be safely used in hardware or software interrupt contexts despite not sleeping. Common Examples =============== Let's step through a simple example: a cache of number to name mappings. The cache keeps a count of how often each of the objects is used, and when it gets full, throws out the least used one. All In User Context ------------------- For our first example, we assume that all operations are in user context (ie. from system calls), so we can sleep. This means we can use a mutex to protect the cache and all the objects within it. Here's the code:: #include <linux/list.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/mutex.h> #include <asm/errno.h> struct object { struct list_head list; int id; char name[32]; int popularity; }; /* Protects the cache, cache_num, and the objects within it */ static DEFINE_MUTEX(cache_lock); static LIST_HEAD(cache); static unsigned int cache_num = 0; #define MAX_CACHE_SIZE 10 /* Must be holding cache_lock */ static struct object *__cache_find(int id) { struct object *i; list_for_each_entry(i, &cache, list) if (i->id == id) { i->popularity++; return i; } return NULL; } /* Must be holding cache_lock */ static void __cache_delete(struct object *obj) { BUG_ON(!obj); list_del(&obj->list); kfree(obj); cache_num--; } /* Must be holding cache_lock */ static void __cache_add(struct object *obj) { list_add(&obj->list, &cache); if (++cache_num > MAX_CACHE_SIZE) { struct object *i, *outcast = NULL; list_for_each_entry(i, &cache, list) { if (!outcast || i->popularity < outcast->popularity) outcast = i; } __cache_delete(outcast); } } int cache_add(int id, const char *name) { struct object *obj; if ((obj = kmalloc(sizeof(*obj), GFP_KERNEL)) == NULL) return -ENOMEM; strscpy(obj->name, name, sizeof(obj->name)); obj->id = id; obj->popularity = 0; mutex_lock(&cache_lock); __cache_add(obj); mutex_unlock(&cache_lock); return 0; } void cache_delete(int id) { mutex_lock(&cache_lock); __cache_delete(__cache_find(id)); mutex_unlock(&cache_lock); } int cache_find(int id, char *name) { struct object *obj; int ret = -ENOENT; mutex_lock(&cache_lock); obj = __cache_find(id); if (obj) { ret = 0; strcpy(name, obj->name); } mutex_unlock(&cache_lock); return ret; } Note that we always make sure we have the cache_lock when we add, delete, or look up the cache: both the cache infrastructure itself and the contents of the objects are protected by the lock. In this case it's easy, since we copy the data for the user, and never let them access the objects directly. There is a slight (and common) optimization here: in cache_add() we set up the fields of the object before grabbing the lock. This is safe, as no-one else can access it until we put it in cache. 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.