Considerações sobre programação
Um módulo de rede deve usar alguma forma de contagem de referência para acompanhar o número de chamadas em andamento para as funções de NPI (Interface de Programação de Rede) de um módulo de rede anexado. Isso facilitará a desanexação de um módulo de rede anexado quando um dos dois módulos de rede desregistros com o NMR. Um módulo de rede não pode concluir o desanexamento até que não haja chamadas em andamento para as funções de NPI do módulo de rede anexado. Um módulo de rede também deve garantir que nenhuma outra chamada para o módulo de rede anexado anteriormente seja iniciada depois que os módulos de rede forem desanexados.
Por exemplo, um módulo cliente pode usar uma implementação semelhante à seguinte para acompanhar o número de chamadas em andamento para as funções NPI de um módulo de provedor anexado:
// 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;
}
Da mesma forma, um módulo de provedor pode usar uma implementação nas mesmas linhas que o exemplo de módulo do cliente acima para acompanhar o número de chamadas em andamento para as funções de retorno de chamada de NPI de um módulo de cliente anexado.
Nota O exemplo de código acima mostra um método possível de acompanhar o número de chamadas em andamento para as funções NPI de um módulo de rede anexado. Um módulo de rede pode usar um método alternativo dependendo dos detalhes de implementação da NPI específica compatível com o módulo de rede.