Consideraciones sobre la programación

Un módulo de red debe usar alguna forma de recuento de referencias para realizar un seguimiento del número de llamadas en curso a las funciones de interfaz de programación de red (NPI) de un módulo de red conectado. Esto facilitará la desasociación de un módulo de red conectado cuando uno de los dos módulos de red anule el registro con nmR. Un módulo de red no puede completar el desaprendimiento hasta que no haya llamadas en curso a las funciones NPI del módulo de red conectados. Un módulo de red también debe asegurarse de que no se inicien más llamadas al módulo de red conectado previamente una vez que se desasocian los módulos de red.

Por ejemplo, un módulo cliente podría usar una implementación similar a la siguiente para realizar el seguimiento del número de llamadas en curso a las funciones NPI de un módulo del proveedor asociado:

// Context structure for the client's binding to a provider module
typedef struct CLIENT_BINDING_CONTEXT_ {
  LIST_ENTRY Link;
  HANDLE NmrBindingHandle;
  PVOID ProviderBindingContext;
  PEXNPI_PROVIDER_DISPATCH ProviderDispatch;
  KSPIN_LOCK DetachLock;
  LONG InProgressCallCount;
  LONG Detaching;
  .
  . // Other client-specific members
  .
} CLIENT_BINDING_CONTEXT, *PCLIENT_BINDING_CONTEXT;

// Pool tag used for allocating the binding context
#define BINDING_CONTEXT_POOL_TAG 'tpcb'

// Structure for the client's dispatch table
const EXNPI_CLIENT_DISPATCH Dispatch = {
  .
  . // Function pointers to the client module's
  . // NPI callback functions
  .
};

// Head of linked list of binding context structures
LIST_ENTRY BindingContextList;

// Spin lock for binding context list
KSPIN_LOCK BindingContextListLock;

// Prototype for the client module's unload function
VOID
  Unload(
    PDRIVER_OBJECT DriverObject
    );

// Variable to contain the handle for the registration
HANDLE ClientHandle;

// DriverEntry function
NTSTATUS
  DriverEntry(
    PDRIVER_OBJECT DriverObject,
    PUNICODE_STRING RegistryPath
    )
{
  NTSTATUS Status;

  // Specify the unload function
  DriverObject->DriverUnload = Unload;

  // Initialize the binding context list spin lock
  KeInitializeSpinLock(
    &BindingContextListLock
    );

  // Initialize the binding context list head
  InitializeListHead(
    &BindingContextList
    );

  .
  . // Other initialization tasks
  .

  // Register the client module with the NMR
  Status = NmrRegisterClient(
    &ClientCharacteristics,
    &ClientRegistrationContext,
    &ClientHandle,
    );

  // Return the result of the registration
  return Status;
}

// ClientAttachProvider callback function
NTSTATUS
  ClientAttachProvider(
    IN HANDLE NmrBindingHandle,
    IN PVOID ClientContext,
    IN PNPI_REGISTRATION_INSTANCE ProviderRegistrationInstance
    )
{
  PNPI_MODULEID ProviderModuleId;
  PEXNPI_PROVIDER_CHARACTERISTICS ProviderNpiSpecificCharacteristics;
  PCLIENT_BINDING_CONTEXT BindingContext;
  PVOID ProviderBindingContext;
  PEXNPI_PROVIDER_DISPATCH ProviderDispatch;
  KLOCK_QUEUE_HANDLE BindingContextListLockHandle;
  NTSTATUS Status;

  // Get pointers to the provider module's identification structure
  // and the provider module's NPI-specific characteristics structure
  ProviderModuleId = ProviderRegistrationInstance->ModuleId;
  ProviderNpiSpecificCharacteristics =
    (PEXNPI_PROVIDER_CHARACTERISTICS)
      ProviderRegistrationInstance->NpiSpecificCharacteristics;

  //
  // Use the data in the structures pointed to by
  // ProviderRegistrationInstance, ProviderModuleId,
  // and ProviderNpiSpecificCharacteristics to determine
  // whether to attach to the provider module.
  //

  // If the client module determines that it will not attach
  // to the provider module
  if (...)
  {
    // Return status code indicating the modules did not
    // attach to each other
    return STATUS_NOINTERFACE;
  }

  // Allocate memory for the client's binding context structure
  BindingContext =
    (PCLIENT_BINDING_CONTEXT)
      ExAllocatePoolWithTag(
        NonPagedPool,
        sizeof(CLIENT_BINDING_CONTEXT),
        BINDING_CONTEXT_POOL_TAG
        );

  // Check result of allocation
  if (BindingContext == NULL)
  {
    // Return error status code
    return STATUS_INSUFFICIENT_RESOURCES;
  }

  // Initialize the client binding context structure
  KeInitializeSpinLock(
    &BindingContext->DetachLock
   );
  BindingContext->InProgressCallCount = 0;
  BindingContext->Detaching = 0;
  ...

  // Continue with the attachment to the provider module
  Status = NmrClientAttachProvider(
    NmrBindingHandle,
    BindingContext,
    &Dispatch,
    &ProviderBindingContext,
    &ProviderDispatch
    );

  // Check result of attachment
  if (Status == STATUS_SUCCESS)
  {
    // Save NmrBindingHandle, ProviderBindingContext,
    // and ProviderDispatch for future reference
    BindingContext->NmrBindingHandle =
      NmrBindingHandle;
    BindingContext->ProviderBindingContext =
      ProviderBindingContext;
    BindingContext->ProviderDispatch =
      ProviderDispatch;

    // Acquire the binding context list spin lock
    KeAcquireInStackQueuedSpinLock(
      &BindingContextListLock,
      &BindingContextListLockHandle
      );

    // Add this binding context to the list of valid
    // binding contexts
    InsertTailList(
      &BindingContextList,
      &BindingContext->Link
      );
 
    // Release the binding context list spin lock
    KeReleaseInStackQueuedSpinLock(
      &BindingContextListLockHandle
      );
  }

  // Attachment did not succeed
  else
  {
    // Free memory for client's binding context structure
    ExFreePoolWithTag(
      BindingContext,
      BINDING_CONTEXT_POOL_TAG
      );
  }

  // Return result of attachment
  return Status;
}

// Wrapper function around a provider NPI function
//
// Each of the provider NPI functions should be wrapped
// in this manner.
NTSTATUS
  ProviderNpiFunctionXxx(
    ClientBindingContext,
    .
    . // Parameters to the provider NPI function
    .
    )
{
  KLOCK_QUEUE_HANDLE BindingContextListLockHandle;
  KLOCK_QUEUE_HANDLE DetachLockHandle;
  PCLIENT_BINDING_CONTEXT BindingContextListElement;
  PLIST_ENTRY Entry;
  NTSTATUS Status;

  // Acquire the binding context list spin lock
  KeAcquireInStackQueuedSpinLock(
    &BindingContextListLock,
    &BindingContextListLockHandle
    );

  // Search for the binding context in the list of valid
  // binding contexts
  for (Entry = BindingContextList.Flink;
       Entry != &BindingContextList;
       Entry = Entry->Flink)
  {
    // Get the next binding context from the list
    BindingContextListElement =
      CONTAINING_RECORD(
        Entry,
        CLIENT_BINDING_CONTEXT,
        Link
        );

    // Check if this binding context is a match
    if (BindingContextListElement == ClientBindingContext)
    {
      // Break out of the search loop
      break;
    }
  }

  // Check if the binding context was not found
  if (Entry == &BindingContextList)
  {
    // Release the binding context list spin lock
    KeReleaseInStackQueuedSpinLock(
      &BindingContextListLockHandle
      );

    // Return status indicating that the interface is not available
    return STATUS_NOINTERFACE;
  }

  // Acquire the detach spin lock at DPC level
  KeAcquireInStackQueuedSpinLockAtDpcLevel(
    &ClientBindingContext->DetachLock,
    &DetachLockHandle
    );

  // The modules should not be detaching
  ASSERT(ClientBindingContext->Detaching != 1);

  // Increment the in-progress call count
  ClientBindingContext->InProgressCallCount++;

  // Release the detach spin lock from DPC level
  KeReleaseInStackQueuedSpinLockFromDpcLevel(
    &DetachLockHandle
    );

  // Release the binding context list spin lock
  KeReleaseInStackQueuedSpinLock(
    &BindingContextListLockHandle
    );

  // Call the provider NPI function
  Status =
    ClientBindingContext->ProviderDispatch->ProviderNpiFunctionXxx(
      ClientBindingContext->ProviderBindingContext,
      .
      . // Parameters to the provider NPI function
      .
      );

  // Check if pending
  if (Status == STATUS_PENDING)
  {
    // If completion of the call is pending, then when the call
    // completes, the completion routine (or other callback function
    // that is called upon completion of the call) must include the
    // the same code as below for the non-pending case.

    // Return pending status
    return STATUS_PENDING;
  }

  // Acquire the detach spin lock
  KeAcquireInStackQueuedSpinLock(
    &ClientBindingContext->DetachLock,
    &DetachLockHandle
    );

  // Decrement the in-progress call count
  ClientBindingContext->InProgressCallCount--;

  // Check if the modules are now detaching
  if (ClientBindingContext->Detaching == 1)
  {
    // Check if this call was the last of the in-progress
    // calls to the provider module to be completed
    if (ClientBindingContext->InProgressCallCount == 0)
    {
      // Release the detach spin lock
      KeReleaseInStackQueuedSpinLock(
        &DetachLockHandle
        );

      // Inform the NMR that detachment is complete
      NmrClientDetachProviderComplete(
        ClientBindingContext->NmrBindingHandle
        );
    }
    else
    {
      // Release the detach spin lock
      KeReleaseInStackQueuedSpinLock(
        &DetachLockHandle
        );
    }
  }
  else
  {
    // Release the detach spin lock
    KeReleaseInStackQueuedSpinLock(
      &DetachLockHandle
      );
  }

  // Return status of the call to the provider NPI function
  return Status;
}

// ClientDetachProvider callback function
NTSTATUS
  ClientDetachProvider(
    IN PVOID ClientBindingContext
    )
{
  PCLIENT_BINDING_CONTEXT BindingContext;
  KLOCK_QUEUE_HANDLE BindingContextListLockHandle;
  KLOCK_QUEUE_HANDLE DetachLockHandle;
  NTSTATUS Status;

  // Get a pointer to the binding context
  BindingContext = (PCLIENT_BINDING_CONTEXT)ClientBindingContext;

  // Acquire the binding context list spin lock
  KeAcquireInStackQueuedSpinLock(
    &BindingContextListLock,
    &BindingContextListLockHandle
    );

  // Remove the binding context from the binding context list
  RemoveEntryList(&BindingContext->Link);

  // Acquire the detach spin lock at DPC level
  KeAcquireInStackQueuedSpinLockAtDpcLevel(
    &BindingContext->DetachLock,
    &DetachLockHandle
    );

  // Set the flag indicating that the client module is detaching
  // from the provider module so that the completion of the final
  // call will complete the detachment
  BindingContext->Detaching = 1;

  // Check if there are no in-progress NPI function calls to the
  // provider module
  if (BindingContext->InProgressCallCount == 0)
  {
    // Set the status to success to indicate detachment is complete
    Status = STATUS_SUCCESS;
  }

  // There are one or more in-progress NPI function calls
  // to the provider module
  else
  {
    // Set the status to pending to indicate that detachment is
    // pending completion of the in-progress NPI function calls
    Status = STATUS_PENDING;
  }

  // Release the detach spin lock from DPC level
  KeReleaseInStackQueuedSpinLockFromDpcLevel(
    &DetachLockHandle
    );

  // Release the binding context list spin lock
  KeReleaseInStackQueuedSpinLock(
    &BindingContextListLockHandle
    );

  // Return the status of the detachment
  return Status;
}

Del mismo modo, un módulo de proveedor podría usar una implementación en las mismas líneas que el ejemplo del módulo de cliente anterior para realizar el seguimiento del número de llamadas en curso a las funciones de devolución de llamada NPI del módulo cliente conectado.

Nota En el ejemplo de código anterior se muestra un posible método de seguimiento del número de llamadas en curso a las funciones NPI de un módulo de red conectado. Un módulo de red puede usar un método alternativo en función de los detalles de implementación del NPI concreto que admite el módulo de red.