Writing Device Drivers

Condition Variables

Condition variables are a standard form of thread synchronization. They are designed to be used with mutexes. The associated mutex is used to ensure that a condition can be checked atomically, and that the thread can block on the associated condition variable without missing either a change to the condition or a signal that the condition has changed. Condition variables must be initialized by calling cv_init(9F) and must be destroyed by calling cv_destroy(9F).


Note -

Condition variable routines are approximately equivalent to the routines sleep() and wakeup() used in SunOS 4.1.


Table 4-2 lists the condvar(9F) interfaces. The four wait routines - cv_wait(9F), cv_timedwait(9F), cv_wait_sig(9F), and cv_timedwait_sig(9F) - take a pointer to a mutex as an argument.

Table 4-2 Condition Variable Routines

Name 

Description 

cv_init(9F) 

Initializes a condition variable. 

cv_destroy(9F) 

Destroys a condition variable. 

cv_wait(9F) 

Waits for condition. 

cv_timedwait(9F) 

Waits for condition or timeout. 

cv_wait_sig(9F) 

Waits for condition or return zero on receipt of a signal. 

cv_timedwait_sig(9F) 

Waits for condition or timeout or signal. 

cv_signal(9F) 

Signals one thread waiting on the condition variable. 

cv_broadcast(9F) 

Signals all threads waiting on the condition variable. 

Initializing Condition Variables

Declare a condition variable (type kcondvar_t) for each condition. Usually, this is done in the driver's soft-state structure. Use cv_init(9F) to initialize each one. Similar to mutexes, condition variables are usually initialized at attach(9E) time. For example:

	...
 	cv_init(&xsp->cv, NULL, CV_DRIVER, NULL);
 	...

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

Multithreading Additions to the State Structure

This section adds the following fields to the state structure. See "Software State Structure" for more information.

int     busy;		/* device busy flag */
 	kmutex_t       mu;		/* mutex to protect state structure */
 	kcondvar_t     cv;		/* threads wait for access here */

Using Condition Variables

To use condition variables, follow these steps in the code path waiting for the condition:

  1. Acquire the mutex guarding the condition.

  2. Test the condition.

  3. If the test results do not allow the thread to continue, use cv_wait(9F) to block the current thread on the condition. cv_wait(9F) releases the mutex before blocking. Upon return from cv_wait(9F) (which will reacquire the mutex before returning), repeat the test.

  4. Once the test allows the thread to continue, set the condition to its new value. For example, set a device flag to busy.

  5. Release the mutex.

Follow these steps in the code path signaling the condition:

  1. Acquire the mutex guarding the condition.

  2. Set the condition.

  3. Signal the blocked thread with cv_signal(9F).

  4. Release the mutex.

Example 4-1 uses a busy flag, and mutex and condition variables to force the read(9E) routine to wait until the device is no longer busy before starting a transfer.


Example 4-1 Using Mutexes and Condition Variables

static int
xxread(dev_t dev, struct uio *uiop, cred_t *credp)
{
	struct xxstate *xsp;
	...
	mutex_enter(&xsp->mu);
	while (xsp->busy)
			cv_wait(&xsp->cv, &xsp->mu);
	xsp->busy = 1;
	mutex_exit(&xsp->mu);
	perform the data access
}

static u_int
xxintr(caddr_t arg);
{
	struct xxstate *xsp = (struct xxstate *)arg;
	mutex_enter(&xsp->mu);
	xsp->busy = 0;
	cv_broadcast(&xsp->cv);
	mutex_exit(&xsp->mu);
}

In Example 4-1, xxintr()( ) always calls cv_broadcast(9F), even if there are no threads waiting on the condition. This extra call can be avoided by using a want flag in the state structure, as shown in Example 4-2. Before a thread blocks on the condition variable (such as because the device is busy), it sets the want flag, indicating that it wants to be signaled when the condition occurs. When the condition occurs (the device finishes the transfer), the call to cv_broadcast(9F) is made only if the want flag is set.


Example 4-2 Using a want Flag

static int
xxread(dev_t dev, struct uio *uiop, cred_t *credp)
{
	struct xxstate *xsp;
	...
	mutex_enter(&xsp->mu);
	while (xsp->busy) {
			xsp->want = 1;
			cv_wait(&xsp->cv, &xsp->mu);
	}
	xsp->busy = 1;
	mutex_exit(&xsp->mu);
	perform error recovery
}
static u_int
xxintr(caddr_t arg);
{
	struct xxstate *xsp = (struct xxstate *)arg;
	mutex_enter(&xsp->mu);
	xsp->busy = 0;
	if (xsp->want) {
			xsp->want = 0;
			cv_broadcast(&xsp->cv);
	}
	mutex_exit(&xsp->mu);
}