Multithreading device drivers

Modifying the strategy routine

This section describes an strategy( ) routine that has not been converted for multiprocessor access, and then explains how to enhance the code so that it can take advantage of a multiprocessor environment.

The following code illustrates the strategy( ) routine of a driver that has not been converted for multiprocessor access.

Single-threaded xxstrategy fragment

  1 s = spl5();
  2 disksort(&xxtab, bp);
  3 xxstart(xxstart_arg);
  4 splx(s);

Line 1
The spl(5) call blocks interrupts from devices with IPLs of 1 to 5.

Line 2
Calls disksort(D3oddi) to sort the disk requests.

Line 3
Calls xxstart( ) to perform the disk I/O

Line 4
Restores the spl level back to its previous value.

To convert this code to take advantage of multiprocessing, the critical code sections need to be protected with locks as illustrated in the following code fragment:

Multithreaded xxstrategy fragment

  1 s = lockb(&lock_xxtab);
  2 disksort(&xxtab, bp);
  3 if (xxtab.b_active == 0) {
  4 	if (can_doio(MP_ATBUS))
  5 		xxstart(xxstart_arg);
  6 	else {
  7 		unlockb(&lock_xxtab, -1);
  8 		startio(xxhandle, xxstart_arg);
  9 	}
 10 	splx(s);
 11 }
 12 else {
 13 	unlockb(&lock_xxtab, s);
 14 	return;
 15 }

NOTE: Not all drivers send requests at interrupt time; some drivers use the start(D2oddi) and strategy(D2oddi) entry point routines. This example assumes that further requests are sent at interrupt time if the device is busy.

Line 1
Before calling disksort(D3oddi), the strategy( ) routine needs to protect critical code sections so that access to the linked list of I/O requests is protected. In this code, the spl5 call is replaced with a call to lockb(D3oddi). lockb is passed the address of the lock (the lock structure defined in the <sys/ci/cilock.h> header file).

Line 2
The disksort call is the same as the original.

Line 3
The flag in the xxtab structure is checked to determine whether the driver is performing I/O. The b_active flag is set in the driver's start( ) routine to indicate that it is active.

Line 4
If the driver is not already performing I/O, can_doio(D3oddi) is called to see if the current processor can access the I/O bus type.

Line 5
If the processor can access the specified bustype, the start routine is called directly.

Line 7
If access to the I/O bus is not possible from the current processor, the critical code section is unlocked. The -1 argument to unlockb(D3oddi) indicates that unlockb(D3oddi) will not restore the spl value; that is, your driver will call splx after the unlockb(D3oddi) call.

Line 8
startio(D3oddi) is called to run the start( ) routine by interrupting a processor that can access the I/O bus. The first argument to the startio(D3oddi) routine is the return value from a previous call to intralloc(D3oddi); the second argument is the parameter with which you want to call start( ).

Line 10
splx(D3oddi) restores the original spl level for the conditions where start or startio(D3oddi) were called.

Lines 13-14
If the driver is already performing I/O, then execution of the critical code section is complete, so the lock is unlocked and the routine exits.

NOTE: Remove any spl calls between lockb(D3oddi) and unlockb(D3oddi) calls.

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