|
|
Synchronization variables are synchronization primitives that are used to coordinate the execution of processes based on asynchronous events. When allocated, synchronization variables serve as points upon which one or more processes can block until an event occurs. Then one or all of the processes can be unblocked. at the same time.
In addition, you can set relative priorities for processes, to be used while running in the kernel after unblocking (waking up). see pri(D5).
Note that any function that blocks on a synchronization variable must be able to tolerate premature wakeups. In other words, the code must be written so that, after it is unblocked (awakened), it reexamines the condition on which it was blocked. This applies to the SV_WAIT(D3) and SV_WAIT_SIG(D3) as well as to sleep(D3).
The manual pages for functions and other information used for synchronization variables are listed below. These routines are available in ddi: 5 and higher.
Synchronous variable locking functions
Function | Description |
SV_ALLOC(D3) | allocate and initialize a synchronization variable |
SV_WAIT(D3) | block on a synchronization variable |
SV_WAIT_SIG(D3) | like SV_WAIT, but can also be unblocked by a signal |
SV_SIGNAL(D3) | unblock one process blocked on a synchronization variable |
SV_BROADCAST(D3) | wake up all processes blocked on a synchronization variable |
SV_DEALLOC(D3) | deallocate an instance of a synchronization variable |
Older drivers that were not multithreaded used the sleep(D3) and wakeup(D3) functions rather than the synchronization variables. The major differences between the use of sleep( ) and wakeup( ) in single-threaded drivers and the analogous (but by no means identical) synchronization variable routines used in multithreaded drivers are:
Calls to wakeup( ) result in all processes sleeping on a given event (kernel address) being awakened; SV_SIGNAL( ) only unblocks a single process.
If needed, any kernel context can wake up all processes sleeping on a event by calling SV_BROADCAST( ).
The sleep( ) and wakeup( ) functions map events to kernel addresses, whereas the SV_WAIT and SV_SIGNAL( ) functions require kernel objects of type sv_t that have to be explicitly allocated and initialized with the SV_ALLOC(D3) function.
On uniprocessor systems, only one active thread of execution exists in the driver code. This thread of execution can be suspended when a driver calls sleep( ) or interrupted when device interrupts are serviced. In contrast, on multiprocessor systems, there may be multiple threads of execution through the same driver code, as user requests for I/O are simultaneously serviced on several processors. In effect, critical sections of driver code have to be protected from interrupts as well as kernel contexts executing on different processors. This is accomplished by using locks. See ``Critical code section''.
In single-threaded drivers, calls to spl(D3) are used to protect calls to sleep( ) to avoid race conditions between multiple base and interrupt contexts. In multithreaded drivers, all calls to SV_WAIT( ) must be preceded by acquiring spin locks and passing the lock pointer as an argument to the routine. In turn, SV_WAIT( ) drops the spin lock and blocks the calling context. Again, use of spin locks alone does not guarantee protection of critical regions of driver code unless the locks are acquired at the appropriate IPLs (interrupt priority levels). The interrupt priority level should be high enough to lock out the highest interrupt that can also execute the critical region. See ``IPLs (Interrupt Priority Levels)''.
The following examples illustrate the difference between using sleep/wakeup in a single-threaded driver and a multithreaded version using SV_WAIT/SV_SIGNAL.
Synchronization with sleep/wakeup Using sleep/wakeup, the blocking code is:
opl = spldisk(); while (variable == FALSE) sleep(&variable); /* Critical code section */ splx(opl);The unblocking code is:
variable = TRUE; wakeup(&variable);
Synchronization with SV_WAIT/SV_SIGNAL Using synchronization variables, the blocking code is:
opl = LOCK(samp_lock,pldisk);The unblocking code is:while (variable == FALSE) { SV_WAIT(samp_sv, SAMP_PRI, samp_lock); LOCK(samp_lock, pldisk); }
/* Critical code section */
UNLOCK(samp_lock, opl);
opl = LOCK(samp_lock,pldisk); variable = TRUE; UNLOCK(samp_lock, opl); SV_SIGNAL(samp_sv);