Writing Device Drivers

Locking Primitives

In traditional UNIX systems, any section of kernel code runs until it explicitly gives up the processor by calling sleep() or is interrupted by hardware. This is not true in SunOS 5.7! A kernel thread can be preempted at any time to run another thread. Because all kernel threads share kernel address space, and often need to read and modify the same data, the kernel provides a number of locking primitives to prevent threads from corrupting shared data. These mechanisms include mutual exclusion locks, readers/writer locks, and semaphores.

Storage Classes of Driver Data

The storage class of data is a guide to whether the driver might need to take explicit steps to control access to the data. There are three types of data storage classes:

Mutual-Exclusion Locks

A mutual-exclusion lock, or mutex, is usually associated with a set of data and regulates access to that data. Mutexes provide a way to allow only one thread at a time access to that data.

Table 4-1 Mutex Routines

Name 

Description 

mutex_init(9F)

Initializes a mutex. 

mutex_destroy(9F)

Releases any associated storage. 

mutex_enter(9F)

Acquires a mutex. 

mutex_tryenter(9F)

Acquires a mutex if available; but does not block. 

mutex_exit(9F)

Releases a mutex. 

mutex_owned(9F)

Test sif the mutex is held by the current thread. To be used in ASSERT(9F) only. 

Setting Up Mutexes

Device drivers usually allocate a mutex for each driver data structure. The mutex is typically a field in the structure and is of type kmutex_t. mutex_init(9F) is called to prepare the mutex for use. This is usually done at attach(9E) time for per-device mutexes and _init(9E) time for global driver mutexes.

For example,

	struct xxstate *xsp;
	...
 	mutex_init(&xsp->mu, "xx mutex", MUTEX_DRIVER, NULL);
 	...

For a more complete example of mutex initialization see Chapter 5, Autoconfiguration.

The driver must destroy the mutex with mutex_destroy(9F) before being unloaded. This is usually done at detach(9E) time for per-device mutexes and _fini(9E) time for global driver mutexes.

Using Mutexes

Every section of the driver code that needs to read or write the shared data structure must do the following:

For example, to protect access to the busy flag in the state structure:

	...
 	mutex_enter(&xsp->mu);
 	xsp->busy = 0;
 	mutex_exit(&xsp->mu);
 	....

The scope of a mutex--the data it protects--is entirely up to the programmer. A mutex protects some particular data structure because the programmer chooses to do so and uses it accordingly. A mutex protects a data structure only if every code path that accesses the data structure does so while holding the mutex. For additional guidelines on using mutexes see Appendix G, Advanced Topics.

Readers/Writer Locks

A readers/writer lock regulates access to a set of data. The readers/writer lock is so called because many threads can hold the lock simultaneously for reading, but only one thread can hold it for writing.

Most device drivers do not use readers/writer locks. These locks are slower than mutexes and provide a performance gain only when protecting data that is not frequently written but is commonly read by many concurrent threads. In this case, contention for a mutex could become a bottleneck, so using a readers/writer lock might be more efficient. See rwlock(9F) for more information.

Semaphores

Counting semaphores are available as an alternative primitive for managing threads within device drivers. See semaphore(9F) for more information.