When modifying a STREAMS driver to take advantage of the multithreaded kernel, a level of MT safety is selected according to:
The desired degree of concurrency
The natural concurrency of the underlying module
The amount of effort or complexity required
Note that much of the effort in conversion is simply determining the appropriate degree of data sharing and the corresponding granularity of locking. The actual time spent configuring perimeters and/or installing locks should be much smaller than the time spent in analysis.
To port your module, you must understand the data structures used within your module, as well as the accesses to those data structures. It is your responsibility to fully understand the relationship between all portions of the module and private data within that module, and to use the MT STREAMS perimeters (or the synchronization primitives available) to maintain the integrity of these private data structures.
You must explicitly restrict access to private module data structures as appropriate to ensure the integrity of these data structures. You must use the MT STREAMS perimeters to restrict the concurrency in the module so that the parts of the module that modify private data are single threaded with respect to the parts of the module that read the same data. Alternatively to the perimeters, you can use the synchronization primitives available (mutex, condition variables, readers/writer, semaphore) to explicitly restrict access to module private data appropriate for the operations within the module on that data.
The first step in multithreading a module or driver is to analyze the module, breaking the entire module up into a list of individual operations and the private data structures referenced in each operation. Part of this first step is deciding upon a level of concurrency for the module. Ask yourself which of these operations can be multithreaded and which must be single threaded. Try to find a level of concurrency that is "natural" for the module and that matches one of the available perimeters (or alternatively, requires the minimal number of locks) and that has a simple and straightforward implementation. Avoid additional complexity.
It is very common to overdo multithreading that results in a very low performance module.
Typical questions to ask are:
What data structures are maintained within the module?
What types of accesses are made to each field of these data structures?
When is each data structure accessed destructively (written) and when is it accessed non-destructively (read)?
Which operations within the module should be allowed to execute concurrently?
Is per module single-threading appropriate for the module?
Is per queue-pair or per queue single-threading appropriate?
What are the message ordering requirements?
Examples of natural levels of concurrency are:
A module, where the put procedures reads as well as modifies module global data, can be configured to be per module single threaded using a per module inner perimeter.
A module, where all the module private data associated with a queue (or a read/write pair of queues) can be configured to be single threaded for each queue (or queue pair) using the corresponding inner perimeter.
A module where most of the module private data is associated with a queue (or a queue pair); but has some module global data that is mostly read; and can be configured with a queue (or queue pair) inner perimeter plus an outer perimeter. The module can use qwriter to protect the sections where it modifies the module's global data.
A module that falls in one of the previous categories, but requires higher concurrency for certain message types while not requiring message ordering, can be configured with the previous perimeters plus shared inner perimeter access for the put procedures. The module can then use qwriter when messages are handled in the put procedures that require exclusive access at the inner and outer perimeter.
A hardware driver can use an appropriate set of inner and outer perimeters to restrict the concurrency in the open, close, put, and service procedures. With explicit synchronization primitives, these drivers restrict the concurrency when accessing the hardware registers in interrupt handlers. Such drivers need to be aware of the issues listed in "MT SAFE Modules Using Explicit Locks".
When porting a STREAMS module or driver from the SunOS 4 system to the SunOS 5 system, the module should be examined with respect to the following areas:
The SunOS5 Device Driver Interface (DDI/DKI)
The SunOS 5 MT design
For portability and correct operation, each module must adhere to the SunOS DDI/DKI. Several facilities available in previous releases of the SunOS system have changed and can take different arguments, or produce different side effects, or no longer exist in the SunOS 5 system. The module writer should carefully review the module with respect to the DDI/DKI.
Each module that accesses underlying Sun-specific features included in the SunOS 5 system should conform to the Device Driver Interface. The SunOS 5 DDI defines the interface used by the device driver to register device hardware interrupts, access device node properties, map device slave memory, and establish and synchronize memory mappings for DVMA (Direct Virtual Memory Access). These areas are primarily applicable to hardware device drivers. Refer to the Device Driver Interface Specification within the Writing Device Drivers for details on the 5 DDI and DVMA.
The kernel networking subsystem in the SunOS 5 system is STREAMS based. Datalink drivers that used the ifnet interface in the SunOS 4 system must be converted to use DLPI for the SunOS 5 system. Refer to the Data Link Provider Interface, Revision 2 specification.
After reviewing the module for conformance to the SunOS 5 DKI and DDI specifications, you should be able to consider the impact of multithreading on the module.