[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


3    Using Simple Lock Interfaces

After you decide that the simple lock method is the appropriate method for locking specific resources, you use the simple lock interfaces to accomplish the locking. To use simple locks in a device driver, perform the following tasks:

To illustrate the use of these interfaces, the chapter uses code from an example device driver called /dev/xx that operates on some XX device. This example driver locks a softc structure resource called xx_softc.


[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


3.1    Declaring a Simple Lock Structure

Before using a simple lock, declare a simple lock structure for the resource you want to lock by using the decl_simple_lock_data interface. The following code fragment shows a call to decl_simple_lock_data in the /dev/xx device driver:


.
.
.
#include <kern/lock.h> [1]
.
.
.
struct xx_softc { int sc_openf; /* Open flag */ int sc_count; /* Count of characters written to device */ decl_simple_lock_data( , lk_xx_softc); /* SMP lock for xx_softc */ }xx_softc[NNONE]; [2]
.
.
.

  1. Includes the header file /usr/sys/include/kern/lock.h. The lock.h file defines the simple spin lock and complex lock structures that device drivers use for synchronization on single processor and multiprocessor systems. [Return to example]

  2. Declares an array of softc structures and calls it xx_softc. The /dev/xx driver uses the decl_simple_lock_data interface to declare a simple lock structure as a member of the xx_softc structure.

    The decl_simple_lock_data interface declares a simple lock structure, slock, of the specified name. You declare a simple lock structure for the purpose of protecting device driver data structures and device register access. You use decl_simple_lock_data to declare a simple lock structure and then pass it to the following simple lock-specific interfaces: simple_lock_init, simple_lock, simple_lock_try, simple_unlock, and simple_lock_terminate.

    The decl_simple_lock_data interface can take two arguments:

    [Return to example]

You can also declare a simple lock structure by using the typedef simple_lock_data_t as in the following example:


.
.
.
struct xx_softc { int sc_openf; /* Open flag */ int sc_count; /* Count of characters written to device */ simple_lock_data_t lk_xx_softc; /* SMP lock for xx_softc */ }xx_softc[NNONE]; [1]

  1. Declares an array of softc structures and calls it xx_softc. The /dev/xx driver declares a simple lock structure as a member of the xx_softc structure to protect the integrity of the data stored in the sc_openf and sc_count members. A device driver's softc structure is one resource that often requires protection in an SMP environment because device driver interfaces use it to share data. It is possible that more than one kernel thread might need to access the members of an xx_softc structure. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


3.2    Initializing a Simple Lock

After declaring the simple lock structure, you initialize it by calling the simple_lock_init interface. The following code fragment shows a call to simple_lock_init by the /dev/xx device driver's xxcattach interface. The xxcattach interface performs the tasks necessary to establish communication with the actual device. One of these tasks is to initialize any global data structures. Thus, the xxcattach interface initializes the simple lock structure lk_xx_softc.

The code fragment also shows the declaration of the simple lock structure in the xx_softc structure.


.
.
.
#include <kern/lock.h> [1]
.
.
.
struct xx_softc { int sc_openf; /* Open flag */ int sc_count; /* Count of characters written to device */ simple_lock_data_t lk_xx_softc; /* SMP lock for xx_softc */ }xx_softc[NNONE]; [2]
.
.
.
xxcattach(ctlr) struct controller *ctlr; { register struct xx_softc *sc = &xx_softc[ctlr->ctlr_num]; /* Tasks to perform controller-specific initialization */
.
.
.
simple_lock_init(&sc->lk_xx_softc); [3]
.
.
.
/* Perform any other controller-specific initialization tasks */ }

  1. Includes the header file /usr/sys/include/kern/lock.h. The lock.h file defines the simple spin lock and complex lock structures that device drivers use for synchronization on uniprocessor and multiprocessor systems. [Return to example]

  2. Declares an array of softc structures and calls it xx_softc. The /dev/xx driver declares a simple lock structure as a member of the xx_softc structure to protect the integrity of the data stored in the sc_openf and sc_count members. A device driver's softc structure is one resource that often requires protection in an SMP environment because device driver interfaces use it to share data. It is possible that more than one kernel thread might need to access the members of an xx_softc structure. [Return to example]

  3. Calls the simple_lock_init interface to initialize the simple lock structure called lk_xx_softc.

    The simple_lock_init interface takes one argument: a pointer to a simple lock structure. You can declare this simple lock structure by using the decl_simple_lock_data interface. In this call, the xxcattach interface passes the address of the lk_xx_softc member of the xx_softc structure pointer. You need to initialize the simple lock structure only once. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


3.3    Asserting Exclusive Access on a Resource

After declaring and initializing the simple lock structure, you can assert exclusive access by calling the simple_lock interface. The following code fragment shows a call to simple_lock by the /dev/xx device driver's xxopen interface. The xxopen interface is called as the result of an open system call.

The xxopen interface performs the following tasks:

The code fragment also shows the declaration of the simple lock structure in the xx_softc structure and the initialization of the simple lock structure by the driver's xxcattach interface. See Section 3.2 for explanations of these tasks.


.
.
.
#include <kern/lock.h>
.
.
.
struct xx_softc { int sc_openf; /* Open flag */ int sc_count; /* Count of characters written to device */ simple_lock_data_t lk_xx_softc; /* SMP lock for xx_softc */ }xx_softc[NXX];
.
.
.
xxcattach(ctlr) struct controller *ctlr; { register struct xx_softc *sc = &xx_softc[ctlr->ctlr_num]; /* Tasks to perform controller-specific initialization */
.
.
.
simple_lock_init(&sc->lk_xx_softc);
.
.
.
}
.
.
.
xxopen(dev, flag, format) dev_t dev; int flag; int format; register int unit = minor(dev); struct controller *ctlr = xxinfo[unit]; struct xx_softc *sc = &xx_softc[unit]; if(unit >= NXX) return ENODEV; [1] simple_lock(&sc->lk_xx_softc); [2] if (sc->sc_openf == DN_OPEN) {
.
.
.
}

  1. If the number of device units on the system is greater than NXX, returns the error code ENODEV, which indicates that no such device exists on the system. This example test is used to ensure that a valid device exists. [Return to example]

  2. Calls the simple_lock interface to assert an exclusive access on the following code block.

    The simple_lock interface takes one argument: a pointer to a simple lock structure. You can declare this simple lock structure by using the decl_simple_lock_data interface. In this call, the xxopen interface passes the address of the lk_xx_softc member of the xx_softc structure pointer.

    Figure 3-1 shows what happens when two instances of the /dev/xx driver execute on two CPUs. As the figure shows, the kernel thread emanating from CPU1 obtains the simple lock on the code block that follows item 2 in the code fragment before the kernel thread emanating from CPU2. The reason for locking this code block is to prevent data corruption of any future writes to the xx_softc structure. The CPU2 kernel thread spins while waiting for the CPU1 kernel thread to free the simple lock. [Return to example]

Figure 3-1: Two Instances of the /dev/xx Device Driver Asserting an Exclusive Lock


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


3.4    Releasing a Previously Asserted Simple Lock

After asserting a simple lock (with exclusive access), you must release the lock by calling the simple_unlock interface. The following code fragment shows calls to simple_unlock by the /dev/xx device driver's xxopen interface. The xxopen interface is called as the result of an open system call.

The xxopen interface performs the following tasks:

The code fragment also shows the declaration of the simple lock structure in the xx_softc structure and the initialization of the simple lock structure by the driver's xxcattach interface.


.
.
.
#include <kern/lock.h>
.
.
.
struct xx_softc { int sc_openf; /* Open flag */ int sc_count; /* Count of characters written to device */ simple_lock_data_t lk_xx_softc; /* SMP lock for xx_softc */ }xx_softc[NXX];
.
.
.
xxcattach(ctlr) struct controller *ctlr; { register struct xx_softc *sc = &xx_softc[ctlr->ctlr_num]; /* Tasks to perform controller-specific initialization */
.
.
.
simple_lock_init(&sc->lk_xx_softc);
.
.
.
}
.
.
.
xxopen(dev, flag, format) dev_t dev; int flag; int format; register int unit = minor(dev); struct controller *ctlr = xxinfo[unit]; struct xx_softc *sc = &xx_softc[unit]; if(unit >= NXX) return ENODEV; [1] simple_lock(&sc->lk_xx_softc); [2] if (sc->sc_openf == DN_OPEN) [3] { simple_unlock(&sc->lk_xx_softc); return (EBUSY); } if ((ctlr !=0) && (ctlr->alive & ALV_ALIVE)) [4] { sc->sc_openf = DN_OPEN; simple_unlock(&sc->lk_xx_softc); return(0); } else [5] { simple_unlock(&sc->lk_xx_softc); return(ENXIO); } }
.
.
.

  1. If the number of device units on the system is greater than NXX, returns the error code ENODEV, which indicates that no such device exists on the system. This example test is used to ensure that a valid device exists. [Return to example]

  2. Calls the simple_lock interface to assert an exclusive access on the following code block.

    The simple_lock interface takes one argument: a pointer to a simple lock structure. You can declare this simple lock structure by using the decl_simple_lock_data interface. In this call, the xxopen interface passes the address of the lk_xx_softc member of the xx_softc structure pointer. [Return to example]

  3. If the sc_openf member of the sc pointer is equal to DN_OPEN, calls the simple_unlock interface and returns the error code EBUSY, which indicates that the NONE device has already been opened. This example test is used to ensure that this unit of the driver can be opened only once at a time. This type of open is referred to as an exclusive access open.

    The simple_unlock interface releases a simple lock for the resource associated with the specified simple lock structure pointer. This simple lock was previously asserted by calling the simple_lock or simple_lock_try interface. In this call, the locked resource is referenced in the code block beginning with item 3.

    The simple_unlock interface takes one argument: a pointer to a simple lock structure. You can declare this simple lock structure by using the decl_simple_lock_data interface. In this call, the xxopen interface passes the address of the lk_xx_softc member of the xx_softc structure pointer. [Return to example]

  4. If the ctlr pointer is not equal to zero (0) and the alive member of ctlr has the ALV_ALIVE bit set, then the device exists. If this is the case, the xxopen interface sets the sc_openf member of the sc pointer to the open bit DN_OPEN, calls simple_unlock to free the lock, and returns the value zero (0) to indicate a successful open. [Return to example]

  5. If the device does not exist, xxopen calls simple_unlock to free the lock and returns the error code ENXIO, which indicates that the device does not exist.

    Figure 3-2 shows what happens when one instance of the /dev/xx driver releases a previously asserted exclusive lock on the code block that opens the device. As the figure shows, the CPU1 kernel thread releases the simple lock on the code block that opens the device. The CPU2 kernel thread, which was spinning while waiting for the simple lock to be freed, now obtains the simple lock. Furthermore, the figure shows that the CPU1 kernel thread makes another attempt to lock the code block that opens the device. This time it spins while waiting for the CPU2 kernel thread to free the simple lock. [Return to example]

Figure 3-2: One Instance of the /dev/xx Device Driver Releasing an Exclusive Lock


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


3.5    Trying to Obtain a Simple Lock

In addition to explicitly asserting a simple lock, you can also try to assert the simple lock by calling the simple_lock_try interface. The main difference between simple_lock and simple_lock_try is that simple_lock_try returns immediately if the resource is already locked while simple_lock spins until the lock has been obtained. Thus, call simple_lock_try when you need a simple lock but the code cannot spin until the lock is obtained.

The following code fragment shows a call to simple_lock_try by the /dev/xx device driver's xxopen interface. The xxopen interface is called as the result of an open system call.

The xxopen interface performs the following tasks:

The code fragment also shows the declaration of the simple lock structure in the xx_softc structure and the initialization of the simple lock structure by the driver's xxcattach interface.


.
.
.
#include <kern/lock.h>
.
.
.
struct xx_softc { int sc_openf; /* Open flag */ int sc_count; /* Count of characters written to device */ simple_lock_data_t lk_xx_softc; /* SMP lock for xx_softc */ }xx_softc[NXX];
.
.
.
xxcattach(ctlr) struct controller *ctlr; { register struct xx_softc *sc = &xx_softc[ctlr->ctlr_num]; /* Tasks to perform controller-specific initialization */
.
.
.
simple_lock_init(&sc->lk_xx_softc);
.
.
.
}
.
.
.
xxopen(dev, flag, format) dev_t dev; int flag; int format; register int unit = minor(dev); struct controller *ctlr = xxinfo[unit]; struct xx_softc *sc = &xx_softc[unit]; boolean_t try_ret_val; [1] if(unit >= NXX) return ENODEV; [2] try_ret_val = simple_lock_try(&sc->lk_xx_softc); [3] if (try_ret_val == TRUE) [4] { if (sc->sc_openf == DN_OPEN)
.
.
.
else /* Perform some other tasks if simple_lock_try fails * * to assert an exclusive access */
.
.
.
}

  1. Declares a variable to store the return value from the simple_lock_try interface. The simple_lock_try interface returns one of the following values:
    Value Meaning
    TRUE The simple_lock_try interface successfully asserted the simple lock.
    FALSE The simple_lock_try interface failed to assert the simple lock.

    [Return to example]

  2. If the number of device units on the system is greater than NXX, returns the error code ENODEV, which indicates that no such device exists on the system. This example test is used to ensure that a valid device exists. [Return to example]

  3. Calls the simple_lock_try interface to try to assert an exclusive access on the following code block.

    The simple_lock_try interface takes one argument: a pointer to a simple lock structure. You can declare this simple lock structure by using the decl_simple_lock_data interface. In this call, the xxopen interface passes the address of the lk_xx_softc member of the xx_softc structure pointer. [Return to example]

  4. If the return from simple_lock_try is TRUE, checks the sc_openf member to determine if this is a unique open. Otherwise, if the return from simple_lock_try is FALSE, performs some other tasks.

    Figure 3-3 shows what happens when two instances of the /dev/xx driver try to assert an exclusive lock on the code block that opens the device. As the figure shows, the CPU1 and CPU2 kernel threads try to assert an exclusive lock on the code block that opens the device. In this case, the CPU1 kernel thread successfully obtains the lock. To indicate this success, simple_lock_try returns the value TRUE. At the same time, the CPU2 kernel thread fails to obtain the lock and simple_lock_try immediately returns the value FALSE to indicate this. [Return to example]

Figure 3-3: The /dev/xx Device Driver Trying to Assert an Exclusive Lock


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


3.6    Terminating a Simple Lock

After unlocking a simple lock (with exclusive access) and knowing that you are finished using the lock for this resource, you can terminate the lock by calling the simple_lock_terminate interface. Typically, you terminate any locks in the driver's controller (or device) unattach interface. These interfaces are associated with loadable drivers. One task associated with a controller or device unattach interface is to terminate any locks initialized in the driver's unattach interface.

The following code fragment shows a call to simple_lock_terminate by the /dev/xx device driver's xx_ctlr_unattach interface. The code fragment also shows the declaration of the simple lock structure in the xx_softc structure, the initialization of the simple lock structure by the driver's xxcattach interface, and the unlocking of the simple lock structure by the driver's xxopen interface.


.
.
.
#include <kern/lock.h> [1]
.
.
.
struct xx_softc { int sc_openf; /* Open flag */ int sc_count; /* Count of characters written to device */ simple_lock_data_t lk_xx_softc; /* SMP lock for xx_softc */ }xx_softc[NXX]; [2]
.
.
.
xxcattach(ctlr) struct controller *ctlr; { register struct xx_softc *sc = &xx_softc[ctlr->ctlr_num]; /* Tasks to perform controller-specific initialization */
.
.
.
simple_lock_init(&sc->lk_xx_softc); [3]
.
.
.
}
.
.
.
xxopen(dev, flag, format) dev_t dev; int flag; int format; register int unit = minor(dev); struct controller *ctlr = xxinfo[unit]; struct xx_softc *sc = &xx_softc[unit]; if(unit >= NXX) return ENODEV; [4] simple_lock(&sc->lk_xx_softc); [5] if (sc->sc_openf == DN_OPEN) [6] { simple_unlock(&sc->lk_xx_softc); return (EBUSY); } if ((ctlr !=0) && (ctlr->alive & ALV_ALIVE)) [7] { sc->sc_openf = DN_OPEN; simple_unlock(&sc->lk_xx_softc); return(0); } else [8] { simple_unlock(&sc->lk_xx_softc); return(ENXIO); } }
.
.
.
xx_ctlr_unattach(bus, ctlr) struct bus *bus; struct controller *ctlr; { register int unit = ctlr->ctlr_num; if ((unit > num_xx) || (unit < 0) { return(1); } if (xx_is_dynamic == 0) { return(1); } /* Performs controller unattach tasks */
.
.
.
simple_lock_terminate(&sc->lk_xx_softc); [9]

  1. Includes the header file /usr/sys/include/kern/lock.h. The lock.h file defines the simple spin lock and complex lock structures that device drivers use for synchronization on uniprocessor and multiprocessor systems. [Return to example]

  2. Declares an array of softc structures and calls it xx_softc. The /dev/xx driver declares a simple lock structure as a member of the xx_softc structure to protect the integrity of the data stored in the sc_openf and sc_count members. A device driver's softc structure is one resource that often requires protection in an SMP environment because device driver interfaces use it to share data. It is possible that more than one kernel thread might need to access the members of an xx_softc structure. [Return to example]

  3. Calls the simple_lock_init interface to initialize the simple lock structure called lk_xx_softc.

    The simple_lock_init interface takes one argument: a pointer to a simple lock structure. You can declare this simple lock structure by using the decl_simple_lock_data interface. In this call, the xxcattach interface passes the address of the lk_xx_softc member of the xx_softc structure pointer. You need to initialize the simple lock structure only once. After initializing a simple lock structure, device drivers can call simple_lock to assert exclusive access on the associated resource or simple_lock_try to attempt to assert exclusive access on the associated resource. [Return to example]

  4. If the number of device units on the system is greater than NXX, returns the error code ENODEV, which indicates that no such device exists on the system. This example test is used to ensure that a valid device exists. [Return to example]

  5. Calls the simple_lock interface to assert an exclusive access on the following code block.

    The simple_lock interface takes one argument: a pointer to a simple lock structure. You can declare this simple lock structure by using the decl_simple_lock_data interface. In this call, the xxopen interface passes the address of the lk_xx_softc member of the xx_softc structure pointer. [Return to example]

  6. If the sc_openf member of the sc pointer is equal to DN_OPEN, calls the simple_unlock interface and returns the error code EBUSY, which indicates that the NONE device has already been opened. This example test is used to ensure that this unit of the driver can be opened only once at a time. This type of open is referred to as an exclusive access open.

    The simple_unlock interface releases a simple lock for the resource associated with the specified simple lock structure pointer. This simple lock was previously asserted by calling the simple_lock or simple_lock_try interface. In this call, the locked resource is referenced in the code block beginning with item 6.

    The simple_unlock interface takes one argument: a pointer to a simple lock structure. You can declare this simple lock structure by using the decl_simple_lock_data interface. In this call, the xxopen interface passes the address of the lk_xx_softc member of the xx_softc structure pointer. [Return to example]

  7. If the ctlr pointer is not equal to zero (0) and the alive member of ctlr has the ALV_ALIVE bit set, then the device exists. If this is the case, the xxopen interface sets the sc_openf member of the sc pointer to the open bit DN_OPEN, calls simple_unlock to free the lock, and returns the value zero (0) to indicate a successful open. [Return to example]

  8. If the device does not exist, xxopen calls simple_unlock to free the lock and returns the error code ENXIO, which indicates that the device does not exist. [Return to example]

  9. Calls the simple_lock_terminate interface to determine that the /dev/xx driver is permanently done using this simple lock.

    The simple_lock_terminate interface takes one argument: a pointer to a simple lock structure. You can declare this simple lock structure by using the decl_simple_lock_data interface. In this call, the xx_ctlr_unattach interface passes the address of the lk_xx_softc member of the xx_softc structure pointer. In calling simple_lock_terminate, the /dev/xx driver must not reference this simple lock again. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Chapter] [Index] [Help]


3.7    Using the spl Interfaces with Simple Locks

The spl interfaces block out asynchronous events on the CPU that the spl call is performed on. Simple locks block out other CPUs. You need to use both the spl interfaces and the simple lock interfaces when synchronizing with kernel threads and interrupt service interfaces. The following code fragment shows calls to the spl and simple lock interfaces:


.
.
.
#include <kern/lock.h> [1]
.
.
.
struct tty_softc {
.
.
.
decl_simple_lock_data( , lk_tty_softc); /* SMP lock for tty_softc */
.
.
.
}tty_softc[NSOMEDEVICE]; [2]
.
.
.
simple_lock_init(&sc->lk_tty_softc);
.
.
.
s = spltty(); [3] simple_lock(&lk_tty_softc); [4]
.
.
.
/* Manipulate resource */
.
.
.

simple_unlock(&lk_tty_softc); [5] splx(s); [6]
.
.
.

  1. Includes the header file /usr/sys/include/kern/lock.h. The lock.h file defines the simple spin lock and complex lock structures that device drivers use for synchronization on single processor and multiprocessor systems. [Return to example]

  2. Declares an array of softc structures and calls it lk_tty_softc. This example driver uses the decl_simple_lock_data interface to declare a simple lock structure as a member of the tty_softc structure. [Return to example]

  3. Calls the spltty interface to mask out all tty (terminal device) interrupts. The spltty takes no arguments. The spltty interface returns an integer value that represents the CPU priority level that existed before the call. Note that the interface masks out all tty interrupts on the CPU on which it is called. [Return to example]

  4. Calls the simple_lock interface to assert a lock with exclusive access for the resource associated with the slock structure pointer, which in this example is lk_tty_softc. Note that the interface ensures that no other kernel thread running on other CPUs can gain access to this resource. This contrasts with the spl interfaces, which block out kernel threads running on this CPU. [Return to example]

  5. After manipulating the resource, calls simple_unlock to release the simple lock. This makes the resource available to kernel threads running on other CPUs. [Return to example]

  6. Calls the splx interface to reset the CPU priority to the level specified by the value returned by spltty.

    The splx interface takes one argument: a CPU priority level. This level must be a value returned by a previous call to one of the spl interfaces, in this example spltty. Calling splx releases the priority on this CPU. [Return to example]