JavaScript is required to for searching.
Skip Navigation Links
Exit Print View
Writing Device Drivers     Oracle Solaris 11 Express 11/10
search filter icon
search icon

Document Information

Preface

Part I Designing Device Drivers for the Oracle Solaris Platform

1.  Overview of Oracle Solaris Device Drivers

2.  Oracle Solaris Kernel and Device Tree

3.  Multithreading

Locking Primitives

Storage Classes of Driver Data

Mutual-Exclusion Locks

Setting Up Mutexes

Using Mutexes

Readers/Writer Locks

Semaphores

Thread Synchronization

Condition Variables in Thread Synchronization

Initializing Condition Variables

Waiting for the Condition

Signaling the Condition

cv_wait() and cv_timedwait() Functions

cv_wait_sig() Function

cv_timedwait_sig() Function

Choosing a Locking Scheme

Potential Locking Pitfalls

Threads Unable to Receive Signals

4.  Properties

5.  Managing Events and Queueing Tasks

6.  Driver Autoconfiguration

7.  Device Access: Programmed I/O

8.  Interrupt Handlers

9.  Direct Memory Access (DMA)

10.  Mapping Device and Kernel Memory

11.  Device Context Management

12.  Power Management

13.  Hardening Oracle Solaris Drivers

14.  Layered Driver Interface (LDI)

Part II Designing Specific Kinds of Device Drivers

15.  Drivers for Character Devices

16.  Drivers for Block Devices

17.  SCSI Target Drivers

18.  SCSI Host Bus Adapter Drivers

19.  Drivers for Network Devices

20.  USB Drivers

Part III Building a Device Driver

21.  Compiling, Loading, Packaging, and Testing Drivers

22.  Debugging, Testing, and Tuning Device Drivers

23.  Recommended Coding Practices

Part IV Appendixes

A.  Hardware Overview

B.  Summary of Oracle Solaris DDI/DKI Services

C.  Making a Device Driver 64-Bit Ready

D.  Console Frame Buffer Drivers

Index

Choosing a Locking Scheme

The locking scheme for most device drivers should be kept straightforward. Using additional locks allows more concurrency but increases overhead. Using fewer locks is less time consuming but allows less concurrency. Generally, use one mutex per data structure, a condition variable for each event or condition the driver must wait for, and a mutex for each major set of data global to the driver. Avoid holding mutexes for long periods of time. Use the following guidelines when choosing a locking scheme:

To look at lock usage, use lockstat(1M). lockstat(1M) monitors all kernel lock events, gathers frequency and timing data about the events, and displays the data.

See the Multithreaded Programming Guide for more details on multithreaded operations.

Potential Locking Pitfalls

Mutexes are not re-entrant by the same thread. If you already own the mutex, attempting to claim this mutex a second time leads to the following panic:

panic: recursive mutex_enter. mutex %x caller %x

Releasing a mutex that the current thread does not hold causes this panic:

panic: mutex_adaptive_exit: mutex not held by thread

The following panic occurs only on uniprocessors:

panic: lock_set: lock held and only one CPU

The lock_set panic indicates that a spin mutex is held and will spin forever, because no other CPU can release this mutex. This situation can happen if the driver forgets to release the mutex on one code path or becomes blocked while holding the mutex.

A common cause of the lock_set panic occurs when a device with a high-level interrupt calls a routine that blocks, such as cv_wait(9F). Another typical cause is a high-level handler grabbing an adaptive mutex by calling mutex_enter(9F).

Threads Unable to Receive Signals

The sema_p_sig(), cv_wait_sig(), and cv_timedwait_sig() functions can be awakened when the thread receives a signal. A problem can arise because some threads are unable to receive signals. For example, when close(9E) is called as a result of the application calling close(2), signals can be received. However, when close(9E) is called from within the exit(2) processing that closes all open file descriptors, the thread cannot receive signals. When the thread cannot receive signals, sema_p_sig() behaves as sema_p(), cv_wait_sig() behaves as cv_wait(), and cv_timedwait_sig() behaves as cv_timedwait().

Use caution to avoid sleeping forever on events that might never occur. Events that never occur create unkillable (defunct) threads and make the device unusable until the system is rebooted. Signals cannot be received by defunct processes.

To detect whether the current thread is able to receive a signal, use the ddi_can_receive_sig(9F) function. If the ddi_can_receive_sig()function returns B_TRUE, then the above functions can wake up on a received signal. If the ddi_can_receive_sig()function returns B_FALSE, then the above functions cannot wake up on a received signal. If the ddi_can_receive_sig()function returns B_FALSE, then the driver should use an alternate means, such as the timeout(9F) function, to reawaken.

One important case where this problem occurs is with serial ports. If the remote system asserts flow control and the close(9E) function blocks while attempting to drain the output data, a port can be stuck until the flow control condition is resolved or the system is rebooted. Such drivers should detect this case and set up a timer to abort the drain operation when the flow control condition persists for an excessive period of time.

This issue also affects the qwait_sig(9F) function, which is described in Chapter 7, STREAMS Framework – Kernel Level, in STREAMS Programming Guide.