Linux Kernel & Device Driver Programming

Linux Kernel Locking Patterns & Anti-Patterns

*

This file uses the W3C HTML Slidy format. The "a" key toggles between one-slide-at-a-time and single-page mode, and the "c" key toggles on and off the table of contents. The ← and → keys can be used to page forward and backward. For more help on controls see the "help?" link at the bottom.

See also the introductory page on patterns and the page on miscellaneous patterns.

Image from http://kadermannan.wordpress.com/2009/04/

Topics

  • Correct use of locks
  • Common locking errors
  • Choosing the right lock
  • Lock implementations
  • Lock usage examples in the kernel
*

Most of these apply equally to mutexes, spinlocks, semaphores, etc. We will point out cases where the type of lock makes a difference.

Image from http://www.flickr.com/photos/hectic/1018015429/

Pattern: Basic Critical Section

   unsigned long flags;
   ...
   spin_lock_irqsave(&task_group_lock, flags);
   for_each_possible_cpu(i)
          unregister_fair_sched_group(tg, i);
   list_del_rcu(&tg->siblings);
   spin_unlock_irqrestore(&task_group_lock, flags);

Pattern: Error Recovery in Critical Section

   mutex_lock(&shares_mutex);
   if (tg->shares == shares) goto done;
   ...several lines of code...
done:
   mutex_unlock(&shares_mutex);
   return 0;

Pattern: Associating Locks with Objects

struct kset {
        struct list_head list;
        spinlock_t list_lock;
        struct kobject kobj;
        struct kset_uevent_ops *uevent_ops;
};

Pattern: Associating Locks with Modules

static DEFINE_MUTEX(mm_all_locks_mutex);

Pattern: Associating Locks with Lists

/* add the kobject to its kset's list */
static void kobj_kset_join(struct kobject *kobj)
{
        if (!kobj->kset) return;
        kset_get(kobj->kset);
        spin_lock(&kobj->kset->list_lock);
        list_add_tail(&kobj->entry, &kobj->kset->list);
        spin_unlock(&kobj->kset->list_lock);
}

This generalizes to other data structures, such as trees.

Pattern: Moving Objects Between Lists

Pattern: Ordered Locking

   mutex_lock(&rt_constraints_mutex);
   read_lock(&tasklist_lock);
   ret = __rt_schedulable(NULL, 0, 0);
   read_unlock(&tasklist_lock);
   mutex_unlock(&rt_constraints_mutex);

Pattern: Document Locking Policies

/*
 * This is the main, per-CPU runqueue data structure.
 *
 * Locking rule: those places that want to lock multiple runqueues
 * (such as the load balancing or the thread migration code), lock
 * acquire operations must be ordered by ascending &runqueue.
 */

Pattern: Complete Example

static int tg_set_bandwidth(struct task_group *tg,
            u64 rt_period, u64 rt_runtime)
{
    int i, err = 0;
    mutex_lock(&rt_constraints_mutex);
    read_lock(&tasklist_lock);
    err = __rt_schedulable(tg, rt_period, rt_runtime);
    if (err)
            goto unlock;
    spin_lock_irq(&tg->rt_bandwidth.rt_runtime_lock);
    tg->rt_bandwidth.rt_period = ns_to_ktime(rt_period);
    tg->rt_bandwidth.rt_runtime = rt_runtime;
    for_each_possible_cpu(i) {
            struct rt_rq *rt_rq = tg->rt_rq[i];
            spin_lock(&rt_rq->rt_runtime_lock);
            rt_rq->rt_runtime = rt_runtime;
            spin_unlock(&rt_rq->rt_runtime_lock);
    }
    spin_unlock_irq(&tg->rt_bandwidth.rt_runtime_lock);
unlock:
    read_unlock(&tasklist_lock);
    mutex_unlock(&rt_constraints_mutex);
    return err;
}

Anti-Pattern: Releasing Lock Too Early

   mutex_lock(&somelock);
   result = needs_service (&aome_object);
   mutex_unlock(&somelock);
   if (result) perform_service (&some_object);

Anti-Pattern: Holding Lock While Sleeping

   mutex_lock(&somelock);
   ...
   wait_event(some_wait_queue, some_test(some_object);
   ...
   mutex_unlock(&somelock);

Anti-Pattern: Not Documenting Lock Coverage

Anti-Pattern Forgetting to Lock

Anti-Pattern Forgetting to Lock

Anti-Pattern: Blocking Lock in Handler

void handler(void * arg) {   
    ...
    mutex_lock(m);
    ... 
}

Anti-Pattern: Too-late Initialization

Anti-Pattern: Splitting Critical Sections Arbitrarily

Anti-Pattern: Unnecessary Locking

   mutex_lock(&some_lock);
   some_int_variable = some_constant_value;
   mutex_unlock(&some_lock);

See also the introductory page on patterns and the page on miscellaneous patterns.