Device Driver Tutorial

Managing Thread Synchronization

The Quote Of The Day Version 3 driver uses condition variables and mutual exclusion locks (mutexes) together to manage thread synchronization. See the Multithreaded Programming Guide for more information about mutexes, condition variables, and thread synchronization.

In this driver, the mutex and condition variable both are initialized in the qotd_attach() entry point and destroyed in the qotd_detach() entry point. The condition variable is tested in the qotd_rw() routine and in the qotd_ioctl() entry point.

The condition variable waits on the QOTD_BUSY condition. This condition is needed because the driver must do some operations that rely on exclusive access to internal structures without holding a lock. Accessing the storage buffer or its metadata requires mutual exclusion, but the driver cannot hold a lock if the operation might sleep. Instead of holding a lock in this case, the driver waits on the QOTD_BUSY condition.

The driver acquires a mutex when the driver tests the condition variable and when the driver accesses the storage buffer. The mutex protects the storage buffer. Failure to use a mutual exclusion when accessing the storage buffer could allow one user process to resize the buffer while another user process tries to read the buffer, for example. The result of unprotected buffer access could be data corruption or a panic.

The condition variable is used when functions are called that might need to sleep. The ddi_copyin(9F), ddi_copyout(9F), and uiomove(9F) functions can sleep. Memory allocation can sleep if you use the SLEEP flag. Functions must not hold a mutex while they are sleeping. Sleeping while holding a mutex can cause deadlock. When a function might sleep, set the QOTD_BUSY flag and take the condition variable, which drops the mutex. To avoid race conditions, the QOTD_BUSY flag can be set or cleared only when holding the mutex. For more information on deadlock, see Using Mutual Exclusion Locks in Multithreaded Programming Guide and Avoiding Deadlock in Multithreaded Programming Guide.

Locking Rules for Quote Of The Day Version 3

    The locking rules for this qotd_3 driver are as follows:

  1. You must have exclusive access to do any of the following operations. To have exclusive access, you must own the mutex or you must set QOTD_BUSY. Threads must wait on QOTD_BUSY.

    • Test the contents of the storage buffer.

    • Modify the contents of the storage buffer.

    • Modify the size of the storage buffer.

    • Modify variables that refer to the address of the storage buffer.

    1. If your operation does not need to sleep, do the following actions:

    2. Acquire the mutex.

    3. Wait until QOTD_BUSY is cleared. When the thread that set QOTD_BUSY clears QOTD_BUSY, that thread also should signal threads waiting on the condition variable and then drop the mutex.

    4. Perform your operation. You do not need to set QOTD_BUSY before you perform your operation.

    5. Drop the mutex.

    The following code sample illustrates this rule:

    mutex_enter(&qsp->lock);
    while (qsp->flags & QOTD_BUSY) {
            if (cv_wait_sig(&qsp->cv, &qsp->lock) == 0) {
                    mutex_exit(&qsp->lock);
                    ddi_umem_free(new_cookie);
                    return (EINTR);
            }
    }
    memcpy(new_qotd, qsp->qotd, min(qsp->qotd_len, new_len));
    ddi_umem_free(qsp->qotd_cookie);
    qsp->qotd = new_qotd;
    qsp->qotd_cookie = new_cookie;
    qsp->qotd_len = new_len;
    qsp->flags |= QOTD_CHANGED;
    mutex_exit(&qsp->lock);
    1. If your operation must sleep, do the following actions:

    2. Acquire the mutex.

    3. Set QOTD_BUSY.

    4. Drop the mutex.

    5. Perform your operation.

    6. Reacquire the mutex.

    7. Signal any threads waiting on the condition variable.

    8. Drop the mutex.

These locking rules are very simple. These three rules ensure consistent access to the buffer and its metadata. Realistic drivers probably have more complex locking requirements. For example, drivers that use ring buffers or drivers that manage multiple register sets or multiple devices have more complex locking requirements.

Lock and Condition Variable Members of the State Structure

The device state structure for Version 3 of the Quote Of The Day driver contains two new members to help manage thread synchronization:

Creating and Destroying Locks and Condition Variables

Version 3 of the Quote Of The Day driver defines two constants to make sure the mutex and condition variable are destroyed when the driver is finished with them. The driver uses these constants to set and reset the new flags member of the device state structure.

Waiting on Signals

In the qotd_rw() and qotd_ioctl() routines, the cv_wait_sig(9F) calls wait until the condition variable is signaled to proceed or until a signal(3C) is received. Either the cv_signal(9F) function or the cv_broadcast(9F) function signals the cv condition variable to proceed.

A thread can wait on a condition variable until either the condition variable is signaled or a signal(3C) is received by the process. The cv_wait(9F) function waits until the condition variable is signaled but ignores signal(3C) signals. This driver uses the cv_wait_sig(9F) function instead of the cv_wait(9F) function because this driver responds if a signal is received by the process performing the operation. If a signal(3C) is taken by the process, this driver returns an interrupt error and does not complete the operation. The cv_wait_sig(9F) function usually is preferred to the cv_wait(9F) function because this implementation offers the user program more precise response. The signal(3C) causes an effect closer to the point at which the process was executing when the signal(3C) was received.

In some cases, you cannot use the cv_wait_sig(9F) function because your driver cannot be interrupted by a signal(3C). For example, you cannot use the cv_wait_sig(9F) function during a DMA transfer that will result in an interrupt later. In this case, if you abandon the cv_wait_sig(9F) call, you have nowhere to put the data when the DMA transfer is finished, and your driver will panic.