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:
Use the multithreading semantics of the entry point to your advantage.
Make all entry points re-entrant. You can reduce the amount of shared data by changing a static variable to automatic.
If your driver acquires multiple mutexes, acquire and release the mutexes in the same order in all code paths.
Hold and release locks within the same functional space.
Avoid holding driver mutexes when calling DDI interfaces that can block, for example, kmem_alloc(9F) with KM_SLEEP.
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.