repository: C:/dev/kvm-guest-drivers-windows branch: master commit a1d1275e735a02450bc70c262e343c356da1bb34 Author: Yan Vugenfirer <yvuge...@redhat.com> Date: Thu Oct 22 13:44:42 2009 +0200
[WIN-GUEST-DRIVERS] Initial support for MSI interrupts and DPC\Interrupt behaviour diff --git a/NetKVM/Common/ParaNdis-Common.c b/NetKVM/Common/ParaNdis-Common.c index 3f9faa1..896e6dc 100644 --- a/NetKVM/Common/ParaNdis-Common.c +++ b/NetKVM/Common/ParaNdis-Common.c @@ -406,7 +406,7 @@ static BOOLEAN GetAdapterResources(PNDIS_RESOURCE_LIST RList, tAdapterResources pResources->Level = RList->PartialDescriptors[i].u.Interrupt.Level; pResources->Affinity = RList->PartialDescriptors[i].u.Interrupt.Affinity; pResources->InterruptFlags = RList->PartialDescriptors[i].Flags; - DPrintf(0, ("Found Interrupt level %d, vector %d, affinity %X, flags %X", + DPrintf(0, ("Found Interrupt vector %d, level %d, affinity %X, flags %X", pResources->Vector, pResources->Level, (ULONG)pResources->Affinity, pResources->InterruptFlags)); } } @@ -529,6 +529,12 @@ NDIS_STATUS ParaNdis_InitializeContext( pContext->AdapterResources.IOLength) ) { + if (pContext->AdapterResources.InterruptFlags & CM_RESOURCE_INTERRUPT_MESSAGE) + { + DPrintf(0, ("[%s] Message interrupt assigned", __FUNCTION__)); + pContext->bUsingMSIX = TRUE; + } + VirtIODeviceSetIOAddress(&pContext->IODevice, pContext->AdapterResources.ulIOAddress); JustForCheckClearInterrupt(pContext, "init 0"); ParaNdis_ResetVirtIONetDevice(pContext); @@ -557,7 +563,7 @@ NDIS_STATUS ParaNdis_InitializeContext( { VirtIODeviceGet( &pContext->IODevice, - 0, // offsetof(struct virtio_net_config, mac) + pContext->bUsingMSIX ? 4 : 0, // offsetof(struct virtio_net_config, mac) &pContext->PermanentMacAddress, ETH_LENGTH_OF_ADDRESS); if (!ParaNdis_ValidateMacAddress(pContext->PermanentMacAddress, FALSE)) @@ -1133,6 +1139,7 @@ BOOLEAN ParaNdis_OnInterrupt( if (b) { NdisGetCurrentSystemTime(&pContext->LastInterruptTimeStamp); + ParaNdis_VirtIOEnableInterrupt(pContext, FALSE); } return b; } @@ -1698,12 +1705,24 @@ void ParaNdis_ReportLinkStatus(PARANDIS_ADAPTER *pContext) if (pContext->bLinkDetectSupported) { USHORT linkStatus = 0; + USHORT offset = (pContext->bUsingMSIX ? 4 : 0) + sizeof(pContext->CurrentMacAddress); // link changed - VirtIODeviceGet(&pContext->IODevice, sizeof(pContext->CurrentMacAddress), &linkStatus, sizeof(linkStatus)); + VirtIODeviceGet(&pContext->IODevice, offset, &linkStatus, sizeof(linkStatus)); bConnected = (linkStatus & VIRTIO_NET_S_LINK_UP) != 0; } ParaNdis_IndicateConnect(pContext, bConnected, FALSE); } + +BOOLEAN ParaNdis_MiniportSynchronizeInterruptEnable(IN PVOID SynchronizeContext) +{ + PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)SynchronizeContext; + + DEBUG_ENTRY(6); + ParaNdis_VirtIOEnableInterrupt(pContext, TRUE); + + return TRUE; +} + /********************************************************** DPC implementation, common for both NDIS Parameters: @@ -1757,6 +1776,8 @@ void ParaNdis_DPCWorkBody(PARANDIS_ADAPTER *pContext) } ParaNdis_DebugHistory(pContext, hopDPC, NULL, 0, pContext->nofFreeHardwareBuffers, pContext->nofFreeTxDescriptors); NdisReleaseSpinLock(&pContext->DPCLock); + + ParaNdis_SyncInterruptEnable(pContext); } /********************************************************** @@ -1967,7 +1988,7 @@ NDIS_STATUS ParaNdis_SetMulticastList( } /********************************************************** -TODO + Parameters: Return value: ***********************************************************/ @@ -1975,7 +1996,8 @@ VOID ParaNdis_VirtIOEnableInterrupt( PARANDIS_ADAPTER *pContext, BOOLEAN bEnable) { - DPrintf(0, ("[%s] NOT IMPLEMENTED", __FUNCTION__)); + pContext->NetSendQueue->vq_ops->enable_interrupt(pContext->NetSendQueue, bEnable); + pContext->NetReceiveQueue->vq_ops->enable_interrupt(pContext->NetReceiveQueue, bEnable); } /********************************************************** diff --git a/NetKVM/Common/ParaNdis-Debug.c b/NetKVM/Common/ParaNdis-Debug.c index ef56651..22d3b73 100644 --- a/NetKVM/Common/ParaNdis-Debug.c +++ b/NetKVM/Common/ParaNdis-Debug.c @@ -11,6 +11,7 @@ **********************************************************************/ #include "ndis56common.h" #include "stdarg.h" +#include "ntstrsafe.h" //#define OVERRIDE_DEBUG_BREAK @@ -65,12 +66,31 @@ KBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord; #if !defined(WPP_EVENT_TRACING) || defined(WPP_USE_BYPASS) #if defined(DPFLTR_MASK) + //common case, except Win2K static void DebugPrint(const char *fmt, ...) { + NTSTATUS status; va_list list; va_start(list, fmt); vDbgPrintEx(DPFLTR_DEFAULT_ID, 9 | DPFLTR_MASK, fmt, list); +#if defined(VIRTIO_DBG_USE_IOPORT) + if (1) + { + char buf[256]; + size_t len, i; + buf[0] = 0; + status = RtlStringCbVPrintfA(buf, sizeof(buf), fmt, list); + if (status == STATUS_SUCCESS) len = strlen(buf); + else if (status == STATUS_BUFFER_OVERFLOW) len = sizeof(buf); + else { memcpy(buf, "Can't print", 11); len = 11; } + for (i = 0; i < len; ++i) + { + NdisRawWritePortUchar(VIRTIO_DBG_USE_IOPORT, buf[i]); + } + NdisRawWritePortUchar(VIRTIO_DBG_USE_IOPORT, '\n'); + } +#endif } DEBUGPRINTFUNC pDebugPrint = DebugPrint; diff --git a/NetKVM/Common/ndis56common.h b/NetKVM/Common/ndis56common.h index c8564e9..3fd23a8 100644 --- a/NetKVM/Common/ndis56common.h +++ b/NetKVM/Common/ndis56common.h @@ -54,6 +54,19 @@ #error "Something is wrong with our versioning" #endif +//define to start really using MSI-X interrupt in Vista+ +//#define VIRTIO_USE_MSIX_INTERRUPT + +//define to see when the status register is unreadable(see ParaNdis_ResetVirtIONetDevice) +//#define VIRTIO_RESET_VERIFY + +//define to if hardware raise interrupt on error (see ParaNdis_DPCWorkBody) +//#define VIRTIO_SIGNAL_ERROR + +// define if qemu supports logging to static IO port for synchronization +// of driver output with qemu printouts; in this case define the port number +//#define VIRTIO_DBG_USE_IOPORT 0x99 + /* The feature bitmap for virtio net */ #define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ @@ -245,6 +258,7 @@ typedef struct _tagPARANDIS_ADAPTER BOOLEAN bUseMergedBuffers; BOOLEAN bDoKickOnNoBuffer; BOOLEAN bSurprizeRemoved; + BOOLEAN bUsingMSIX; NDIS_DEVICE_POWER_STATE powerState; LONG InterruptStatus; ULONG ulPriorityVlanSetting; @@ -319,6 +333,7 @@ typedef struct _tagPARANDIS_ADAPTER NDIS_OFFLOAD_PARAMETERS InitialOffloadParameters; #if NDIS60_MINIPORT // Vista + + PIO_INTERRUPT_MESSAGE_INFO pMSIXInfoTable; PNET_BUFFER_LIST SendHead; PNET_BUFFER_LIST SendTail; PNET_BUFFER_LIST SendWaitingList; @@ -418,6 +433,12 @@ UINT ParaNdis_VirtIONetReleaseTransmitBuffers( void ParaNdis_DPCWorkBody( PARANDIS_ADAPTER *pContext); +BOOLEAN ParaNdis_MiniportSynchronizeInterruptEnable( + IN PVOID SynchronizeContext); + +void ParaNdis_SyncInterruptEnable( + PARANDIS_ADAPTER *pContext); + NDIS_STATUS ParaNdis_SetMulticastList( PARANDIS_ADAPTER *pContext, PVOID Buffer, diff --git a/NetKVM/wlh/ParaNdis6-Driver.c b/NetKVM/wlh/ParaNdis6-Driver.c index f3f60de..e7cf3ec 100644 --- a/NetKVM/wlh/ParaNdis6-Driver.c +++ b/NetKVM/wlh/ParaNdis6-Driver.c @@ -707,8 +707,256 @@ static VOID ParaNdis6_DevicePnPEvent( } +static NDIS_STATUS ParaNdis6_AddDevice(IN NDIS_HANDLE MiniportAdapterHandle, IN NDIS_HANDLE MiniportDriverContext) +{ + NDIS_MINIPORT_ADAPTER_ATTRIBUTES MiniportAttributes; + NDIS_STATUS status; + DEBUG_ENTRY(0); + MiniportAttributes.AddDeviceRegistrationAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADD_DEVICE_REGISTRATION_ATTRIBUTES; + MiniportAttributes.AddDeviceRegistrationAttributes.Header.Revision = NDIS_MINIPORT_ADD_DEVICE_REGISTRATION_ATTRIBUTES_REVISION_1; + MiniportAttributes.AddDeviceRegistrationAttributes.Header.Size = NDIS_SIZEOF_MINIPORT_ADD_DEVICE_REGISTRATION_ATTRIBUTES_REVISION_1; + MiniportAttributes.AddDeviceRegistrationAttributes.MiniportAddDeviceContext = MiniportAdapterHandle; + MiniportAttributes.AddDeviceRegistrationAttributes.Flags = 0; + status = NdisMSetMiniportAttributes(MiniportAdapterHandle, &MiniportAttributes); + return status; +} + +static VOID ParaNdis6_RemoveDevice (IN NDIS_HANDLE MiniportAddDeviceContext) +{ + DEBUG_ENTRY(0); +} + +static NDIS_STATUS ParaNdis6_StartDevice(IN NDIS_HANDLE MiniportAddDeviceContext, IN PIRP Irp) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + DEBUG_ENTRY(0); + return status; +} + +typedef struct _tagRRLData +{ + PIO_RESOURCE_REQUIREMENTS_LIST prrl; + PIO_RESOURCE_LIST currentList; + PIO_RESOURCE_DESCRIPTOR currentDesc; +}tRRLData; + +/****************************************************************** +Replacement of resource requirement list: initialize the new list +*******************************************************************/ +static void InitializeNewResourceRequirementsList( + tRRLData *pData, + PIO_RESOURCE_REQUIREMENTS_LIST newList, + PIO_RESOURCE_REQUIREMENTS_LIST oldList) +{ + pData->prrl = newList; + pData->currentList = NULL; + pData->currentDesc = NULL; + if (pData->prrl) + { + ULONG len = RtlPointerToOffset(pData->prrl, &pData->prrl->List[0]); + RtlCopyMemory(newList, oldList, len); + newList->ListSize = len; + newList->AlternativeLists = 0; + } +} + +/****************************************************************** +Replacement of resource requirement list: adding new resource list +to existing resource requirement list +*******************************************************************/ +static void AddNewResourceList(tRRLData *pData, PIO_RESOURCE_LIST pior) +{ + if (pData->prrl) + { + ULONG len = RtlPointerToOffset(pior, &pior->Descriptors[0]); + pData->currentList = (PIO_RESOURCE_LIST)RtlOffsetToPointer(pData->prrl, pData->prrl->ListSize); + RtlCopyMemory(pData->currentList, pior, len); + pData->currentList->Count = 0; + pData->prrl->ListSize += len; + pData->prrl->AlternativeLists++; + pData->currentDesc = &pData->currentList->Descriptors[0]; + } +} + +/****************************************************************** +Replacement of resource requirement list: done with new resource list, +verify if it contains all the required resources +*******************************************************************/ +static void FinalizeResourceList(tRRLData *pData) +{ + if (pData->prrl && pData->currentList) + { + BOOLEAN bFound = FALSE; + ULONG len = RtlPointerToOffset(pData->currentList, &pData->currentList->Descriptors[0]); + UINT i; + for (i = 0; i < pData->currentList->Count && !bFound; ++i) + { + len += sizeof(IO_RESOURCE_DESCRIPTOR); + if (pData->currentList->Descriptors[i].Type == CmResourceTypeInterrupt) bFound = TRUE; + } + if (!bFound) + { + pData->prrl->AlternativeLists--; + pData->prrl->ListSize -= len; + } + } +} +/****************************************************************** +Replacement of resource requirement list: adding new resource descriptor +to current resource list +*******************************************************************/ +static void AddNewResourceDescriptor(tRRLData *pData, PIO_RESOURCE_DESCRIPTOR prd) +{ + if (pData->prrl && pData->currentList && pData->currentDesc) + { + *(pData->currentDesc) = *prd; + pData->currentList->Count++; + pData->prrl->ListSize += sizeof(IO_RESOURCE_DESCRIPTOR); + pData->currentDesc++; + } +} + + +/****************************************************************** +Replacement of resource requirement list, when needed: +The procedure traverses over all the resource lists in existing resource requirement list +(we receive it in IRP information field). +When the driver is not built to work with MSI resources, we must remove them from the +resource requirement list, otherwise the driver will fail to initialize +Typically MSI interrupts are labeled as preferred ones, when line interrupts are labeled as +alternative resources. Removing message interrupts, remove also "alternative" label from line interrupts. +*******************************************************************/ +static PIO_RESOURCE_REQUIREMENTS_LIST ParseFilterResourceIrp( + IN NDIS_HANDLE MiniportAddDeviceContext, + PIO_RESOURCE_REQUIREMENTS_LIST prrl, + BOOLEAN bRemoveMSIResources) +{ + tRRLData newRRLData; + PIO_RESOURCE_REQUIREMENTS_LIST newPrrl = NULL; + DPrintf(0, ("[%s]%s", __FUNCTION__, bRemoveMSIResources ? "(Remove MSI resources...)" : "")); + if (MiniportAddDeviceContext && prrl) newPrrl = (PIO_RESOURCE_REQUIREMENTS_LIST)NdisAllocateMemoryWithTagPriority( + MiniportAddDeviceContext, + prrl->ListSize, + PARANDIS_MEMORY_TAG, + NormalPoolPriority); + InitializeNewResourceRequirementsList(&newRRLData, newPrrl, prrl); + if (prrl) + { + ULONG n, offset; + PVOID p = &prrl->List[0]; + DPrintf(0, ("[%s] %d bytes, %d lists", __FUNCTION__, prrl->ListSize, prrl->AlternativeLists)); + offset = RtlPointerToOffset(prrl, p); + for (n = 0; n < prrl->AlternativeLists && offset < prrl->ListSize; ++n) + { + ULONG nDesc; + IO_RESOURCE_LIST *pior = (IO_RESOURCE_LIST *)p; + if ((offset + sizeof(*pior)) < prrl->ListSize) + { + IO_RESOURCE_DESCRIPTOR *pd = &pior->Descriptors[0]; + DPrintf(0, ("[%s]+%d %d:%d descriptors follow", __FUNCTION__, offset, n, pior->Count)); + offset += RtlPointerToOffset(p, pd); + AddNewResourceList(&newRRLData, pior); + for (nDesc = 0; nDesc < pior->Count; ++nDesc) + { + BOOLEAN bRemove = FALSE; + if ((offset + sizeof(*pd)) <= prrl->ListSize) + { + DPrintf(0, ("[%s]+%d %d: type %d, flags %X, option %X", + __FUNCTION__, offset, nDesc, pd->Type, pd->Flags, pd->Option)); + if (pd->Type == CmResourceTypeInterrupt) + { + if (pd->Flags & CM_RESOURCE_INTERRUPT_MESSAGE) + { + bRemove = bRemoveMSIResources; + } + else + { + // reset IO_RESOURCE_ALTERNATIVE attribute on Line Interrupt, + // if we remove MSI vectors, otherwise Windows will not allocate it for the device + if (bRemoveMSIResources && (pd->Option & IO_RESOURCE_ALTERNATIVE)) + { + pd->Option &= ~IO_RESOURCE_ALTERNATIVE; + } + } + } + if (!bRemove) AddNewResourceDescriptor(&newRRLData, pd); + } + offset += sizeof(*pd); + pd = (IO_RESOURCE_DESCRIPTOR *)RtlOffsetToPointer(prrl, offset); + } + FinalizeResourceList(&newRRLData); + p = pd; + } + } + } + + return newPrrl; +} + +/****************************************************************** +Registered procedure for filtering if resource requirement +Required when the device supports MSI, but the driver decides - will it work with MSI or not +(currently MSI is supported but does not work, exactly our case). +In this case the resource requirement list must be replaced - we need to remove from it +all the "message interrupt" resources. + +When we are ready to work with MSI (VIRTIO_USE_MSIX_INTERRUPT is DEFINED), +we just enumerate allocated resources and do not modify them. +*******************************************************************/ +static NDIS_STATUS ParaNdis6_FilterResource(IN NDIS_HANDLE MiniportAddDeviceContext, IN PIRP Irp) +{ + BOOLEAN bRemoveMSI = FALSE; + PIO_RESOURCE_REQUIREMENTS_LIST prrl = (PIO_RESOURCE_REQUIREMENTS_LIST)(PVOID)Irp->IoStatus.Information; +#if !defined(VIRTIO_USE_MSIX_INTERRUPT) + bRemoveMSI = TRUE; +#endif + if (bRemoveMSI) + { + // traverse the resource requirements list, clean up all the MSI resources + PIO_RESOURCE_REQUIREMENTS_LIST newPrrl = ParseFilterResourceIrp(MiniportAddDeviceContext, prrl, TRUE); + if (newPrrl) + { + Irp->IoStatus.Information = (ULONG_PTR)newPrrl; + NdisFreeMemory(prrl, 0, 0); + DPrintf(0, ("[%s] Reparsing resources using new requirements...", __FUNCTION__)); + // just parse and print after MSI cleanup, this time do not remove amything and do not reallocate the list + ParseFilterResourceIrp(NULL, newPrrl, FALSE); + } + } + else + { + // just parse and print, do not remove amything and do not reallocate the list + ParseFilterResourceIrp(NULL, prrl, FALSE); + } + return NDIS_STATUS_SUCCESS; +} + + + +/****************************************************************************** +This procedure required when we want to be able filtering resource requirements. +ParaNdis6_AddDevice need to register context (to allow other procedures to allocate memory) +ParaNdis6_FilterResource able to replace resource requirements list if needed +ParaNdis6_RemoveDevice does not do anything if other procedures do not allocate any resources + which must be freed upon device removal +******************************************************************************/ +static NDIS_STATUS ParaNdis6_SetOptions(IN NDIS_HANDLE NdisDriverHandle, IN NDIS_HANDLE DriverContext) +{ + NDIS_STATUS status; + NDIS_MINIPORT_PNP_CHARACTERISTICS pnpChars; + pnpChars.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_PNP_CHARACTERISTICS; + pnpChars.Header.Revision = NDIS_MINIPORT_PNP_CHARACTERISTICS_REVISION_1; + pnpChars.Header.Size = NDIS_SIZEOF_MINIPORT_PNP_CHARACTERISTICS_REVISION_1; + pnpChars.MiniportAddDeviceHandler = ParaNdis6_AddDevice; + pnpChars.MiniportRemoveDeviceHandler = ParaNdis6_RemoveDevice; + pnpChars.MiniportStartDeviceHandler = ParaNdis6_StartDevice; + pnpChars.MiniportFilterResourceRequirementsHandler = ParaNdis6_FilterResource; + status = NdisSetOptionalHandlers(NdisDriverHandle, (PNDIS_DRIVER_OPTIONAL_HANDLERS)&pnpChars); + return status; +} + /********************************************************** Driver entry point: Register miniport driver @@ -754,7 +1002,7 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath chars.ResetHandlerEx = ParaNdis6_Reset; chars.ShutdownHandlerEx = ParaNdis6_AdapterShutdown; chars.DevicePnPEventNotifyHandler = ParaNdis6_DevicePnPEvent; - + chars.SetOptionsHandler = ParaNdis6_SetOptions; status = NdisMRegisterMiniportDriver( pDriverObject, pRegistryPath, diff --git a/NetKVM/wlh/ParaNdis6-Impl.c b/NetKVM/wlh/ParaNdis6-Impl.c index 7f8ec2e..d781fd8 100644 --- a/NetKVM/wlh/ParaNdis6-Impl.c +++ b/NetKVM/wlh/ParaNdis6-Impl.c @@ -178,30 +178,30 @@ VOID ParaNdis_FreePhysicalMemory( /********************************************************** NDIS-required procedure for hardware interrupt registration Parameters: - context + IN PVOID MiniportInterruptContext (actually Adapter context) ***********************************************************/ -static VOID MiniportDisableInterruptEx(IN PVOID MiniportInterruptContext) +static VOID MiniportDisableInterruptEx(IN PVOID MiniportInterruptContext) { DEBUG_ENTRY(0); - // TODO: implement + ParaNdis_VirtIOEnableInterrupt((PARANDIS_ADAPTER *)MiniportInterruptContext, FALSE); } /********************************************************** NDIS-required procedure for hardware interrupt registration Parameters: - context + IN PVOID MiniportInterruptContext (actually Adapter context) ***********************************************************/ -static VOID MiniportEnableInterruptEx(IN PVOID MiniportInterruptContext) +static VOID MiniportEnableInterruptEx(IN PVOID MiniportInterruptContext) { DEBUG_ENTRY(0); - // TODO: implement + ParaNdis_VirtIOEnableInterrupt((PARANDIS_ADAPTER *)MiniportInterruptContext, TRUE); } /********************************************************** NDIS-required procedure for hardware interrupt handling Parameters: IN PVOID MiniportInterruptContext (actually Adapter context) - OUT PBOOLEAN QueueDefaultInterruptDpc - set to TRUE for DPC spawning + OUT PBOOLEAN QueueDefaultInterruptDpc - set to TRUE for default DPC spawning OUT PULONG TargetProcessors Return value: TRUE if recognized @@ -217,9 +217,43 @@ static BOOLEAN MiniportInterrupt( b = ParaNdis_OnInterrupt(pContext, QueueDefaultInterruptDpc); *TargetProcessors = 0; pContext->ulIrqReceived += b; + return b; } +/********************************************************** +NDIS-required procedure for MSI hardware interrupt handling +Parameters: + IN PVOID MiniportInterruptContext (actually Adapter context) + IN ULONG MessageId - specific interrupt index + OUT PBOOLEAN QueueDefaultInterruptDpc - - set to TRUE for default DPC spawning + OUT PULONG TargetProcessors +Return value: + TRUE if recognized +***********************************************************/ +static BOOLEAN MiniportMSIInterrupt( + IN PVOID MiniportInterruptContext, + IN ULONG MessageId, + OUT PBOOLEAN QueueDefaultInterruptDpc, + OUT PULONG TargetProcessors + ) +{ + BOOLEAN b = MiniportInterrupt(MiniportInterruptContext, QueueDefaultInterruptDpc, TargetProcessors); + return b; +} + +/********************************************************** +Syncronize interrupt enable from DPC +Parameters: + context +***********************************************************/ +void ParaNdis_SyncInterruptEnable(PARANDIS_ADAPTER *pContext) +{ + NdisMSynchronizeWithInterruptEx(pContext->InterruptHandle, + 0, + ParaNdis_MiniportSynchronizeInterruptEnable, + pContext); +} /********************************************************** NDIS-required procedure for DPC handling @@ -238,6 +272,43 @@ static VOID MiniportInterruptDPC( ParaNdis_DPCWorkBody(pContext); } +/********************************************************** +NDIS-required procedure for MSI DPC handling +Parameters: + PVOID MiniportInterruptContext (Adapter context) + IN ULONG MessageId - specific interrupt index +***********************************************************/ +static VOID MiniportMSIInterruptDpc( + IN PVOID MiniportInterruptContext, + IN ULONG MessageId, + IN PVOID MiniportDpcContext, + IN PULONG NdisReserved1, + IN PULONG NdisReserved2 + ) +{ + PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportInterruptContext; + DPrintf(0, ("[%s] (Message %d)", __FUNCTION__, MessageId)); + ParaNdis_DPCWorkBody(pContext); +} + +static VOID MiniportDisableMSIInterrupt( + IN PVOID MiniportInterruptContext, + IN ULONG MessageId + ) +{ + DEBUG_ENTRY(0); + ParaNdis_VirtIOEnableInterrupt((PARANDIS_ADAPTER *)MiniportInterruptContext, FALSE); +} + +static VOID MiniportEnableMSIInterrupt( + IN PVOID MiniportInterruptContext, + IN ULONG MessageId + ) +{ + DEBUG_ENTRY(0); + ParaNdis_VirtIOEnableInterrupt((PARANDIS_ADAPTER *)MiniportInterruptContext, TRUE); +} + /********************************************************** NDIS required handler for run-time allocation of physical memory @@ -256,6 +327,32 @@ static VOID SharedMemAllocateCompleteHandler( } +static NDIS_STATUS SetInterruptMessage(PARANDIS_ADAPTER *pContext, UINT queueIndex, ULONG messageData) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + ULONG val = messageData - 1; + switch (queueIndex) + { + case 0: case 1: // queue interrupt: + WriteVirtIODeviceWord(pContext->IODevice.addr + VIRTIO_PCI_QUEUE_SEL, (u16)queueIndex); + WriteVirtIODeviceWord(pContext->IODevice.addr + VIRTIO_PCI_CONFIG + 2, (u16)messageData); + val = ReadVirtIODeviceWord(pContext->IODevice.addr + VIRTIO_PCI_CONFIG + 2); + break; + case 2: // config interrupt + WriteVirtIODeviceWord(pContext->IODevice.addr + VIRTIO_PCI_CONFIG, (u16)messageData); + val = ReadVirtIODeviceWord(pContext->IODevice.addr + VIRTIO_PCI_CONFIG); + break; + default: + break; + } + if (val != messageData) + { + DPrintf(0, ("[%s] ERROR: Wrong MSI-X message for q%d(w%X,r%X)!", __FUNCTION__, queueIndex, messageData, val)); + status = NDIS_STATUS_DEVICE_FAILED; + } + return status; +} + /********************************************************** NDIS6-related final initialization: Installing interrupt handler @@ -276,12 +373,22 @@ NDIS_STATUS ParaNdis_FinishSpecificInitialization(PARANDIS_ADAPTER *pContext) NdisZeroMemory(&mic, sizeof(mic)); mic.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_INTERRUPT; mic.Header.Revision = NDIS_MINIPORT_INTERRUPT_REVISION_1; - mic.Header.Size = sizeof(mic); + mic.Header.Size = NDIS_SIZEOF_MINIPORT_INTERRUPT_CHARACTERISTICS_REVISION_1; mic.DisableInterruptHandler = MiniportDisableInterruptEx; mic.EnableInterruptHandler = MiniportEnableInterruptEx; mic.InterruptDpcHandler = MiniportInterruptDPC; mic.InterruptHandler = MiniportInterrupt; - +#ifdef VIRTIO_USE_MSIX_INTERRUPT + if (pContext->bUsingMSIX) + { + mic.MsiSupported = TRUE; + mic.MsiSyncWithAllMessages = TRUE; + mic.EnableMessageInterruptHandler = MiniportEnableMSIInterrupt; + mic.DisableMessageInterruptHandler = MiniportDisableMSIInterrupt; + mic.MessageInterruptHandler = MiniportMSIInterrupt; + mic.MessageInterruptDpcHandler = MiniportMSIInterruptDpc; + } +#endif PoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; PoolParams.Header.Size = sizeof(PoolParams); PoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; @@ -325,6 +432,38 @@ NDIS_STATUS ParaNdis_FinishSpecificInitialization(PARANDIS_ADAPTER *pContext) if (status == NDIS_STATUS_SUCCESS) { + if (NDIS_CONNECT_MESSAGE_BASED == mic.InterruptType) + { + UINT i; + DPrintf(0, ("[%s] Using MSIX interrupts (%d messages, irql %d)", + __FUNCTION__, mic.MessageInfoTable->MessageCount, mic.MessageInfoTable->UnifiedIrql)); + for (i = 0; mic.MessageInfoTable && i < mic.MessageInfoTable->MessageCount; ++i) + { + DPrintf(0, ("[%s] MSIX message%d=%08X=>%I64X", + __FUNCTION__, i, + mic.MessageInfoTable->MessageInfo[i].MessageData, + mic.MessageInfoTable->MessageInfo[i].MessageAddress)); + } + status = SetInterruptMessage(pContext, 0, 0); + if (NDIS_STATUS_SUCCESS == status) + { + i = mic.MessageInfoTable->MessageCount > 1 ? 1 : 0; + status = SetInterruptMessage(pContext, 1, i); + } + if (NDIS_STATUS_SUCCESS == status) + { + i = mic.MessageInfoTable->MessageCount > 2 ? 2 : 0; + status = SetInterruptMessage(pContext, 2, i); + } + pContext->pMSIXInfoTable = mic.MessageInfoTable; + //pContext->bDoInterruptRecovery = FALSE; + } + else if (pContext->bUsingMSIX) + { + DPrintf(0, ("[%s] ERROR: Interrupt type %d, message table %p", + __FUNCTION__, mic.InterruptType, mic.MessageInfoTable)); + pContext->bUsingMSIX = FALSE; + } ParaNdis6_ApplyOffloadPersistentConfiguration(pContext); } DEBUG_EXIT_STATUS(0, status); diff --git a/NetKVM/wlh/netkvm.inf b/NetKVM/wlh/netkvm.inf index e2abef6..45dc7f3 100644 --- a/NetKVM/wlh/netkvm.inf +++ b/NetKVM/wlh/netkvm.inf @@ -23,6 +23,18 @@ DriverPackageDisplayName = %kvmnet6.DeviceDesc% [RedHat.NT$ARCH$] %kvmnet6.DeviceDesc% = kvmnet6.ndi, PCI\VEN_1AF4&DEV_1000&SUBSYS_00011AF4 +[kvmnet6.ndi.hw] +AddReg = kvmnet6.EnableMSI + +[kvmnet6.EnableMSI] +HKR, "Interrupt Management",, 0x00000010 +HKR, "Interrupt Management\MessageSignaledInterruptProperties",, 0x00000010 +HKR, "Interrupt Management\MessageSignaledInterruptProperties", MSISupported, 0x00010001, 1 +HKR, "Interrupt Management\MessageSignaledInterruptProperties", MessageNumberLimit, 0x00010001, 4 +HKR, "Interrupt Management\Affinity Policy",, 0x00000010 +HKR, "Interrupt Management\Affinity Policy", DevicePolicy, 0x00010001, 1 +HKR, "Interrupt Management\Affinity Policy", DevicePriority, 0x00010001, 2 + [kvmnet6.ndi] Characteristics = 0x84 ; NCF_PHYSICAL | NCF_HAS_UI diff --git a/NetKVM/wxp/ParaNdis5-Impl.c b/NetKVM/wxp/ParaNdis5-Impl.c index 03c2033..9abbb98 100644 --- a/NetKVM/wxp/ParaNdis5-Impl.c +++ b/NetKVM/wxp/ParaNdis5-Impl.c @@ -1228,6 +1228,18 @@ NDIS_STATUS ParaNdis5_StopSend(PARANDIS_ADAPTER *pContext, BOOLEAN bStop, ONPAUS } /********************************************************** +Syncronize interrupt enable from DPC +Parameters: + context +***********************************************************/ +void ParaNdis_SyncInterruptEnable(PARANDIS_ADAPTER *pContext) +{ + NdisMSynchronizeWithInterrupt(&pContext->Interrupt, + ParaNdis_MiniportSynchronizeInterruptEnable, + pContext); +} + +/********************************************************** Pause or resume receive operation: Parameters: context -- To unsubscribe from this list: send the line "unsubscribe kvm-commits" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html