Part I Application Programming Interface
2. STREAMS Application-Level Components
3. STREAMS Application-Level Mechanisms
4. Application Access to the STREAMS Driver and Module Interfaces
7. STREAMS Framework - Kernel Level
8. STREAMS Kernel-Level Mechanisms
11. Configuring STREAMS Drivers and Modules
Multithreaded (MT) STREAMS Overview
Routines Used Inside a Perimeter
Asynchronous Callback Functions
MT SAFE Modules Using Explicit Locks
Sample Multithreaded Device Driver Using a Per Module Inner Perimeter
Sample Multithreaded Module With Outer Perimeter
14. Debugging STREAMS-based Applications
B. Kernel Utility Interface Summary
This section describes the routines and data fields used after you enter a perimeter.
The routines qprocson(9F) and qprocsoff(9F) respectively enable and disable the put and service procedures of the queue pair. Before calling qprocson(9F) and after calling qprocsoff(9F), the module's put and service procedures are disabled; messages flow around the module as if it were not present in the stream.
Call qprocson(9F) in the first open of a module, but only after allocating and initializing any module resources on which the put and service procedures depend. Call the qprocsoff routine in the close routine of the module before deallocating any resources on which the put and service procedures depend.
Note - To avoid deadlocks, modules must not hold private locks across the calls to qprocson(9F) or qprocsoff(9F).
The timeout(9F) and bufcall(9F) callbacks are asynchronous. For a module using MT STREAMS perimeters, the timeout(9F) and bufcall(9F) callback functions execute outside the scope of the perimeters. This makes synchronization of callbacks with the rest of the module complex.
To make timeout(9F) and bufcall(9F) functionality easier to use for modules with perimeters, there are additional interfaces that use synchronous callbacks. These routines are qtimeout(9F), quntimeout(9F), qbufcall(9F), and qunbufcall(9F). When using these routines, the callback functions are executed inside the perimeters, and hence have the same concurrency restrictions as the put and service procedures.
Modules can use the qwriter(9F) function to upgrade from shared to exclusive access at a perimeter. For example, a module with an outer perimeter can use qwriter(9F) in the put procedure to upgrade to exclusive access at the outer perimeter. A module where the put procedure runs with shared access at the inner perimeter (D_MTPUTSHARED) can use qwriter(9F) in the put procedure to upgrade to exclusive access at the inner perimeter.
Returning from a qwriter call does not mean that the callback function has executed. If the framework can become exclusive in the qwriter call, it will enter the perimeter synchronously, and execute the callback. If it cannot, the callback will be deferred. It is a good idea for any caller of qwriter to immediately return to its caller as there is little that can be accomplished in this thread of execution.
Caution - Hardening Information. Do not call qwriter with another queue, as qwriter assumes that the caller has already made a claim to the perimeter that the queue is associated with (asynchronous entry), and calling another perimeter will cause problems. |
Note - qwriter(9F) cannot be used in the open or close procedures. If a module needs exclusive access at the outer perimeter in the open and/or close procedures, it has to specify that the outer perimeter should always be entered exclusively for open and close (using D_MTOCEXCL).
The STREAMS framework guarantees that all deferred qwriter(9F) callbacks associated with a queue have executed before the module's close routine is called for that queue.
For an example of a driver using qwriter(9F) see Example 12-2.
A module that uses perimeters and must wait in its open or close procedure for a message from another STREAMS module has to wait outside the perimeters; otherwise, the message would never be allowed to enter its put and service procedures. This is accomplished by using the qwait(9F) interface. See qwriter(9F) man page for an example. For information about signal reception during a close, see close Routine
Interrupt handlers and other asynchronous callback functions require special care by the module writer, because they can execute asynchronously to threads executing within the module open, close, put, and service procedures.
For modules using perimeters, use qtimeout(9F) and qbufcall(9F) instead of timeout(9F) and bufcall(9F). The qtimeout and qbufcall callbacks are synchronous and consequently introduce no special synchronization requirements.
Because a thread can enter the module at any time, you must ensure that the asynchronous callback function acquires the proper private locks before accessing private module data structures, and releases these locks before returning. You must cancel any outstanding registered callback routines before the data structures on which the callback routines depend are deallocated and the module closed.
For hardware device interrupts, this involves disabling the device interrupts.
Outstanding callbacks from timeout(9F) and bufcall(9F) must be canceled by calling untimeout(9F) and unbufcall(9F).
The module cannot hold certain private locks across calls to untimeout(9F) or unbufcall(9F). These locks are those that the module's timeout(9F) or bufcall(9F) callback functions acquire. See MT SAFE Modules Using Explicit Locks.
If outstanding callbacks from esballoc(9F) are associated with a particular stream, they must be allowed to complete before the module close routine deallocates the private data structures on which they depend.
Because the callback functions are by nature asynchronous, they can be executing or about to execute at the time the module close routine is called. You must cancel all outstanding callback and interrupt conditions before deallocating those data structures or returning from the close routine.
The callback functions scheduled with timeout(9F) and bufcall(9F) are guaranteed to have been canceled by the time untimeout(9F) and unbufcall(9F) return. The same is true for qtimeout(9F) and qbufcall(9F) by the time quntimeout(9F) and qunbufcall(9F) return. You must also take responsibility for other asynchronous routines, including esballoc(9F) callbacks and hardware, as well as software interrupts.
The STREAMS framework prevents a module or driver text from being unloaded while there are open instances of the module or driver. If a module does not cancel all callbacks in the last close routine, it should not be allowed to be unloaded.
This is an issue mainly for modules and drivers using esballoc because esballoc callbacks cannot be canceled. Thus, modules and drivers using esballoc have to be prepared to handle calls to the esballoc callback free function after the last instance of the module or driver has been closed.
Modules and drivers can maintain a semaphore count of outstanding callbacks. They can deny an unload by making the _fini(9E) routine return EBUSY if there are outstanding callbacks.
The q_next field in the queue_t structure can be referenced in open, close, put, and service procedures as well as the synchronous callback procedures (scheduled with qtimeout(9F), qbufcall(9F), and qwriter(9F)). However, the value in the q_next field should not be trusted. It is relevant to the STREAMS framework, but may not be relevant to a specific module.
All other module code, such as interrupt routines, timeout(9F) and esballoc(9F) callback routines, cannot dereference q_next. Those routines have to use the “next” version of all functions. For instance, use canputnext(9F) instead of dereferencing q_next and using canput(9F).