lock_management(3K) DG/UX R4.11MU05 lock_management(3K)
NAME
lock_management: lm_initialize_sequenced_lock,
lm_initialize_unsequenced_lock, lm_obtain_sequenced_lock,
lm_obtain_sequenced_lock_no_wait, lm_obtain_unsequenced_lock,
lm_obtain_unsequenced_lock_no_wait, lm_release_sequenced_lock,
lm_release_unsequenced_lock, misc_obtain_spin_lock,
misc_release_spin_lock - implement locks on critical sections of data
SYNOPSIS
#include "/usr/src/uts/aviion/ii/i_lm.h"
void lm_initialize_sequenced_lock (lock_ptr)
lm_sequenced_lock_ptr_type lock_ptr; /*WRITE ONLY*/
void lm_initialize_unsequenced_lock (lock_ptr)
lm_unsequenced_lock_ptr_type lock_ptr; /*WRITE ONLY*/
void lm_obtain_sequenced_lock (lock_ptr)
lm_sequenced_lock_ptr_type lock_ptr; /*WRITE ONLY*/
boolean_type lm_obtain_sequenced_lock_no_wait (lock_ptr)
lm_sequenced_lock_ptr_type lock_ptr; /*READ/WRITE*/
void lm_obtain_unsequenced_lock(lock_ptr)
lm_unsequenced_lock_ptr_type lock_ptr; /*WRITE ONLY*/
boolean_type lm_obtain_unsequenced_lock_no_wait (lock_ptr)
lm_unsequenced_lock_ptr_type lock_ptr; /*READ/WRITE*/
void lm_release_sequenced_lock(lock_ptr)
lm_sequenced_lock_ptr_type lock_ptr; /*WRITE ONLY*/
void lm_release_unsequenced_lock(lock_ptr)
lm_unsequenced_lock_ptr_type lock_ptr; /*WRITE ONLY*/
#include "/usr/src/uts/aviion/ii/i_misc.h"
void misc_obtain_spin_lock (lock_ptr)
misc_spin_lock_ptr_type lock_ptr; /*READ/WRITE*/
void misc_release_spin_lock (lock_ptr)
misc_spin_lock_ptr_type lock_ptr; /*READ/WRITE*/
where:
lock_ptr Pointer to lock to be initialized, obtained, or released.
DESCRIPTION
The following routines are described in this man page:
lm_initialize_sequenced_lock Initialize a sequenced lock
lm_initialize_unsequenced_lock Initialize an unsequenced lock
lm_obtain_sequenced_lock Obtain a sequenced lock
lm_obtain_sequenced_lock_no_wait Get sequenced lock, if available
lm_obtain_unsequenced_lock Obtain an unsequenced lock
lm_obtain_unsequenced_lock_no_wait Get unsequenced lock, if available
lm_release_sequenced_lock Release a sequenced lock
lm_release_unsequenced_lock Release an unsequenced lock
misc_obtain_spin_lock Obtain a spin lock
misc_release_spin_lock Release a spin lock
Overview to Using Locks on the DG/UX System
The kernel lock facilities are used to protect critical sections of
code. If more than one LWP is executing the same code and/or
accessing the same data at the same time, the data may become
corrupted. The lock facilities guarantee exclusive access to the
code or data covered by the lock while the lock-holder is executing
in that region.
Most operating systems use locking facilities. However, locking is
particularly important in the DG/UX kernel. Unlike traditional UNIX
kernels, the DG/UX kernel provides fully preemptive scheduling. This
means that a LWP might be suspended while updating a data base and
another LWP that accesses the same data might be given control. In
addition, because the DG/UX system runs in a fully symmetric
environment, you can't disable interrupts for protection as has often
been done in single-processor UNIX kernels. Such disabling only
affects one processor--other LWPs may be running on other processors.
To further enhance performance with multiple processors, the DG/UX
kernel also provides fine-grained locks that protect individual data
bases rather than the traditional approach that locks the entire
kernel data base at once.
All locks provide mutual exclusion. However, several types of locks
are available each of which provides certain additional features.
Different locks also vary in performance and in the memory required
to implement them. The three types of locks the DG/UX kernel
provides are: sequenced locks, unsequenced locks, and spin locks.
There are three routines for each type of lock: a routine to
initialize the lock; a routine to obtain the lock (start locking);
and a routine to release the lock (stop locking). Note that routine
names consist of the operation and lock type plus the kernel
subsystem (and, hence, include file) where the routine is located--
for example, lm_obtain_sequenced_lock. Each type of lock also uses
its own corresponding data structures.
To use a lock, you first allocate space for it and then call the
appropriate initialization routine. You then call the obtain
routine. Once this call returns, you hold the lock and no other LWP
can have the lock until you call the corresponding release routine.
Sometimes someone else may already hold the lock you want when you
try to obtain it. This situation, called contention, is handled
differently depending on the type of lock involved. In fact, what
happens during contention is one of the major differences that define
the different locks. So, before discussing this situation, we'll
describe the different types of locks.
Spin locks are the simplest type of lock. They cause the caller to
loop within the call until the lock can be obtained. This looping,
called a "busy wait," consumes a lot of CPU resources because the LWP
continues to run on the physical processor until the lock is
available.
Spin locks are very dangerous because the potential for deadlock is
high. Obviously, to release a lock the owner of the lock must be
able to execute the release routine for that lock. If someone else
busy waits for a spin lock on the same processor that is running the
lock-owner LWP, they will tie up the processor and the lock owner
will not be able to call the release routine. This is called
deadlock.
Because of the potential for deadlock spin locks should generally be
avoided. If you must use them, you should use them only for
protecting very small critical sections of code (only a few
instructions in length). You should also make sure that the LWP
cannot lose the processor on which it is running while holding a spin
lock. This means that the LWP cannot take any action that might
require it to be removed from the processor including taking a page
fault. Thus, the lock itself must be allocated in wired memory and
you must only reference wired memory while holding the lock.
Finally, while holding a spin lock, you must also ensure that
interrupts are disabled.
The sequence to gain exclusive access to a resource protected by a
spin lock is as follows:
nk_jp_disable_my_interrupts();
misc_obtain_spin_lock(&some_resource_spin_lock);
.
.
misc_release_spin_lock(&some_resource_spin_lock);
nk_jp_enable_my_interrupts();
You disable interrupts before attempting to gain the spin lock.
Then, if the lock is not available, it can only be because another
LWP on a different processor is holding the lock. If this happens,
your LWP, by owning its home processor, will spin until the lock
becomes available. Such busy-waiting is a major reason why spin
locks should be held only for a short time.
You will use sequenced or unsequenced locks for most locking needs.
Neither sequenced nor unsequenced locks use busy waiting; so the
holder of the lock can give up the processor. They differ in how
waiting is done.
Sequenced locks grant access on a first-come-first-serve basis. They
avoid the scheduling overhead by ordering contending LWPs based on
when they first tried to obtain the lock. When the lock is released,
only the next LWP in line is awakened.
Unsequenced locks are faster and take less space and CPU time than
sequenced locks. When you call to obtain an unsequenced lock, the
kernel removes your LWP from the processor until the lock is
available (that is, when the current owner releases it). They
provide no ordering of requesters.
Unsequenced locks, however, may not perform well under high
contention, because they can cause a cascade of rescheduling. When
the lock is released, ALL the LWPs waiting on the lock are awakened
(made runnable again). At some point after they start running, they
will attempt to obtain the lock again. One of them will be first and
will succeed in obtaining the lock. The rest will find the lock
already locked and will be put back to sleep until the new owner
releases the lock and the sequence of events repeats itself. The
resulting cascade of awakenings and reschedulings creates a high cost
in system time.
Unsequenced locks might also have a "livelock" problem. If new LWPs
are always trying to get the lock, a LWP might recurrently fail to
obtain the lock, and thus wait a long time to make forward progress.
The LWP would not be dead, but would essentially be looping trying
but failing to get the lock and continue.
Sequenced locks avoid these problems, but at a cost. The cost is in
performance (obtaining and releasing them is slower and takes more
CPU time) and space (a sequenced lock takes more space to implement).
The cost results from the queue of waiting LWPs that is associated
with a sequenced lock. When a LWP must wait on the lock, it is
entered onto the end of a FIFO queue of waiting LWPs. When the lock
is released, the system wakes up only the LWP at the head of the list
and this LWP obtains the lock.
Both sequenced and unsequenced locks provide no-wait versions of
their obtain routines that return control immediately. The no-wait
routines either return with the lock now locked on the caller's
behalf, or with an indication that the lock could not be obtained.
The no-wait version allows a LWP to handle other operations and try
to obtain the lock again later.
For a more detailed discussion of locks on the DG/UX system, see the
reference listed in the Preface of this manual.
Constants and Data Structures
This subsection describes constants and data structures defined in
the include files cited in the SYNOPSIS section and used by the
routines documented in this man page.
Try to avoid dependencies on the specifics of these structures, such
as size or location of fields, because these specifics may change in
later releases of the software. You can verify exact variable
definitions in the appropriate include file. The best way to avoid
such dependencies is to use kernel-supplied routines to manipulate
these structures.
lm_sequenced_lock_type
typedef struct
{
lm_resource_counter_type rc;
} lm_sequenced_lock_type ;
This type is a sequenced lock. A sequenced lock may be created by
simply declaring an instance of this type. The user of the lock is
responsible for allocating the space occupied by the lock instance
and reclaiming that space when the lock is destroyed. A sequenced
lock is simply a resource counter that has an initial value of one.
lm_unsequenced_lock_type
typedef struct
{
...
} lm_unsequenced_lock_type ;
This type is an unsequenced lock. An unsequenced lock may be created
by simply declaring an instance of this type. The user of the lock
is responsible for allocating the space occupied by the lock instance
and reclaiming that space when the lock is destroyed.
misc_spin_lock_type
typedef bit32e_type misc_spin_lock_type ;
This type defines a spin lock. The spin lock uses all 32 bits. The
lock is considered held when all bits are 1, and is considered not
held when all bits are zero.
lm_initialize_sequenced_lock
This routine initializes a sequenced lock. None of the obtain or
release operations should be performed on a lock until it has been
initialized by this routine.
lm_initialize_unsequenced_lock
This routine initializes an unsequenced lock. None of the obtain or
release operations should be performed on a lock until it has been
initialized by this routine.
lm_obtain_sequenced_lock
This routine obtains the specified lock. The calling LWP is pended
if the lock is not immediately available.
lm_obtain_sequenced_lock_no_wait
This routine obtains the specified lock if it is not already held.
The calling LWP is not pended if the lock is not immediately
available. The return value indicates whether the lock was obtained.
lm_obtain_unsequenced_lock
This routine obtains the specified lock. The calling LWP is pended
if the lock is not immediately available.
lm_obtain_unsequenced_lock_no_wait
This routine obtains the specified lock if it is not already held.
The calling LWP is not pended if the lock is not immediately
available.
lm_release_sequenced_lock
This routine releases the specified lock. If other LWPs are waiting
for the lock to become available, the next one in sequence will be
awakened.
lm_release_unsequenced_lock
This routine releases the specified lock. If other LWPs are waiting
for the lock to become available, all waiting LWPs will be awakened
and one will be given the lock.
misc_obtain_spin_lock
This routine obtains a spin lock if it is not already held. If the
lock is not immediately available, the LWP loops until it becomes
available. Spin locks are the only locks that can be obtained at
interrupt level.
misc_release_spin_lock
This routine releases a spin lock.
DIAGNOSTICS
Return Value
For lm_obtain_sequenced_lock_no_wait and
lm_obtain_unsequenced_lock_no_wait:
TRUE The lock was obtained.
FALSE The lock was not obtained.
For the other routines: none.
Errors
None.
SEE ALSO
interrupt_management(3K), process_management(3K).
Programming in the DG/UX Kernel Environment.
Licensed material--property of copyright holder(s)