Writing Device Drivers

Thread Synchronization

These interfaces enable a device to exploit multiple CPUs on multiprocessor machines. They prevent the corruption of data by simultaneous access by more than one thread. The mechanisms for thread synchronization are mutual exclusion locks (mutexes), condition variables, readers/writer locks, and semaphores.

void cv_init(kcondvar_t *cvp, char *name,  	
			kcv_type_t type, void *arg);

cv_init(9F) prepares the condition variable pointed to by cvp for use. CV_DRIVER should be specified for type.

void cv_destroy(kcondvar_t *cvp);

cv_destroy(9F) releases the resources associated with the condition variable pointed to by cvp.

void cv_wait(kcondvar_t *cvp, kmutex_t *mp);

cv_wait(9F) must be called while holding the mutex pointed to by mp. cv_wait(9F) releases the mutex and blocks until a call is made to cv_signal(9F) or cv_broadcast(9F) for the condition variable pointed to by cvp. cv_wait(9F) then reacquires the mutex and returns.

Use cv_wait(9F) to block on a condition that may take a while to change.

void cv_signal(kcondvar_t *cvp);

cv_signal(9F) unblocks one cv_wait(9F) call that is blocked on the condition variable pointed to by cvp. Call cv_signal(9F) when the condition that cv_wait(9F) is waiting for becomes true. To unblock all threads blocked on this condition variable, use cv_broadcast(9F).

void cv_broadcast(kcondvar_t *cvp);

cv_broadcast(9F) unblocks all threads that are blocked on the condition variable pointed to by cvp. To unblock only one thread, use cv_signal(9F).

int cv_wait_sig(kcondvar_t *cvp, kmutex_t *mp);

cv_wait_sig(9F) is like cv_wait(9F), but if the calling thread receives a signal while cv_wait_sig(9F) is blocked, cv_wait_sig(9F) immediately reacquires the mutex and returns zero.

int cv_timedwait(kcondvar_t *cvp, kmutex_t *mp,  	
			clock_t timeout);

cv_timedwait(9F) is like cv_wait(9F), but it returns -1 at time timeout if the condition has not occurred. timeout is given as a number of clock ticks since the last reboot. drv_usectohz(9F) converts microseconds, a platform-independent time, to clock ticks.

int cv_timedwait_sig(kcondvar_t *cvp, kmutex_t *mp,  	
			clock_t timeout);

cv_timedwait_sig(9F) is like cv_timedwait(9F) and cv_wait_sig(9F), except that it returns -1 at time timeout if the condition has not occurred. If the calling thread receives a signal while cv_timedwait_sig(9F) is blocked, cv_timedwait_sig(9F) immediately returns zero. In all cases, cv_timedwait_sig(9F) reacquires the mutex before returning.

void mutex_init(kmutex_t *mp, char *name,  	
			kmutex_type_t type, void *arg);

mutex_init(9F) prepares the mutual exclusion lock pointed to by mp for use. MUTEX_DRIVER should be specified for type, and pass an interrupt block cookie of type ddi_iblock_cookie_t for arg. The interrupt block cookie is returned by ddi_get_iblock_cookie(9F).

void mutex_enter(kmutex_t *mp);

mutex_enter(9F) acquires the mutual exclusion lock pointed to by mp. If another thread holds the mutex, mutex_enter(9F) will either block or spin, waiting for the mutex to become available.

Mutexes are not reentrant; if a thread calls mutex_enter(9F) on a mutex it already holds, the system will panic.

mp is assumed to protect a certain set of data, often a single data structure, and all driver threads accessing those data must first acquire the mutex by calling mutex_enter(9F). This is accomplished by mutual agreement and consistency among all driver code paths that access the data in question; mutex_enter(9F) in no way prevents other threads from accessing the data. It is only when all driver code paths agree to acquire the mutex before accessing the data that the data are safe.

void mutex_exit(kmutex_t *mp);

mutex_exit(9F) releases the mutual exclusion lock pointed to by mp.

void mutex_destroy(kmutex_t *mp);

mutex_destroy(9F) releases the resources associated with the mutual exclusion lock pointed to by mp.

int mutex_owned(kmutex_t *mp);

mutex_owned(9F) returns nonzero if the mutual exclusion lock pointed to by mp is currently held; otherwise, it returns zero. Use mutex_owned(9F) only in an expression used in ASSERT(9F).

int mutex_tryenter(kmutex_t *mp);

mutex_tryenter(9F) is similar to mutex_enter(9F), but it does not block waiting for the mutex to become available. If the mutex is held by another thread, mutex_tryenter(9F) returns zero. Otherwise, mutex_tryenter(9F) acquires the mutex and returns nonzero.

void rw_destroy(krwlock_t *rwlp);

rw_destroy(9F) releases the resources associated with the readers or writer lock pointed to by rwlp.

void rw_downgrade(krwlock_t *rwlp);

If the calling thread holds the lock pointed to by rwlp for writing, rw_downgrade(9F) releases the lock for writing, but retains the lock for reading. This allows other readers to acquire the lock unless a thread is waiting to acquire the lock for writing.

void rw_enter(krwlock_t *rwlp, krw_t enter_type);

If enter_type is RW_READER, rw_enter(9F) acquires the lock pointed to by rwlp for reading if no thread currently holds the lock for writing, and if no thread is waiting to acquire the lock for writing. Otherwise, rw_enter(9F) blocks.

If enter_type is RW_WRITER, rw_enter(9F) acquires the lock for writing if no thread holds the lock for reading or writing, and if no other thread is waiting to acquire the lock for writing. Otherwise, rw_enter(9F) blocks.

void rw_exit(krwlock_t *rwlp);

rw_exit(9F) releases the lock pointed to by rwlp.

void rw_init(krwlock_t *rwlp, char *name,  	
			krw_type_t type, void *arg);

rw_init(9F) prepares the readers or writer lock pointed to by rwlp for use. RW_DRIVER should be passed for type.

int rw_read_locked(krwlock_t *rwlp);

The lock pointed to by rwlp must be held during a call to rw_read_locked(9F). If the calling thread holds the lock for reading, rw_read_locked(9F) returns a nonzero value. If the calling thread holds the lock for writing, rw_read_locked(9F) returns zero.

int rw_tryenter(krwlock_t *rwlp, krw_t enter_type);

rw_tryenter(9F) attempts to enter the lock, like rw_enter(9F), but never blocks. It returns a nonzero value if the lock was successfully entered, and zero otherwise.

int rw_tryupgrade(krwlock_t *rwlp);

If the calling thread holds the lock pointed to by rwlp for reading, rw_tryupgrade(9F) acquires the lock for writing if no other threads hold the lock, and no thread is waiting to acquire the lock for writing. If rw_tryupgrade(9F) cannot acquire the lock for writing, it returns zero.

void sema_init(ksema_t *sp, u_int val, char *name,  	
			ksema_type_t type, void *arg);

sema_init(9F) prepares the semaphore pointed to by sp for use. SEMA_DRIVER should be passed for type. count is the initial count for the semaphore, which usually should be 1 or 0. In almost all cases, drivers should pass 1 for count.

void sema_destroy(ksema_t *sp);

sema_destroy(9F) releases the resources associated with the semaphore pointed to by sp.

void sema_p(ksema_t *sp);

sema_p(9F) acquires the semaphore pointed to by sp by decrementing the counter if its value is greater than zero. If the semaphore counter is zero, sema_p(9F) blocks, waiting to acquire the semaphore.

int sema_p_sig(ksema_t *sp);

sema_p_sig(9F) is like sema_p(9F), except that if the calling thread has a signal pending, and the semaphore counter is zero, sema_p_sig(9F) returns zero without blocking.

void sema_v(ksema_t *sp);

sema_v(9F) releases the semaphore pointed to by sp by incrementing its counter.

int sema_tryp(ksema_t *sp);

sema_tryp(9F) is similar to sema_p(9F), but if the semaphore counter is zero, sema_tryp(9F) immediately returns zero.