Multithreading device drivers

Registering the task-time level of a block device

Use the bdistributed(D3oddi) function to register the task-time level of a block driver as multithreaded. The initialization routine must also determine whether the hardware configuration allows all processors to access the I/O bus and, if not, allocate an interprocessor interrupt that will be used to access the correct start( ) routine in the driver's strategy( ) routine (see ``Modifying the strategy routine'').

See the idistributed(D3oddi) manual page for examples of how to register the driver as level-two multithreaded.

Multithreaded init routine for block driver

  1 int xxhandle = -1;
  2 xxinit()
  3 {
  4 	...
  5 	if (all_io(MP_ATBUS))
  6 	      bdistributed(xxstrategy, DIST_BDEV_STRATEGY);
  7 	else if (xxhandle == -1) {
  8 		xxhandle = intralloc(&lock_xxtab, xxstart);
  9 	if (xxhandle == -1)
 10 		cmn_err(CE_WARN, "xxinit: Multiprocessor access denied");
 11 		else
 12 			bdistributed(xxstrategy, DIST_BDEV_STRATEGY);
 13 	}
 14 	...
 15 }

line 1
The xxhandle variable describes the stored address of the start( ) routine that will be used by startio(D3oddi). startio is called to run the start( ) routine from the default processor when the current processor cannot access the I/O bus.

line 5
calls all_io(D3oddi) to determine if all the processors can access the AT bus. all_io( ) takes one argument that specifies the bus type; these are defined in the <sys/ci/cidriver.h> header file. The recommended policy is for the driver to pass the most common bus type to all_io( ). For example, if a controller works on both the AT-bus and the EISA bus, the driver should pass MP_ATBUS rather than MP_EISABUS.

all_io( ) returns TRUE if all processors on the system can perform I/O (to or from a device) on the specified bus. TRUE will also be returned on uniprocessor systems.

The driver does not have to know on which type of platform it is running; the kernel support routines handle all of the operations at that level.

line 6
If all_io(D3oddi) returns TRUE, bdistributed(D3oddi) is called to notify the kernel that the driver can be accessed by more than one processor. The first argument to bdistributed( ) specifies the name of the driver's strategy( ) routine in the block device switch table, bdevsw[]. The second argument specifies flags for I/O operations. This value must include DIST_BDEV_STRATEGY; other values defined in the <sys/conf.h> header file may be ORed with it. The flags are stored in an array associated with bdevsw[].

If the driver is a SCSI host adapter, it must also call the scsi_distributed(D3osdi) function:

          if (all_io(MP_ATBUS)) {
                bdistributed(xxstrategy, DIST_BDEV_STRATEGY);
SCSI drivers are discussed in SCSI Driver Interface.

lines 7-8
If the system is one in which not all of the processors have I/O access, all_io(D3oddi) will return FALSE. In this case, the driver calls intralloc(D3oddi) to allocate an interprocessor interrupt so that when startio(D3oddi) is called, the correct routine is called.

line 9
The xxhandle variable (declared in line 1) provides a handle on a structure describing the address and lock of the start( ) routine. If a processor needs to access the I/O bus but cannot (see the all_io(D3oddi) manual page), startio(D3oddi) is called. startio( ) interrupts a processor that can access the I/O bus, and instructs it to initiate I/O for the driver by locking the lock specified by intralloc(D3oddi) and running the start( ) routine passed to intralloc(D3oddi).

line 10
If the call to intralloc( ) fails, a message should be displayed to indicate that the driver will not be operating in multiprocessor mode.

lines 11-12
If the call to intralloc( ) succeeds, bdistributed(D3oddi) is called.

© 2005 The SCO Group, Inc. All rights reserved.