5.3 Writing Multi Processor-Aware Drivers

There are three basic modifications to allow NetWare to run your HAM driver on multiple processors:

  1. Modify the instance number to allow the OS to run your HAM driver on multiple processors.
  2. Protect operations on global variables from inadvertent changes.
  3. Protect modifications to global list links from inadvertent changes.

The following sections elaborate on the above basic modifications and illustrate the proper methods of altering your storage (HAM or CDM) driver to make it MP-aware (multiprocessor aware).

  1. Modify the instance number passed to NPA_Register_HAM_Module with the MP bit in a HAM as follows:
        NPA_Register_HAM_Module(
           &npaHandle,
           novellAssignedModuleID,
           loadHandle,
           HAM_Check_Option,
           HAM_Software_Hot_Replace,
           HAM_ISR,
           HAM_Execute_HACB,
           HAM_Abort_HACB,
           (instanceNumber | 0x00010000)); 
        

    or to a NPA_Register_CDM_Module in a CDM as follows:

        NPA_Register_CDM_Module(
           &npaHandle,
           novellAssignedModuleID,
           loadHandle,
           CDM_Check_Option,
           CDM_Execute_Message,
           CDM_Inquiry,
           (instanceNumber | 0x00010000)); 
        

    Do not modify the instance number directly, as in

        instanceNumber |= 0x000100000;
        

    After NWPA detects this bit is set, your HAM driver code executes on multiple processors in multiprocessor systems, for all instances of the driver. Your HAM driver then receives requests on threads originating from any processor. After this, pass InstanceNumber to the functions that need it, as usual.

    NOTE:Note: The MP bit is recognized only on the first instance of the driver load, but is effective for all subsequent instances. Do not use any other bit in the high word of the instance number for any reason. They are all reserved by NWPA.

  2. Protect operations on your global variables (those variables accessible by other modules) by locking them with the following atomic operations:

    Before:

           deviceCount++; 
           numberOfInstances--; 
           length += 6; 
           width -= 17; 
           DoSwap(&status, &result); //swaps values in 2 variables
        

    After

           NPA_Inc(&deviceCount); 
           NPA_Dec(&numberOfInstances); 
           NPA_Add(&length, 6); 
           NPA_Sub(&width, 17); 
           previousStatus = NPA_Xchg(&status, result);
        

    NOTE:Atomic lock operations are not supported on 8-bit or 16-bit variables.

  3. Protect modification to global list links from inadvertent changes. NPA_SpinLock does not put your driver to sleep, but spins in a loop and returns only after the lock is accomplished. Use the following guidelines:
    1. Initialize each lock status variable when you initialize its structure.
    2. Protect global variable test-and-set assignments.
    3. Protect link field assignments.
    4. You can protect more than one variable/link at a time.
    5. Compares with global variables need no protection.
    6. Unprotect the assignments before calling other functions, if practical.
    7. Protect lock status variables that are shared with your ISR.

      An example follows:

      Before:

          struct DeviceStruct *device; 
          struct HACBStruct *HACB; 
          ... 
          HACB = queue->firstHACB;          // needs protection 
          device = (struct DeviceStruct *) HACB->deviceHandle;
          if (HACB == 0)                     // no need to protect this compare 
          { 
             InitializeDeviceParameters(device); 
             CheckForDeviceReady(device); 
          } 
          else 
          { 
             queue->firstHACB = HACB;          // needs protection 
             HACB->nextHACB->prevHACB = NULL;   // needs protection 
             ExecuteHACB(device, HACB); 
          }
          

      After:

          struct QueueStruct               // add a “status” field to your structure 
          { 
             ... 
             LONG lockStatus;                 // simply an unsigned long 
             ... 
          }; 
          struct DeviceStruct *device;
          struct HACBStruct *HACB;
          ... 
          NPA_SpinLockInit(&queue->lockStatus); // initialize each lock status variable
                                                // only once in your code...preferably
                                                // during initialization of the structure
                                               // structure to which the lock status
                                              // variable belongs 
          ... 
          NPA_SpinLock(&queue->lockStatus);  // protect all local elements used after
                                              // this line 
          HACB = queue->firstHACB;           // this line is now protected 
          device = (struct DeviceStruct *) HACB->deviceHandle;
          if (HACB == 0) 
          { 
             NPA_SpinUnlock(&queue->lockStatus); // remove the protection
             InitializeDeviceParameters(device);  // do not lock during  
             CheckForDeviceReady(device);         // time-expensive functions 
          } 
          else 
          { 
             queue->firstHACB = HACB;            // this line is now protected 
             HACB->nextHACB->prevHACB = NULL;     // this line is now protected 
             NPA_SpinUnlock(&queue->lockStatus); // remove the protection 
             ExecuteHACB(device, HACB); 
          }
          

      NOTE:If you lock the same “status” variable in your ISR that you lock elsewhere in your code, you must use NPA_SpinLockDisable or NPA_SpinTryLockDisable (to disable interrupts) and NPA_SpinUnlockRestore on the same status variable in your code outside your ISR. An example of this follows, assuming queue->lockStatus is being used in the ISR and the following code is outside your ISR:

          NPA_SpinLockInit(&queue->lockStatus); 
          ... 
          LONG CPUFlags;
          struct DeviceStruct *device;
          ... 
          CPUFlags = NPA_SpinLockDisable(&queue->lockStatus); 
          HACB = queue->firstHACB; 
          device = (struct DeviceStruct *)HACB->deviceHandle;
          if (HACB == 0) 
          { 
             NPA_SpinUnlockRestore(&queue->lockStatus, CPUFlags); 
             InitializeDeviceParameters(device); 
             CheckForDeviceReady(device); 
          } 
          else 
          { 
             queue->firstHACB = HACB; 
             HACB->nextHACB->prevHACB = NULL; 
             NPA_SpinUnlockRestore(&queue->lockStatus, CPUFlags); 
             ExecuteHACB(device, HACB); 
          }
          

These MP functions are available in NetWare 4 Support Pack 8 and greater, NetWare 5.0 Support Pack 2 and greater, and in all versions of NetWare 5.1 and greater.