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.
Comentarios
https://aka.ms/ContentUserFeedback.
Próximamente: A lo largo de 2024 iremos eliminando gradualmente GitHub Issues como mecanismo de comentarios sobre el contenido y lo sustituiremos por un nuevo sistema de comentarios. Para más información, vea:Enviar y ver comentarios de