After you decide that the complex lock method is the appropriate method for locking specific resources, you use the complex lock interfaces to accomplish the locking. To use complex 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 if_fta that operates on some FTA device.
Before using a complex lock, declare a complex lock structure for the resource you want to lock. The following code fragment shows how to declare a complex lock structure for a specific member of the fta_softc structure:
.
.
.
#include <kern/lock.h> [1]
.
.
.
struct cmd_buf { u_long *req_buf; u_long *rsp_buf; short timeout; struct cmd_buf *next; }; [2]
.
.
.
struct fta_softc {
.
.
.
struct cmd_buf *q_first; /* first in the request queue */ struct cmd_buf *q_last; /* last in the request queue */ lock_data_t cmd_buf_q_lock; /* lock for the command */ /* request queue */
.
.
.
}; [3]
.
.
.
Specifies a pointer to a cmd_buf data structure. This member represents the first command queue in the linked list.
Specifies a pointer to a cmd_buf data structure. This member represents the last command queue in the linked list.
Declares a lock structure called cmd_buf_q_lock. The purpose of this lock is to protect the integrity of the data stored in the linked list of cmd_buf data structures. Note that the alternate name lock_data_t is used to declare the complex lock structure. Embedding the complex lock in the fta_softc structure protects the cmd_buf structures for any number of instances.
After declaring the complex lock structure, you initialize it by calling the lock_init interface. The following code fragment shows a call to lock_init by the if_fta device driver's ftaattach interface. The ftaattach 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 ftaattach interface initializes the complex lock structure cmd_buf_q_lock.
The code fragment also shows the include file associated with complex locks, definitions of the cmd_buf and fta_softc structures, and the declaration of the complex lock.
.
.
.
#include <kern/lock.h> [1]
.
.
.
struct cmd_buf { u_long *req_buf; u_long *rsp_buf; short timeout; struct cmd_buf *next; }; [2]
.
.
.
struct fta_softc {
.
.
.
struct cmd_buf *q_first; /* first in the request queue */ struct cmd_buf *q_last; /* last in the request queue */ lock_data_t cmd_buf_q_lock; /* lock for the command */ /* request queue */
.
.
.
}; [3]
.
.
.
ftaattach(ctlr) struct controller *ctlr; { struct fta_softc *sc = &fta_softc[ctlr->ctlr_num];
.
.
.
/* Tasks to perform controller-specific initialization */
.
.
.
lock_init(&sc->cmd_buf_q_lock, TRUE); [4]
.
.
.
/* Perform other tasks */ }
Specifies a pointer to a cmd_buf data structure. This member represents the first command queue in the linked list.
Specifies a pointer to a cmd_buf data structure. This member represents the last command queue in the linked list.
Declares a lock structure called cmd_buf_q_lock. The purpose of this lock is to protect the integrity of the data stored in the linked list of cmd_buf data structures. Note that the alternate name lock_data_t is used to declare the complex lock structure. Embedding the complex lock in the fta_softc structure protects the cmd_buf structures for any number of instances.
The lock_init interface takes two arguments:
After declaring and initializing the complex lock structure, you can perform the following access operations on the complex lock:
Each of these tasks is discussed in the following sections.
After declaring and initializing the complex lock structure, you can assert a complex lock with read-only access or a complex lock with write access by calling the lock_read or lock_write interface. The following sections describe how to use these interfaces.
The lock_read interface asserts a lock with read-only access for the resource associated with the specified lock structure pointer. The following code fragment shows a call to lock_read by the if_fta device driver's ftaioctl interface. The ftaioctl interface is called as the result of an ioctl system call.
The ftaioctl interface performs the following tasks:
The code fragment also shows the include file associated with complex locks, definitions of the cmd_buf and fta_softc structures, the declaration of the complex lock structure in the fta_softc structure, and the initialization of the complex lock structure by the driver's ftaattach interface. Section 4.2 provides descriptions of these tasks.
.
.
.
#include <kern/lock.h>
.
.
.
struct cmd_buf { u_long *req_buf; u_long *rsp_buf; short timeout; struct cmd_buf *next; };
.
.
.
struct fta_softc {
.
.
.
struct cmd_buf *q_first; /* first in the request queue */ struct cmd_buf *q_last; /* last in the request queue */ lock_data_t cmd_buf_q_lock; /* lock for the command */ /* request queue */
.
.
.
};
.
.
.
ftaattach(ctlr) struct controller *ctlr; { struct fta_softc *sc = &fta_softc[ctlr->ctlr_num];
.
.
.
/* Tasks to perform controller-specific initialization */
.
.
.
lock_init(&sc->cmd_buf_q_lock, TRUE);
.
.
.
/* Perform other tasks */ }
.
.
.
ftaioctl(ifp, cmd, data) register struct ifnet *ifp; unsigned int cmd; caddr_t data; { struct fta_softc *sc = &fta_softc[ifp->if_unit];
.
.
.
switch (cmd) { case SIOCENABLBACK: {
.
.
.
if (ifp->if_flags & IFF_RUNNING) { [1] lock_read(&sc->cmd_buf_q_lock); /* Performs read operation on the resource */ if(sc->q_first->req_buf = (u_long*)(data);
.
.
.
The lock_read interface takes one argument: a pointer to the complex lock structure, lock. This is the lock structure associated with the resource on which you want to assert a complex lock with read-only access. The ftaioctl interface passes the address of the cmd_buf_q_lock member of the fta_softc structure pointer.
Figure 4-1 shows what happens when multiple instances of the if_fta driver assert a read-only complex lock on the specified code block. As the figure shows, kernel threads from the if_fta driver executing on CPU1, CPU2, and CPU3 assert read-only complex locks on the specified code block. The lock_read interface allows multiple kernel threads to access the resource read-only at the same time. When a read lock is asserted, the protected resource is guaranteed not to change. In this case, the cmd_buf resource is guaranteed not to change. [Return to example]
The lock_write interface asserts a lock with exclusive write access for the resource associated with the specified lock structure pointer. This means that once a write lock is asserted, no other kernel thread can gain read or write access to the resource until it is released.
The following code fragment shows a call to lock_write by the if_fta device driver's ftaioctl interface. The ftaioctl interface is called as the result of an ioctl system call.
The ftaioctl interface performs the following tasks:
The code fragment also shows the include file associated with complex locks, definitions of the cmd_buf and fta_softc structures, the declaration of the complex lock structure in the fta_softc structure, and the initialization of the complex lock structure by the driver's ftaattach interface. Section 4.2 provides descriptions of these tasks.
.
.
.
#include <kern/lock.h>
.
.
.
struct cmd_buf { u_long *req_buf; u_long *rsp_buf; short timeout; struct cmd_buf *next; };
.
.
.
struct fta_softc {
.
.
.
struct cmd_buf *q_first; /* first in the request queue */ struct cmd_buf *q_last; /* last in the request queue */ lock_data_t cmd_buf_q_lock; /* lock for the command /* /* request queue */
.
.
.
};
.
.
.
ftaattach(ctlr) struct controller *ctlr; { struct fta_softc *sc = &fta_softc[ctlr->ctlr_num];
.
.
.
/* Tasks to perform controller-specific initialization */
.
.
.
lock_init(&sc->cmd_buf_q_lock, TRUE);
.
.
.
/* Perform other tasks */ }
.
.
.
ftaioctl(ifp, cmd, data) register struct ifnet *ifp; unsigned int cmd; caddr_t data; { struct fta_softc *sc = &fta_softc[ifp->if_unit];
.
.
.
switch (cmd) { case SIOCENABLBACK: {
.
.
.
if (ifp->if_flags & IFF_RUNNING) { [1] lock_write(&sc->cmd_buf_q_lock); sc->q_first->req_buf = (u_long*) (data);
.
.
.
The lock_write interface takes one argument: a pointer to the complex lock structure, lock. This is the lock structure associated with the resource on which you want to assert a complex lock with write access. The ftaioctl interface passes the address of the cmd_buf_q_lock member of the fta_softc structure pointer.
Figure 4-2 shows what happens when multiple instances of the if_fta driver assert a write complex lock on the specified code block. As the figure shows, kernel threads from the if_fta driver executing on CPU1, CPU2, and CPU3 assert write complex locks on the specified code block. The kernel thread emanating from CPU3 asserts the write complex lock before the kernel threads emanating from CPU1 and CPU2. The kernel thread emanating from CPU3 writes to the req_buf member.
The lock_write interface blocks (puts to sleep) the kernel threads emanating from CPU1 and CPU2 by placing the requests on a lock queue. This shows that once lock_write successfully asserts a complex write lock, no other kernel thread can gain read or write access to the resource until the resource is released. [Return to example]
After you finish manipulating the resource associated with the complex lock, you need to release the lock. To release a complex lock that you previously asserted with a call to lock_read or lock_write, call the lock_done interface. The following code fragment shows a call to lock_done by the if_fta driver's ftaioctl interface. The ftaioctl interface is called as the result of an ioctl system call.
The ftaioctl interface performs the following tasks:
The code fragment also shows the include file associated with complex locks, definitions of the cmd_buf and fta_softc structures, the declaration of the complex lock structure in the fta_softc structure, the initialization of the complex lock structure by the driver's ftaattach interface, and the assertion of a complex write lock on the code block by the driver's ftaioctl interface. Section 4.2 and Section 4.3.1.2 provide descriptions of these tasks.
.
.
.
#include <kern/lock.h>
.
.
.
struct cmd_buf { u_long *req_buf; u_long *rsp_buf; short timeout; struct cmd_buf *next; };
.
.
.
struct fta_softc {
.
.
.
struct cmd_buf *q_first; /* first in the request queue */ struct cmd_buf *q_last; /* last in the request queue */ lock_data_t cmd_buf_q_lock; /* lock for the command */ /* request queue */
.
.
.
};
.
.
.
ftaattach(ctlr) struct controller *ctlr; { struct fta_softc *sc = &fta_softc[ctlr->ctlr_num];
.
.
.
/* Tasks to perform controller-specific initialization */
.
.
.
lock_init(&sc->cmd_buf_q_lock, TRUE);
.
.
.
/* Perform other tasks */ }
.
.
.
ftaioctl(ifp, cmd, data) register struct ifnet *ifp; unsigned int cmd; caddr_t data; { struct fta_softc *sc = &fta_softc[ifp->if_unit];
.
.
.
switch (cmd) { case SIOCENABLBACK: {
.
.
.
if (ifp->if_flags & IFF_RUNNING) { lock_write(&sc->cmd_buf_q_lock); sc->q_first->req_buf = (u_long*) (data);
.
.
.
lock_done(&sc->cmd_buf_q_lock); [1]
.
.
.
The lock_done interface takes one argument: a pointer to the complex lock structure, lock. This is the lock structure associated with the resource on which you want to assert a complex lock with write access. The ftaioctl interface passes the address of the cmd_buf_q_lock member of the fta_softc structure pointer.
Figure 4-3 shows what happens when one instance of the if_fta driver releases a previously asserted complex write lock on the code block that writes to the command buffer queue. As the figure shows, the CPU3 kernel thread releases the complex write lock on the code block that writes to the command buffer queue. The CPU1 and CPU2 kernel threads are blocked, waiting on the wait queue for the complex write lock to be freed. Because the CPU1 kernel thread is first on the wait queue, it now obtains the complex write lock. Furthermore, the figure shows that the CPU3 kernel thread makes another attempt to assert a complex write lock on the code block. This time lock_write blocks (puts to sleep) the CPU3 kernel thread by placing it on the wait queue behind the CPU2 kernel thread. [Return to example]
After declaring and initializing the complex lock structure, you can try to assert a complex lock with read-only access or a complex lock with write access by calling the lock_try_read or lock_try_write interface. Unlike the lock_read or lock_write interfaces, the lock_try_read and lock_try_write interfaces do not block if the lock associated with the resource is owned by another kernel thread.
The following sections describe how to use these interfaces.
To try to assert a complex lock with read-only access, call the lock_try_read interface. The lock_try_read interface tries to assert a complex lock (without blocking) with read-only access for the resource associated with the specified lock structure pointer.
The following code fragment shows a call to lock_try_read by the if_fta driver's ftaioctl interface. The code fragment also shows the include file associated with complex locks, definitions of the cmd_buf and fta_softc structures, the declaration of the complex lock structure in the fta_softc structure, and the initialization of the complex lock structure by the driver's ftaattach interface. Section 4.2 provides descriptions of these tasks. In addition, the code fragment shows a call to lock_done if the complex read-only lock is successfully asserted.
.
.
.
#include <kern/lock.h>
.
.
.
struct cmd_buf { u_long *req_buf; u_long *rsp_buf; short timeout; struct cmd_buf *next; };
.
.
.
struct fta_softc {
.
.
.
struct cmd_buf *q_first; /* first in the request queue */ struct cmd_buf *q_last; /* last in the request queue */ lock_data_t cmd_buf_q_lock; /* lock for the command */ /* request queue */
.
.
.
};
.
.
.
ftaattach(ctlr) struct controller *ctlr; { struct fta_softc *sc = &fta_softc[ctlr->ctlr_num];
.
.
.
/* Tasks to perform controller-specific initialization */
.
.
.
lock_init(&sc->cmd_buf_q_lock, TRUE);
.
.
.
/* Perform other tasks */ }
.
.
.
ftaioctl(ifp, cmd, data) register struct ifnet *ifp; unsigned int cmd; caddr_t data; { struct fta_softc *sc = &fta_softc[ifp->if_unit]; boolean_t try_ret_val; [1]
.
.
.
switch (cmd) { case SIOCENABLBACK: {
.
.
.
if (ifp->if_flags & IFF_RUNNING) { [2] try_ret_val = lock_try_read(&sc->cmd_buf_q_lock); if (try_ret_val == TRUE) { [3] if (sc->q_first->req_buf == (u_long*) (data)) {
.
.
.
lock_done(&sc->cmd_buf_q_lock); [4] } } }
.
.
.
else [5]
.
.
.
/* Code that executes when try_ret_val == FALSE */
.
.
.
}
.
.
.
}
.
.
.
}
Value | Meaning |
TRUE | The attempt to acquire the read-only complex lock was successful. |
FALSE | The attempt to acquire the read-only complex lock was unsuccessful. |
The lock_try_read interface takes one argument: a pointer to the complex lock structure, lock. This is the lock structure associated with the resource on which you want to try to assert a complex lock with read-only access. The ftaioctl interface passes the address of the cmd_buf_q_lock member of the fta_softc structure pointer. [Return to example]
Figure 4-4 shows what happens when two instances of the if_fta driver attempt to assert a read-only complex lock on the code block that performs a read operation on the resource. As the figure shows, both the CPU1 and CPU2 kernel threads try to assert a read-only complex lock on the code block that performs a read operation on the command buffer queue. Because this is a read-only operation, the CPU1 and CPU2 kernel threads obtain the read-only complex lock and as a result lock_try_read returns the value TRUE in both cases. [Return to example]
To try to assert a complex lock with write access, call the lock_try_write interface. The lock_try_write interface tries to assert a complex lock (without blocking) with write access for the resource associated with the specified lock structure pointer.
The following code fragment shows a call to lock_try_write by the if_fta driver's ftaioctl interface. The code fragment also shows the include file associated with complex locks, definitions of the cmd_buf and fta_softc structures, the declaration of the complex lock structure in the fta_softc structure, and the initialization of the complex lock structure by the driver's ftaattach interface. Section 4.2 provides descriptions of these tasks. In addition, the code fragment shows a call to lock_done if the complex write lock is successfully asserted.
.
.
.
#include <kern/lock.h>
.
.
.
struct cmd_buf { u_long *req_buf; u_long *rsp_buf; short timeout; struct cmd_buf *next; };
.
.
.
struct fta_softc {
.
.
.
struct cmd_buf *q_first; /* first in the request queue */ struct cmd_buf *q_last; /* last in the request queue */ lock_data_t cmd_buf_q_lock; /* lock for the command */ /* request queue */
.
.
.
};
.
.
.
ftaattach(ctlr) struct controller *ctlr; { struct fta_softc *sc = &fta_softc[ctlr->ctlr_num];
.
.
.
/* Tasks to perform controller-specific initialization */
.
.
.
lock_init(&sc->cmd_buf_q_lock, TRUE);
.
.
.
/* Perform other tasks */ }
.
.
.
ftaioctl(ifp, cmd, data) register struct ifnet *ifp; unsigned int cmd; caddr_t data; { struct fta_softc *sc = &fta_softc[ifp->if_unit]; boolean_t try_ret_val; [1]
.
.
.
switch (cmd) { case SIOCENABLBACK: {
.
.
.
if (ifp->if_flags & IFF_RUNNING) { [2] try_ret_val = lock_try_write(&sc->cmd_buf_q_lock); if (try_ret_val == TRUE) { [3] sc->q_first->req_buf = (u_long*) (data);
.
.
.
lock_done(&sc->cmd_buf_q_lock); [4] }
.
.
.
else [5]
.
.
.
/* Code that executes when try_ret_val == FALSE */
.
.
.
}
.
.
.
}
.
.
.
}
Value | Meaning |
TRUE | The attempt to acquire the write complex lock was successful. |
FALSE | The attempt to acquire the write complex lock was unsuccessful. |
The lock_try_write interface takes one argument: a pointer to the complex lock structure, lock. This is the lock structure associated with the resource on which you want to try to assert write access. The ftaioctl interface passes the address of the cmd_buf_q_lock member of the fta_softc structure pointer. [Return to example]
Figure 4-5 shows what happens when two instances of the if_fta driver attempt to assert a write complex lock on the code block that performs a write operation on the resource. As the figure shows, both the CPU1 and CPU2 kernel threads try to assert a write complex lock on the code block that performs a write operation on the command buffer queue. The CPU1 kernel thread obtains the write complex lock first and as a result lock_try_write returns the value TRUE. Because the CPU2 kernel thread was not successful in obtaining the write complex lock, lock_try_write immediately returns (does not block the kernel thread) the value FALSE. [Return to example]
After unlocking a complex read or write lock and knowing that you are finished using the lock for this resource, you can terminate the lock by calling the 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 attach interface.
The following code fragment shows a call to lock_terminate by the if_fta device driver's fta_ctlr_unattach interface. The code fragment also shows the include file associated with complex locks, definitions of the cmd_buf and fta_softc structures, the declaration of the complex lock structure in the fta_softc structure, and the initialization of the complex lock structure by the driver's ftaattach interface. Section 4.2 provides descriptions of these tasks. In addition, the code fragment shows calls to lock_write and lock_done.
.
.
.
#include <kern/lock.h>
.
.
.
struct cmd_buf { u_long *req_buf; u_long *rsp_buf; short timeout; struct cmd_buf *next; };
.
.
.
struct fta_softc {
.
.
.
struct cmd_buf *q_first; /* first in the request queue */ struct cmd_buf *q_last; /* last in the request queue */ lock_data_t cmd_buf_q_lock; /* lock for the command */ /* request queue */
.
.
.
};
.
.
.
ftaattach(ctlr) struct controller *ctlr; { struct fta_softc *sc = &fta_softc[ctlr->ctlr_num];
.
.
.
/* Tasks to perform controller-specific initialization */
.
.
.
lock_init(&sc->cmd_buf_q_lock, TRUE);
.
.
.
/* Perform other tasks */ }
.
.
.
ftaioctl(ifp, cmd, data) register struct ifnet *ifp; unsigned int cmd; caddr_t data; { struct fta_softc *sc = &fta_softc[ifp->if_unit];
.
.
.
switch (cmd) { case SIOCENABLBACK: {
.
.
.
if (ifp->if_flags & IFF_RUNNING) { lock_write(&sc->cmd_buf_q_lock); sc->q_first->req_buf = (u_long*) (data);
.
.
.
lock_done(&sc->cmd_buf_q_lock);
.
.
.
fta_ctlr_unattach(bus, ctlr) struct bus *bus; struct controller *ctlr; { register int unit = ctlr->ctlr_num; if ((unit > num_fta) || (unit < 0) { return(1); } if (fta_is_dynamic == 0) { return(1); } /* Performs controller unattach tasks */
.
.
.
lock_terminate(&sc->cmd_buf_q_lock); [1]
The lock_terminate interface takes one argument: a pointer to the complex lock structure, lock. In this call, the fta_ctlr_unattach interface passes the address of the cmd_buf_q_lock member of the fta_softc structure pointer. In calling lock_terminate, the if_fta driver must not reference this complex lock again. [Return to example]