[ Bottom of Page | Previous Page | Next Page | Contents | Index | Library Home | Legal | Search ]

Kernel Extensions and Device Support Programming Concepts

Locking Kernel Services

The following information is provided to assist you in understanding the locking kernel services:

Lock Allocation and Other Services

The following lock allocation services allocate and free internal operating system memory for simple and complex locks, or check if the caller owns a lock:

lock_alloc Allocates system memory for a simple or complex lock.
lock_free Frees the system memory of a simple or complex lock.
lock_mine Checks whether a simple or complex lock is owned by the caller.

Simple Locks

Simple locks are exclusive-write, non-recursive locks that protect thread-thread or thread-interrupt critical sections. Simple locks are preemptable, meaning that a kernel thread can be preempted by another, higher priority kernel thread while it holds a simple lock. The simple lock kernel services are:

simple_lock_init Initializes a simple lock.
simple_lock, simple_lock_try Locks a simple lock.
simple_unlock Unlocks a simple lock.

On a multiprocessor system, simple locks that protect thread-interrupt critical sections must be used in conjunction with interrupt control in order to serialize execution both within the executing processor and between different processors. On a uniprocessor system interrupt control is sufficient; there is no need to use locks. The following kernel services provide appropriate locking calls for the system on which they are executed:

disable_lock Raises the interrupt priority, and locks a simple lock if necessary.
unlock_enable Unlocks a simple lock if necessary, and restores the interrupt priority.

Using the disable_lock and unlock_enable kernel services to protect thread-interrupt critical sections (instead of calling the underlying interrupt control and locking kernel services directly) ensures that multiprocessor-safe code does not make unnecessary locking calls on uniprocessor systems.

Simple locks are spin locks; a kernel thread that attempts to acquire a simple lock may spin (busy-wait: repeatedly execute instructions which do nothing) if the lock is not free. The table shows the behavior of kernel threads and interrupt handlers that attempt to acquire a busy simple lock.

Caller Owner is Running Owner is Sleeping
Thread (with interrupts enabled) Caller spins initially; it sleeps if the maximum spin threshold is crossed. Caller sleeps immediately.
Interrupt handler or thread (with interrupts disabled) Caller spins until lock is acquired. Caller spins until lock is freed (must not happen).
Note
On uniprocessor systems, the maximum spin threshold is set to one, meaning that that a kernel thread will never spin waiting for a lock.

A simple lock that protects a thread-interrupt critical section must never be held across a sleep, otherwise the interrupt could spin for the duration of the sleep, as shown in the table. This means that such a routine must not call any external services that might result in a sleep. In general, using any kernel service which is callable from process level may result in a sleep, as can accessing unpinned data. These restrictions do not apply to simple locks that protect thread-thread critical sections.

The lock word of a simple lock must be located in pinned memory if simple locking services are called with interrupts disabled.

Complex Locks

Complex locks are read-write locks that protect thread-thread critical sections. Complex locks are preemptable, meaning that a kernel thread can be preempted by another, higher priority kernel thread while it holds a complex lock. The complex lock kernel services are:

lock_init Initializes a complex lock.
lock_islocked Tests whether a complex lock is locked.
lock_done Unlocks a complex lock.
lock_read, lock_try_read Locks a complex lock in shared-read mode.
lock_read_to_write, lock_try_read_to_write Upgrades a complex lock from shared-read mode to exclusive-write mode.
lock_write, lock_try_write Locks a complex lock in exclusive-write mode.
lock_write_to_read Downgrades a complex lock from exclusive-write mode to shared-read mode.
lock_set_recursive Prepares a complex lock for recursive use.
lock_clear_recursive Prevents a complex lock from being acquired recursively.

By default, complex locks are not recursive (they cannot be acquired in exclusive-write mode multiple times by a single thread). A complex lock can become recursive through the lock_set_recursive kernel service. A recursive complex lock is not freed until lock_done is called once for each time that the lock was locked.

Complex locks are not spin locks; a kernel thread that attempts to acquire a complex lock may spin briefly (busy-wait: repeatedly execute instructions which do nothing) if the lock is not free. The table shows the behavior of kernel threads that attempt to acquire a busy complex lock:

Current Lock Mode Owner is Running and no Other Thread is Asleep on This Lock Owner is Sleeping
Exclusive-write Caller spins initially, but sleeps if the maximum spin threshold is crossed, or if the owner later sleeps. Caller sleeps immediately.
Shared-read being acquired for exclusive-write Caller sleeps immediately.
Shared-read being acquired for shared-read Lock granted immediately
Note
  1. On uniprocessor systems, the maximum spin threshold is set to one, meaning that a kernel thread will never spin waiting for a lock.
  2. The concept of a single owner does not apply to a lock held in shared-read mode.

Lockl Locks

Note
Lockl locks (previously called conventional locks) are only provided to ensure compatibility with existing code. New code should use simple or complex locks.

Lockl locks are exclusive-access and recursive locks. The lockl lock kernel services are:

lockl Locks a conventional lock.
unlockl Unlocks a conventional lock.

A thread which tries to acquire a busy lockl lock sleeps immediately.

The lock word of a lockl lock must be located in pinned memory if the lockl service is called with interrupts disabled.

Atomic Operations

Atomic operations are sequences of instructions that guarantee atomic accesses and updates of shared single word variables. This means that atomic operations cannot protect accesses to complex data structures in the way that locks can, but they provide a very efficient way of serializing access to a single word.

The atomic operation kernel services are:

fetch_and_add Increments a single word variable atomically.
fetch_and_and, fetch_and_or Manipulates bits in a single word variable atomically.
compare_and_swap Conditionally updates or returns a single word variable atomically.

Single word variables accessed by atomic operations must be aligned on a full word boundary, and must be located in pinned memory if atomic operation kernel services are called with interrupts disabled.

[ Top of Page | Previous Page | Next Page | Contents | Index | Library Home | Legal | Search ]