Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Przegląd
Kolejki wysyłania i odbioru w sterownikach NetAdapterCx umożliwiają sterownikom klienta modelowanie funkcji sprzętowych, takich jak sprzętowe kolejki wysyłania i odbioru, w oprogramowaniu. W tym artykule wyjaśniono, jak zaimplementować te kolejki i zarządzać nimi, aby zoptymalizować wydajność ścieżki danych w środowisku NetAdapterCx.
Gdy sterownik klienta wywołuje NET_ADAPTER_DATAPATH_CALLBACKS_INIT, zazwyczaj z funkcji wywołania zwrotnego zdarzeń EVT_WDF_DRIVER_DEVICE_ADD, udostępnia dwa wywołania zwrotne tworzenia kolejki: EVT_NET_ADAPTER_CREATE_TXQUEUE i EVT_NET_ADAPTER_CREATE_RXQUEUE. Klient tworzy odpowiednio kolejki transmisji i odbierania w tych wywołaniach zwrotnych.
Struktura opróżnia kolejki przed przejściem do stanu niskiego poboru energii, a następnie je usuwa przed usunięciem adaptera.
Tworzenie kolejek pakietów
Podczas tworzenia kolejki pakietów, kolejki transmisji lub kolejki odbierania, klient musi podać wskaźniki do następujących trzech funkcji wywołania zwrotnego:
Ponadto, po zainicjowaniu struktury konfiguracji kolejki, klient może udostępnić te opcjonalne funkcje wywołań zwrotnych.
Utwórz kolejkę transmisji
NetAdapterCx wywołuje EVT_NET_ADAPTER_CREATE_TXQUEUE na samym końcu sekwencji zasilania. Podczas tego wywołania zwrotnego sterowniki klienta zwykle wykonują następujące czynności:
- Możesz opcjonalnie zarejestrować funkcje zwrotne dla uruchamiania i zatrzymywania tej kolejki.
- Wywołaj NetTxQueueInitGetQueueId, aby pobrać identyfikator kolejki nadawczej do skonfigurowania.
- Wywołaj NetTxQueueUtwórz, aby przydzielić kolejkę.
- Jeśli NetTxQueueCreate zakończy się niepowodzeniem, funkcja EvtNetAdapterCreateTxQueue wywołania zwrotnego powinna zwrócić kod błędu.
- Wykonywanie zapytań o przesunięcia rozszerzenia pakietu.
W poniższym przykładzie pokazano, jak te kroki mogą wyglądać w kodzie. W celu zapewnienia przejrzystości kod obsługi błędów zostanie pominięty w przykładzie.
NTSTATUS
EvtAdapterCreateTxQueue(
_In_ NETADAPTER Adapter,
_Inout_ NETTXQUEUE_INIT * TxQueueInit
)
{
NTSTATUS status = STATUS_SUCCESS;
// Prepare the configuration structure
NET_PACKET_QUEUE_CONFIG txConfig;
NET_PACKET_QUEUE_CONFIG_INIT(
&txConfig,
EvtTxQueueAdvance,
EvtTxQueueSetNotificationEnabled,
EvtTxQueueCancel);
// Optional: register the queue's start and stop callbacks
txConfig.EvtStart = EvtTxQueueStart;
txConfig.EvtStop = EvtTxQueueStop;
// Get the queue ID
const ULONG queueId = NetTxQueueInitGetQueueId(TxQueueInit);
// Create the transmit queue
NETPACKETQUEUE txQueue;
status = NetTxQueueCreate(
TxQueueInit,
&txAttributes,
&txConfig,
&txQueue);
// Get the queue context for storing the queue ID and packet extension offset info
PMY_TX_QUEUE_CONTEXT queueContext = GetMyTxQueueContext(txQueue);
// Store the queue ID in the context
queueContext->QueueId = queueId;
// Query checksum packet extension offset and store it in the context
NET_EXTENSION_QUERY extension;
NET_EXTENSION_QUERY_INIT(
&extension,
NET_PACKET_EXTENSION_CHECKSUM_NAME,
NET_PACKET_EXTENSION_CHECKSUM_VERSION_1);
NetTxQueueGetExtension(txQueue, &extension, &queueContext->ChecksumExtension);
// Query Large Send Offload packet extension offset and store it in the context
NET_EXTENSION_QUERY_INIT(
&extension,
NET_PACKET_EXTENSION_LSO_NAME,
NET_PACKET_EXTENSION_LSO_VERSION_1);
NetTxQueueGetExtension(txQueue, &extension, &queueContext->LsoExtension);
return status;
}
Tworzenie kolejki odbierania
Aby utworzyć kolejkę odbierania z EVT_NET_ADAPTER_CREATE_RXQUEUE, użyj tego samego wzorca co kolejka przesyłania i wywołaj NetRxQueueCreate.
W poniższym przykładzie pokazano, jak tworzenie kolejki odbierania może wyglądać w kodzie. W celu zapewnienia przejrzystości kod obsługi błędów zostanie pominięty w przykładzie.
NTSTATUS
EvtAdapterCreateRxQueue(
_In_ NETADAPTER NetAdapter,
_Inout_ PNETRXQUEUE_INIT RxQueueInit
)
{
NTSTATUS status = STATUS_SUCCESS;
// Prepare the configuration structure
NET_PACKET_QUEUE_CONFIG rxConfig;
NET_PACKET_QUEUE_CONFIG_INIT(
&rxConfig,
EvtRxQueueAdvance,
EvtRxQueueSetNotificationEnabled,
EvtRxQueueCancel);
// Optional: register the queue's start and stop callbacks
rxConfig.EvtStart = EvtRxQueueStart;
rxConfig.EvtStop = EvtRxQueueStop;
// Get the queue ID
const ULONG queueId = NetRxQueueInitGetQueueId(RxQueueInit);
// Create the receive queue
NETPACKETQUEUE rxQueue;
status = NetRxQueueCreate(
RxQueueInit,
&rxAttributes,
&rxConfig,
&rxQueue);
// Get the queue context for storing the queue ID and packet extension offset info
PMY_RX_QUEUE_CONTEXT queueContext = GetMyRxQueueContext(rxQueue);
// Store the queue ID in the context
queueContext->QueueId = queueId;
// Query the checksum packet extension offset and store it in the context
NET_EXTENSION_QUERY extension;
NET_EXTENSION_QUERY_INIT(
&extension,
NET_PACKET_EXTENSION_CHECKSUM_NAME,
NET_PACKET_EXTENSION_CHECKSUM_VERSION_1);
NetRxQueueGetExtension(rxQueue, &extension, &queueContext->ChecksumExtension);
return status;
}
Model sondowania
Ścieżka danych NetAdapter jest modelem sondowania, a operacja sondowania w jednej kolejce pakietów jest niezależna od innych kolejek. Model odpytywania jest implementowany przez wywołanie funkcji kolejki w sterowniku klienta, jak pokazano na poniższej ilustracji.
Zaawansowane kolejki pakietów
Sekwencja operacji odpytywania w kolejce pakietów jest następująca:
- System operacyjny dostarcza bufory sterownikowi klienta na potrzeby przesyłania lub odbierania.
- Sterownik klienta programuje pakiety do sprzętu.
- Sterownik klienta zwraca ukończone pakiety do systemu operacyjnego.
Operacje odpytywania są wykonywane w funkcji wywołania zwrotnego EvtPacketQueueAdvance. Każda kolejka pakietów w sterowniku klienta jest wspierana przez bazowe struktury danych o nazwie pierścienie sieci, które zawierają lub łączą się z faktycznymi danymi sieciowymi w pamięci systemowej. Podczas EvtPacketQueueAdvancesterowniki klienckie przeprowadzają operacje wysyłania i odbierania danych na pierścieniach sieciowych, kontrolując indeksy w pierścieniach oraz przekazując własność bufora między sprzętem a systemem operacyjnym w trakcie przesyłania lub odbierania danych.
Aby uzyskać więcej informacji na temat pierścieni sieciowych, zobacz Wprowadzenie do pierścieni sieciowych.
Aby zapoznać się z przykładem implementacji EvtPacketQueueAdvance dla kolejki transmisji, zobacz Wysyłanie danych sieciowych za pomocą pierścieni sieciowych. Aby zapoznać się z przykładem implementacji EvtPacketQueueAdvance dla kolejki odbioru, proszę zapoznać się z Odbieranie danych sieciowych za pomocą pierścieni sieciowych.
Włączanie i wyłączanie powiadomienia kolejki pakietów
Gdy sterownik klienta odbiera nowe pakiety w pierścieniach sieci kolejki pakietów, NetAdapterCx wywołuje funkcję wywołania zwrotnego sterownika klienta EvtPacketQueueSetNotificationEnabled. To wywołanie zwrotne informuje sterownik klienta, że sondowanie (EvtPacketQueueAdvance lub EvtPacketQueueCancel) zostanie wstrzymane i nie zostanie wznowione, dopóki sterownik klienta nie wywoła NetTxQueueNotifyMoreCompletedPacketsAvailable lub NetRxQueueNotifyMoreReceivedPacketsAvailable. Zwykle urządzenie PCI używa tego wywołania zwrotnego do włączania przerwań Tx lub Rx. Po odebraniu przerwania można ponownie wyłączyć przerwania, a sterownik klienta wywołuje NetTxQueueNotifyMoreCompletedPacketsAvailable lub NetRxQueueNotifyMoreReceivedPacketsAvailable, aby spowodować, że framework znowu zacznie sondować.
Włączanie i wyłączanie powiadomień dla kolejki transmisji
W przypadku karty sieciowej PCI włączenie powiadomienia o kolejce transmisji zwykle oznacza włączenie przerwania sprzętowego kolejki transmisji. Gdy występuje przerwanie sprzętowe, klient wywołuje NetTxQueueNotifyMoreCompletedPacketsAvailable ze swojego DPC.
Podobnie w przypadku karty sieciowej PCI, wyłączenie powiadomienia kolejki oznacza wyłączenie przerwania skojarzonego z kolejką.
W przypadku urządzenia, które ma asynchroniczny model we/wy, klient zazwyczaj używa flagi wewnętrznej do śledzenia stanu włączonego. Po zakończeniu operacji asynchronicznej program obsługi uzupełniania sprawdza tę flagę i wywołuje polecenie NetTxQueueNotifyMoreCompletedPacketsAvailable , jeśli jest ustawiona.
Jeśli NetAdapterCx wywołuje EvtPacketQueueSetNotificationEnabled z NotificationEnabled ustawioną na FALSE, klient nie może wywołać NetTxQueueNotifyMoreCompletedPacketsAvailable, dopóki NetAdapterCx nie wywoła ponownie tej funkcji wywołania zwrotnego z NotificationEnabled ustawioną na TRUE.
Na przykład:
VOID
MyEvtTxQueueSetNotificationEnabled(
_In_ NETPACKETQUEUE TxQueue,
_In_ BOOLEAN NotificationEnabled
)
{
// Optional: retrieve queue's WDF context
MY_TX_QUEUE_CONTEXT *txContext = GetTxQueueContext(TxQueue);
// If NotificationEnabled is TRUE, enable transmit queue's hardware interrupt
...
}
VOID
MyEvtTxInterruptDpc(
_In_ WDFINTERRUPT Interrupt,
_In_ WDFOBJECT AssociatedObject
)
{
MY_INTERRUPT_CONTEXT *interruptContext = GetInterruptContext(Interrupt);
NetTxQueueNotifyMoreCompletedPacketsAvailable(interruptContext->TxQueue);
}
Włączanie i wyłączanie powiadomień dla kolejki odbiorczej
W przypadku karty sieciowej PCI włączenie powiadomienia o kolejce odbierania wygląda bardzo podobnie do kolejki Tx. Zazwyczaj oznacza to włączenie przerwania sprzętowego kolejki odbiorczej. Po uruchomieniu się przerwania sprzętowego klient wywołuje NetRxQueueNotifyMoreReceivedPacketsAvailable ze swojego DPC.
Na przykład:
VOID
MyEvtRxQueueSetNotificationEnabled(
_In_ NETPACKETQUEUE RxQueue,
_In_ BOOLEAN NotificationEnabled
)
{
// optional: retrieve queue's WDF Context
MY_RX_QUEUE_CONTEXT *rxContext = GetRxQueueContext(RxQueue);
// If NotificationEnabled is TRUE, enable receive queue's hardware interrupt
...
}
VOID
MyEvtRxInterruptDpc(
_In_ WDFINTERRUPT Interrupt,
_In_ WDFOBJECT AssociatedObject
)
{
MY_INTERRUPT_CONTEXT *interruptContext = GetInterruptContext(Interrupt);
NetRxQueueNotifyMoreReceivedPacketsAvailable(interruptContext->RxQueue);
}
W przypadku urządzenia USB lub innej kolejki z mechanizmem do obsługi odbioru za pomocą oprogramowania, sterownik klienta powinien monitorować we własnym kontekście, czy powiadomienie kolejki jest włączone. Z procedury uzupełniania (wyzwalanej na przykład, gdy komunikat stanie się dostępny w czytniku ciągłym USB), wywołaj NetRxQueueNotifyMoreReceivedPacketsAvailable, jeśli powiadomienie jest włączone. W poniższym przykładzie pokazano, jak można to zrobić.
VOID
UsbEvtReaderCompletionRoutine(
_In_ WDFUSBPIPE Pipe,
_In_ WDFMEMORY Buffer,
_In_ size_t NumBytesTransferred,
_In_ WDFCONTEXT Context
)
{
UNREFERENCED_PARAMETER(Pipe);
PUSB_RCB_POOL pRcbPool = *((PUSB_RCB_POOL*) Context);
PUSB_RCB pRcb = (PUSB_RCB) WdfMemoryGetBuffer(Buffer, NULL);
pRcb->DataOffsetCurrent = 0;
pRcb->DataWdfMemory = Buffer;
pRcb->DataValidSize = NumBytesTransferred;
WdfObjectReference(pRcb->DataWdfMemory);
ExInterlockedInsertTailList(&pRcbPool->ListHead,
&pRcb->Link,
&pRcbPool->ListSpinLock);
if (InterlockedExchange(&pRcbPool->NotificationEnabled, FALSE) == TRUE)
{
NetRxQueueNotifyMoreReceivedPacketsAvailable(pRcbPool->RxQueue);
}
}
Anuluj kolejki pakietów
Gdy system operacyjny zatrzymuje ścieżkę danych, rozpoczyna od wywołania funkcji zwrotnej EvtPacketQueueCancel klienta. To wywołanie zwrotne polega na tym, że sterowniki klienta wykonują wszelkie wymagane operacje przetwarzania, zanim platforma usunie kolejki pakietów. Anulowanie kolejki transmisji jest opcjonalne i zależy od tego, czy sprzęt obsługuje anulowanie transmisji w locie, ale anulowanie kolejki odbierania jest wymagane.
Podczas EvtPacketQueueCancelsterowniki, w razie potrzeby, zwracają pakiety do systemu operacyjnego. Aby zapoznać się z przykładami kodu anulowania kolejki nadawczej i kolejki odbiorczej, zobacz Anulowanie danych sieciowych za pomocą pierścieni sieciowych.
Po wywołaniu wywołania zwrotnego EvtPacketQueueCancel, struktura kontynuuje sondowanie wywołania zwrotnego EvtPacketQueueAdvance, aż wszystkie pakiety i bufory zostaną zwrócone do systemu operacyjnego.