mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 14:30:57 +00:00
4c37757e81
CORE-15841
3017 lines
117 KiB
C
3017 lines
117 KiB
C
/*
|
|
* This file contains NDIS driver procedures, common for NDIS5 and NDIS6
|
|
*
|
|
* Copyright (c) 2008-2017 Red Hat, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met :
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and / or other materials provided with the distribution.
|
|
* 3. Neither the names of the copyright holders nor the names of their contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
#include "ndis56common.h"
|
|
|
|
#ifdef WPP_EVENT_TRACING
|
|
#include "ParaNdis-Common.tmh"
|
|
#endif
|
|
|
|
static void ReuseReceiveBufferRegular(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBuffersDescriptor);
|
|
static void ReuseReceiveBufferPowerOff(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBuffersDescriptor);
|
|
|
|
//#define ROUNDSIZE(sz) ((sz + 15) & ~15)
|
|
#define MAX_VLAN_ID 4095
|
|
|
|
#if 0
|
|
void FORCEINLINE DebugDumpPacket(LPCSTR prefix, PVOID header, int level)
|
|
{
|
|
PUCHAR peth = (PUCHAR)header;
|
|
DPrintf(level, ("[%s] %02X%02X%02X%02X%02X%02X => %02X%02X%02X%02X%02X%02X", prefix,
|
|
peth[6], peth[7], peth[8], peth[9], peth[10], peth[11],
|
|
peth[0], peth[1], peth[2], peth[3], peth[4], peth[5]));
|
|
}
|
|
#else
|
|
void FORCEINLINE DebugDumpPacket(LPCSTR prefix, PVOID header, int level)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
/**********************************************************
|
|
Validates MAC address
|
|
Valid MAC address is not broadcast, not multicast, not empty
|
|
if bLocal is set, it must be LOCAL
|
|
if not, is must be non-local or local
|
|
Parameters:
|
|
PUCHAR pcMacAddress - MAC address to validate
|
|
BOOLEAN bLocal - TRUE, if we validate locally administered address
|
|
Return value:
|
|
TRUE if valid
|
|
***********************************************************/
|
|
BOOLEAN ParaNdis_ValidateMacAddress(PUCHAR pcMacAddress, BOOLEAN bLocal)
|
|
{
|
|
BOOLEAN bLA = FALSE, bEmpty, bBroadcast, bMulticast = FALSE;
|
|
bBroadcast = ETH_IS_BROADCAST(pcMacAddress);
|
|
bLA = !bBroadcast && ETH_IS_LOCALLY_ADMINISTERED(pcMacAddress);
|
|
bMulticast = !bBroadcast && ETH_IS_MULTICAST(pcMacAddress);
|
|
bEmpty = ETH_IS_EMPTY(pcMacAddress);
|
|
return !bBroadcast && !bEmpty && !bMulticast && (!bLocal || bLA);
|
|
}
|
|
|
|
static eInspectedPacketType QueryPacketType(PVOID data)
|
|
{
|
|
if (ETH_IS_BROADCAST(data))
|
|
return iptBroadcast;
|
|
if (ETH_IS_MULTICAST(data))
|
|
return iptMulticast;
|
|
return iptUnicast;
|
|
}
|
|
|
|
typedef struct _tagConfigurationEntry
|
|
{
|
|
const char *Name;
|
|
ULONG ulValue;
|
|
ULONG ulMinimal;
|
|
ULONG ulMaximal;
|
|
}tConfigurationEntry;
|
|
|
|
typedef struct _tagConfigurationEntries
|
|
{
|
|
tConfigurationEntry isPromiscuous;
|
|
tConfigurationEntry PrioritySupport;
|
|
tConfigurationEntry ConnectRate;
|
|
tConfigurationEntry isLogEnabled;
|
|
tConfigurationEntry debugLevel;
|
|
tConfigurationEntry connectTimer;
|
|
tConfigurationEntry dpcChecker;
|
|
tConfigurationEntry TxCapacity;
|
|
tConfigurationEntry RxCapacity;
|
|
tConfigurationEntry InterruptRecovery;
|
|
tConfigurationEntry LogStatistics;
|
|
tConfigurationEntry PacketFiltering;
|
|
tConfigurationEntry ScatterGather;
|
|
tConfigurationEntry BatchReceive;
|
|
tConfigurationEntry OffloadTxChecksum;
|
|
tConfigurationEntry OffloadTxLSO;
|
|
tConfigurationEntry OffloadRxCS;
|
|
tConfigurationEntry OffloadGuestCS;
|
|
tConfigurationEntry UseSwTxChecksum;
|
|
tConfigurationEntry IPPacketsCheck;
|
|
tConfigurationEntry stdIpcsV4;
|
|
tConfigurationEntry stdTcpcsV4;
|
|
tConfigurationEntry stdTcpcsV6;
|
|
tConfigurationEntry stdUdpcsV4;
|
|
tConfigurationEntry stdUdpcsV6;
|
|
tConfigurationEntry stdLsoV1;
|
|
tConfigurationEntry stdLsoV2ip4;
|
|
tConfigurationEntry stdLsoV2ip6;
|
|
tConfigurationEntry PriorityVlanTagging;
|
|
tConfigurationEntry VlanId;
|
|
tConfigurationEntry UseMergeableBuffers;
|
|
tConfigurationEntry MTU;
|
|
tConfigurationEntry NumberOfHandledRXPackersInDPC;
|
|
tConfigurationEntry Indirect;
|
|
}tConfigurationEntries;
|
|
|
|
static const tConfigurationEntries defaultConfiguration =
|
|
{
|
|
{ "Promiscuous", 0, 0, 1 },
|
|
{ "Priority", 0, 0, 1 },
|
|
{ "ConnectRate", 100,10,10000 },
|
|
{ "DoLog", 1, 0, 1 },
|
|
{ "DebugLevel", 2, 0, 8 },
|
|
{ "ConnectTimer", 0, 0, 300000 },
|
|
{ "DpcCheck", 0, 0, 2 },
|
|
{ "TxCapacity", 1024, 16, 1024 },
|
|
{ "RxCapacity", 256, 32, 1024 },
|
|
{ "InterruptRecovery", 0, 0, 1},
|
|
{ "LogStatistics", 0, 0, 10000},
|
|
{ "PacketFilter", 1, 0, 1},
|
|
{ "Gather", 1, 0, 1},
|
|
{ "BatchReceive", 1, 0, 1},
|
|
{ "Offload.TxChecksum", 0, 0, 31},
|
|
{ "Offload.TxLSO", 0, 0, 2},
|
|
{ "Offload.RxCS", 0, 0, 31},
|
|
{ "Offload.GuestCS", 0, 0, 1},
|
|
{ "UseSwTxChecksum", 0, 0, 1 },
|
|
{ "IPPacketsCheck", 0, 0, 3 },
|
|
{ "*IPChecksumOffloadIPv4", 3, 0, 3 },
|
|
{ "*TCPChecksumOffloadIPv4",3, 0, 3 },
|
|
{ "*TCPChecksumOffloadIPv6",3, 0, 3 },
|
|
{ "*UDPChecksumOffloadIPv4",3, 0, 3 },
|
|
{ "*UDPChecksumOffloadIPv6",3, 0, 3 },
|
|
{ "*LsoV1IPv4", 1, 0, 1 },
|
|
{ "*LsoV2IPv4", 1, 0, 1 },
|
|
{ "*LsoV2IPv6", 1, 0, 1 },
|
|
{ "*PriorityVLANTag", 3, 0, 3},
|
|
{ "VlanId", 0, 0, MAX_VLAN_ID},
|
|
{ "MergeableBuf", 1, 0, 1},
|
|
{ "MTU", 1500, 500, 65500},
|
|
{ "NumberOfHandledRXPackersInDPC", MAX_RX_LOOPS, 1, 10000},
|
|
{ "Indirect", 0, 0, 2},
|
|
};
|
|
|
|
static void ParaNdis_ResetVirtIONetDevice(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
virtio_device_reset(&pContext->IODevice);
|
|
DPrintf(0, ("[%s] Done", __FUNCTION__));
|
|
/* reset all the features in the device */
|
|
pContext->ulCurrentVlansFilterSet = 0;
|
|
pContext->ullGuestFeatures = 0;
|
|
#ifdef VIRTIO_RESET_VERIFY
|
|
if (1)
|
|
{
|
|
u8 devStatus;
|
|
devStatus = virtio_get_status(&pContext->IODevice);
|
|
if (devStatus)
|
|
{
|
|
DPrintf(0, ("[%s] Device status is still %02X", __FUNCTION__, (ULONG)devStatus));
|
|
virtio_device_reset(&pContext->IODevice);
|
|
devStatus = virtio_get_status(&pContext->IODevice);
|
|
DPrintf(0, ("[%s] Device status on retry %02X", __FUNCTION__, (ULONG)devStatus));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**********************************************************
|
|
Gets integer value for specifies in pEntry->Name name
|
|
Parameters:
|
|
NDIS_HANDLE cfg previously open configuration
|
|
tConfigurationEntry *pEntry - Entry to fill value in
|
|
***********************************************************/
|
|
static void GetConfigurationEntry(NDIS_HANDLE cfg, tConfigurationEntry *pEntry)
|
|
{
|
|
NDIS_STATUS status;
|
|
const char *statusName;
|
|
NDIS_STRING name = {0};
|
|
PNDIS_CONFIGURATION_PARAMETER pParam = NULL;
|
|
NDIS_PARAMETER_TYPE ParameterType = NdisParameterInteger;
|
|
NdisInitializeString(&name, (PUCHAR)pEntry->Name);
|
|
NdisReadConfiguration(
|
|
&status,
|
|
&pParam,
|
|
cfg,
|
|
&name,
|
|
ParameterType);
|
|
if (status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
ULONG ulValue = pParam->ParameterData.IntegerData;
|
|
if (ulValue >= pEntry->ulMinimal && ulValue <= pEntry->ulMaximal)
|
|
{
|
|
pEntry->ulValue = ulValue;
|
|
statusName = "value";
|
|
}
|
|
else
|
|
{
|
|
statusName = "out of range";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
statusName = "nothing";
|
|
}
|
|
DPrintf(2, ("[%s] %s read for %s - 0x%x",
|
|
__FUNCTION__,
|
|
statusName,
|
|
pEntry->Name,
|
|
pEntry->ulValue));
|
|
if (name.Buffer) NdisFreeString(name);
|
|
}
|
|
|
|
static void DisableLSOv4Permanently(PARANDIS_ADAPTER *pContext, LPCSTR procname, LPCSTR reason)
|
|
{
|
|
if (pContext->Offload.flagsValue & osbT4Lso)
|
|
{
|
|
DPrintf(0, ("[%s] Warning: %s", procname, reason));
|
|
pContext->Offload.flagsValue &= ~osbT4Lso;
|
|
ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
static void DisableLSOv6Permanently(PARANDIS_ADAPTER *pContext, LPCSTR procname, LPCSTR reason)
|
|
{
|
|
if (pContext->Offload.flagsValue & osbT6Lso)
|
|
{
|
|
DPrintf(0, ("[%s] Warning: %s", procname, reason));
|
|
pContext->Offload.flagsValue &= ~osbT6Lso;
|
|
ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
static void DisableBothLSOPermanently(PARANDIS_ADAPTER *pContext, LPCSTR procname, LPCSTR reason)
|
|
{
|
|
if (pContext->Offload.flagsValue & (osbT4Lso | osbT6Lso))
|
|
{
|
|
DPrintf(0, ("[%s] Warning: %s", procname, reason));
|
|
pContext->Offload.flagsValue &= ~(osbT6Lso | osbT4Lso);
|
|
ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
/**********************************************************
|
|
Loads NIC parameters from adapter registry key
|
|
Parameters:
|
|
context
|
|
PUCHAR *ppNewMACAddress - pointer to hold MAC address if configured from host
|
|
***********************************************************/
|
|
static void ReadNicConfiguration(PARANDIS_ADAPTER *pContext, PUCHAR *ppNewMACAddress)
|
|
{
|
|
NDIS_HANDLE cfg;
|
|
tConfigurationEntries *pConfiguration = ParaNdis_AllocateMemory(pContext, sizeof(tConfigurationEntries));
|
|
if (pConfiguration)
|
|
{
|
|
*pConfiguration = defaultConfiguration;
|
|
cfg = ParaNdis_OpenNICConfiguration(pContext);
|
|
if (cfg)
|
|
{
|
|
GetConfigurationEntry(cfg, &pConfiguration->isLogEnabled);
|
|
GetConfigurationEntry(cfg, &pConfiguration->debugLevel);
|
|
GetConfigurationEntry(cfg, &pConfiguration->ConnectRate);
|
|
GetConfigurationEntry(cfg, &pConfiguration->PrioritySupport);
|
|
GetConfigurationEntry(cfg, &pConfiguration->isPromiscuous);
|
|
GetConfigurationEntry(cfg, &pConfiguration->TxCapacity);
|
|
GetConfigurationEntry(cfg, &pConfiguration->RxCapacity);
|
|
GetConfigurationEntry(cfg, &pConfiguration->connectTimer);
|
|
GetConfigurationEntry(cfg, &pConfiguration->dpcChecker);
|
|
GetConfigurationEntry(cfg, &pConfiguration->InterruptRecovery);
|
|
GetConfigurationEntry(cfg, &pConfiguration->LogStatistics);
|
|
GetConfigurationEntry(cfg, &pConfiguration->PacketFiltering);
|
|
GetConfigurationEntry(cfg, &pConfiguration->ScatterGather);
|
|
GetConfigurationEntry(cfg, &pConfiguration->BatchReceive);
|
|
GetConfigurationEntry(cfg, &pConfiguration->OffloadTxChecksum);
|
|
GetConfigurationEntry(cfg, &pConfiguration->OffloadTxLSO);
|
|
GetConfigurationEntry(cfg, &pConfiguration->OffloadRxCS);
|
|
GetConfigurationEntry(cfg, &pConfiguration->OffloadGuestCS);
|
|
GetConfigurationEntry(cfg, &pConfiguration->UseSwTxChecksum);
|
|
GetConfigurationEntry(cfg, &pConfiguration->IPPacketsCheck);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdIpcsV4);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdTcpcsV4);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdTcpcsV6);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdUdpcsV4);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdUdpcsV6);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdLsoV1);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdLsoV2ip4);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdLsoV2ip6);
|
|
GetConfigurationEntry(cfg, &pConfiguration->PriorityVlanTagging);
|
|
GetConfigurationEntry(cfg, &pConfiguration->VlanId);
|
|
GetConfigurationEntry(cfg, &pConfiguration->UseMergeableBuffers);
|
|
GetConfigurationEntry(cfg, &pConfiguration->MTU);
|
|
GetConfigurationEntry(cfg, &pConfiguration->NumberOfHandledRXPackersInDPC);
|
|
GetConfigurationEntry(cfg, &pConfiguration->Indirect);
|
|
|
|
#if !defined(WPP_EVENT_TRACING)
|
|
bDebugPrint = pConfiguration->isLogEnabled.ulValue;
|
|
nDebugLevel = pConfiguration->debugLevel.ulValue;
|
|
#endif
|
|
// ignoring promiscuous setting, nothing to do with it
|
|
pContext->maxFreeTxDescriptors = pConfiguration->TxCapacity.ulValue;
|
|
pContext->NetMaxReceiveBuffers = pConfiguration->RxCapacity.ulValue;
|
|
pContext->ulMilliesToConnect = pConfiguration->connectTimer.ulValue;
|
|
pContext->nEnableDPCChecker = pConfiguration->dpcChecker.ulValue;
|
|
pContext->bDoInterruptRecovery = pConfiguration->InterruptRecovery.ulValue != 0;
|
|
pContext->Limits.nPrintDiagnostic = pConfiguration->LogStatistics.ulValue;
|
|
pContext->uNumberOfHandledRXPacketsInDPC = pConfiguration->NumberOfHandledRXPackersInDPC.ulValue;
|
|
pContext->bDoSupportPriority = pConfiguration->PrioritySupport.ulValue != 0;
|
|
pContext->ulFormalLinkSpeed = pConfiguration->ConnectRate.ulValue;
|
|
pContext->ulFormalLinkSpeed *= 1000000;
|
|
pContext->bDoHwPacketFiltering = pConfiguration->PacketFiltering.ulValue != 0;
|
|
pContext->bUseScatterGather = pConfiguration->ScatterGather.ulValue != 0;
|
|
pContext->bBatchReceive = pConfiguration->BatchReceive.ulValue != 0;
|
|
pContext->bDoHardwareChecksum = pConfiguration->UseSwTxChecksum.ulValue == 0;
|
|
pContext->bDoGuestChecksumOnReceive = pConfiguration->OffloadGuestCS.ulValue != 0;
|
|
pContext->bDoIPCheckTx = pConfiguration->IPPacketsCheck.ulValue & 1;
|
|
pContext->bDoIPCheckRx = pConfiguration->IPPacketsCheck.ulValue & 2;
|
|
pContext->Offload.flagsValue = 0;
|
|
// TX caps: 1 - TCP, 2 - UDP, 4 - IP, 8 - TCPv6, 16 - UDPv6
|
|
if (pConfiguration->OffloadTxChecksum.ulValue & 1) pContext->Offload.flagsValue |= osbT4TcpChecksum | osbT4TcpOptionsChecksum;
|
|
if (pConfiguration->OffloadTxChecksum.ulValue & 2) pContext->Offload.flagsValue |= osbT4UdpChecksum;
|
|
if (pConfiguration->OffloadTxChecksum.ulValue & 4) pContext->Offload.flagsValue |= osbT4IpChecksum | osbT4IpOptionsChecksum;
|
|
if (pConfiguration->OffloadTxChecksum.ulValue & 8) pContext->Offload.flagsValue |= osbT6TcpChecksum | osbT6TcpOptionsChecksum;
|
|
if (pConfiguration->OffloadTxChecksum.ulValue & 16) pContext->Offload.flagsValue |= osbT6UdpChecksum;
|
|
if (pConfiguration->OffloadTxLSO.ulValue) pContext->Offload.flagsValue |= osbT4Lso | osbT4LsoIp | osbT4LsoTcp;
|
|
if (pConfiguration->OffloadTxLSO.ulValue > 1) pContext->Offload.flagsValue |= osbT6Lso | osbT6LsoTcpOptions;
|
|
// RX caps: 1 - TCP, 2 - UDP, 4 - IP, 8 - TCPv6, 16 - UDPv6
|
|
if (pConfiguration->OffloadRxCS.ulValue & 1) pContext->Offload.flagsValue |= osbT4RxTCPChecksum | osbT4RxTCPOptionsChecksum;
|
|
if (pConfiguration->OffloadRxCS.ulValue & 2) pContext->Offload.flagsValue |= osbT4RxUDPChecksum;
|
|
if (pConfiguration->OffloadRxCS.ulValue & 4) pContext->Offload.flagsValue |= osbT4RxIPChecksum | osbT4RxIPOptionsChecksum;
|
|
if (pConfiguration->OffloadRxCS.ulValue & 8) pContext->Offload.flagsValue |= osbT6RxTCPChecksum | osbT6RxTCPOptionsChecksum;
|
|
if (pConfiguration->OffloadRxCS.ulValue & 16) pContext->Offload.flagsValue |= osbT6RxUDPChecksum;
|
|
/* full packet size that can be configured as GSO for VIRTIO is short */
|
|
/* NDIS test fails sometimes fails on segments 50-60K */
|
|
pContext->Offload.maxPacketSize = PARANDIS_MAX_LSO_SIZE;
|
|
pContext->InitialOffloadParameters.IPv4Checksum = (UCHAR)pConfiguration->stdIpcsV4.ulValue;
|
|
pContext->InitialOffloadParameters.TCPIPv4Checksum = (UCHAR)pConfiguration->stdTcpcsV4.ulValue;
|
|
pContext->InitialOffloadParameters.TCPIPv6Checksum = (UCHAR)pConfiguration->stdTcpcsV6.ulValue;
|
|
pContext->InitialOffloadParameters.UDPIPv4Checksum = (UCHAR)pConfiguration->stdUdpcsV4.ulValue;
|
|
pContext->InitialOffloadParameters.UDPIPv6Checksum = (UCHAR)pConfiguration->stdUdpcsV6.ulValue;
|
|
pContext->InitialOffloadParameters.LsoV1 = (UCHAR)pConfiguration->stdLsoV1.ulValue;
|
|
pContext->InitialOffloadParameters.LsoV2IPv4 = (UCHAR)pConfiguration->stdLsoV2ip4.ulValue;
|
|
pContext->InitialOffloadParameters.LsoV2IPv6 = (UCHAR)pConfiguration->stdLsoV2ip6.ulValue;
|
|
pContext->ulPriorityVlanSetting = pConfiguration->PriorityVlanTagging.ulValue;
|
|
pContext->VlanId = pConfiguration->VlanId.ulValue & 0xfff;
|
|
pContext->bUseMergedBuffers = pConfiguration->UseMergeableBuffers.ulValue != 0;
|
|
pContext->MaxPacketSize.nMaxDataSize = pConfiguration->MTU.ulValue;
|
|
pContext->bUseIndirect = pConfiguration->Indirect.ulValue != 0;
|
|
if (!pContext->bDoSupportPriority)
|
|
pContext->ulPriorityVlanSetting = 0;
|
|
// if Vlan not supported
|
|
if (!IsVlanSupported(pContext))
|
|
pContext->VlanId = 0;
|
|
if (1)
|
|
{
|
|
NDIS_STATUS status;
|
|
PVOID p;
|
|
UINT len = 0;
|
|
NdisReadNetworkAddress(&status, &p, &len, cfg);
|
|
if (status == NDIS_STATUS_SUCCESS && len == sizeof(pContext->CurrentMacAddress))
|
|
{
|
|
*ppNewMACAddress = ParaNdis_AllocateMemory(pContext, sizeof(pContext->CurrentMacAddress));
|
|
if (*ppNewMACAddress)
|
|
{
|
|
NdisMoveMemory(*ppNewMACAddress, p, len);
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, ("[%s] MAC address present, but some problem also...", __FUNCTION__));
|
|
}
|
|
}
|
|
else if (len && len != sizeof(pContext->CurrentMacAddress))
|
|
{
|
|
DPrintf(0, ("[%s] MAC address has wrong length of %d", __FUNCTION__, len));
|
|
}
|
|
else
|
|
{
|
|
DPrintf(4, ("[%s] Nothing read for MAC, error %X", __FUNCTION__, status));
|
|
}
|
|
}
|
|
NdisCloseConfiguration(cfg);
|
|
}
|
|
NdisFreeMemory(pConfiguration, 0, 0);
|
|
}
|
|
}
|
|
|
|
void ParaNdis_ResetOffloadSettings(PARANDIS_ADAPTER *pContext, tOffloadSettingsFlags *pDest, PULONG from)
|
|
{
|
|
if (!pDest) pDest = &pContext->Offload.flags;
|
|
if (!from) from = &pContext->Offload.flagsValue;
|
|
|
|
pDest->fTxIPChecksum = !!(*from & osbT4IpChecksum);
|
|
pDest->fTxTCPChecksum = !!(*from & osbT4TcpChecksum);
|
|
pDest->fTxUDPChecksum = !!(*from & osbT4UdpChecksum);
|
|
pDest->fTxTCPOptions = !!(*from & osbT4TcpOptionsChecksum);
|
|
pDest->fTxIPOptions = !!(*from & osbT4IpOptionsChecksum);
|
|
|
|
pDest->fTxLso = !!(*from & osbT4Lso);
|
|
pDest->fTxLsoIP = !!(*from & osbT4LsoIp);
|
|
pDest->fTxLsoTCP = !!(*from & osbT4LsoTcp);
|
|
|
|
pDest->fRxIPChecksum = !!(*from & osbT4RxIPChecksum);
|
|
pDest->fRxIPOptions = !!(*from & osbT4RxIPOptionsChecksum);
|
|
pDest->fRxTCPChecksum = !!(*from & osbT4RxTCPChecksum);
|
|
pDest->fRxTCPOptions = !!(*from & osbT4RxTCPOptionsChecksum);
|
|
pDest->fRxUDPChecksum = !!(*from & osbT4RxUDPChecksum);
|
|
|
|
pDest->fTxTCPv6Checksum = !!(*from & osbT6TcpChecksum);
|
|
pDest->fTxTCPv6Options = !!(*from & osbT6TcpOptionsChecksum);
|
|
pDest->fTxUDPv6Checksum = !!(*from & osbT6UdpChecksum);
|
|
pDest->fTxIPv6Ext = !!(*from & osbT6IpExtChecksum);
|
|
|
|
pDest->fTxLsov6 = !!(*from & osbT6Lso);
|
|
pDest->fTxLsov6IP = !!(*from & osbT6LsoIpExt);
|
|
pDest->fTxLsov6TCP = !!(*from & osbT6LsoTcpOptions);
|
|
|
|
pDest->fRxTCPv6Checksum = !!(*from & osbT6RxTCPChecksum);
|
|
pDest->fRxTCPv6Options = !!(*from & osbT6RxTCPOptionsChecksum);
|
|
pDest->fRxUDPv6Checksum = !!(*from & osbT6RxUDPChecksum);
|
|
pDest->fRxIPv6Ext = !!(*from & osbT6RxIpExtChecksum);
|
|
}
|
|
|
|
/**********************************************************
|
|
Enumerates adapter resources and fills the structure holding them
|
|
Verifies that IO assigned and has correct size
|
|
Verifies that interrupt assigned
|
|
Parameters:
|
|
PNDIS_RESOURCE_LIST RList - list of resources, received from NDIS
|
|
tAdapterResources *pResources - structure to fill
|
|
Return value:
|
|
TRUE if everything is OK
|
|
***********************************************************/
|
|
static BOOLEAN GetAdapterResources(NDIS_HANDLE MiniportHandle, PNDIS_RESOURCE_LIST RList, tAdapterResources *pResources)
|
|
{
|
|
UINT i;
|
|
int read, bar = -1;
|
|
PCI_COMMON_HEADER pci_config;
|
|
NdisZeroMemory(pResources, sizeof(*pResources));
|
|
|
|
// read the PCI config space header
|
|
read = NdisReadPciSlotInformation(
|
|
MiniportHandle,
|
|
0 /* SlotNumber, reserved */,
|
|
0 /* Offset */,
|
|
&pci_config,
|
|
sizeof(pci_config));
|
|
if (read != sizeof(pci_config)) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < RList->Count; ++i)
|
|
{
|
|
ULONG type = RList->PartialDescriptors[i].Type;
|
|
if (type == CmResourceTypePort)
|
|
{
|
|
PHYSICAL_ADDRESS Start = RList->PartialDescriptors[i].u.Port.Start;
|
|
ULONG len = RList->PartialDescriptors[i].u.Port.Length;
|
|
DPrintf(0, ("Found IO ports at %08lX(%d)", Start.LowPart, len));
|
|
bar = virtio_get_bar_index(&pci_config, Start);
|
|
if (bar < 0) {
|
|
break;
|
|
}
|
|
pResources->PciBars[bar].BasePA = Start;
|
|
pResources->PciBars[bar].uLength = len;
|
|
pResources->PciBars[bar].bPortSpace = TRUE;
|
|
}
|
|
else if (type == CmResourceTypeMemory)
|
|
{
|
|
PHYSICAL_ADDRESS Start = RList->PartialDescriptors[i].u.Memory.Start;
|
|
ULONG len = RList->PartialDescriptors[i].u.Memory.Length;
|
|
DPrintf(0, ("Found IO memory at %08I64X(%d)", Start.QuadPart, len));
|
|
bar = virtio_get_bar_index(&pci_config, Start);
|
|
if (bar < 0) {
|
|
break;
|
|
}
|
|
pResources->PciBars[bar].BasePA = Start;
|
|
pResources->PciBars[bar].uLength = len;
|
|
pResources->PciBars[bar].bPortSpace = FALSE;
|
|
}
|
|
else if (type == CmResourceTypeInterrupt)
|
|
{
|
|
pResources->Vector = RList->PartialDescriptors[i].u.Interrupt.Vector;
|
|
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 vector %d, level %d, affinity %X, flags %X",
|
|
pResources->Vector, pResources->Level, (ULONG)pResources->Affinity, pResources->InterruptFlags));
|
|
}
|
|
}
|
|
return bar >= 0 && pResources->Vector;
|
|
}
|
|
|
|
static void DumpVirtIOFeatures(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
const struct { ULONG bitmask; const PCHAR Name; } Features[] =
|
|
{
|
|
|
|
{VIRTIO_NET_F_CSUM, "VIRTIO_NET_F_CSUM" },
|
|
{VIRTIO_NET_F_GUEST_CSUM, "VIRTIO_NET_F_GUEST_CSUM" },
|
|
{VIRTIO_NET_F_MAC, "VIRTIO_NET_F_MAC" },
|
|
{VIRTIO_NET_F_GSO, "VIRTIO_NET_F_GSO" },
|
|
{VIRTIO_NET_F_GUEST_TSO4, "VIRTIO_NET_F_GUEST_TSO4"},
|
|
{VIRTIO_NET_F_GUEST_TSO6, "VIRTIO_NET_F_GUEST_TSO6"},
|
|
{VIRTIO_NET_F_GUEST_ECN, "VIRTIO_NET_F_GUEST_ECN"},
|
|
{VIRTIO_NET_F_GUEST_UFO, "VIRTIO_NET_F_GUEST_UFO"},
|
|
{VIRTIO_NET_F_HOST_TSO4, "VIRTIO_NET_F_HOST_TSO4"},
|
|
{VIRTIO_NET_F_HOST_TSO6, "VIRTIO_NET_F_HOST_TSO6"},
|
|
{VIRTIO_NET_F_HOST_ECN, "VIRTIO_NET_F_HOST_ECN"},
|
|
{VIRTIO_NET_F_HOST_UFO, "VIRTIO_NET_F_HOST_UFO"},
|
|
{VIRTIO_NET_F_MRG_RXBUF, "VIRTIO_NET_F_MRG_RXBUF"},
|
|
{VIRTIO_NET_F_STATUS, "VIRTIO_NET_F_STATUS"},
|
|
{VIRTIO_NET_F_CTRL_VQ, "VIRTIO_NET_F_CTRL_VQ"},
|
|
{VIRTIO_NET_F_CTRL_RX, "VIRTIO_NET_F_CTRL_RX"},
|
|
{VIRTIO_NET_F_CTRL_VLAN, "VIRTIO_NET_F_CTRL_VLAN"},
|
|
{VIRTIO_NET_F_CTRL_RX_EXTRA, "VIRTIO_NET_F_CTRL_RX_EXTRA"},
|
|
{VIRTIO_RING_F_INDIRECT_DESC, "VIRTIO_RING_F_INDIRECT_DESC"},
|
|
{VIRTIO_F_VERSION_1, "VIRTIO_F_VERSION_1" },
|
|
{VIRTIO_F_ANY_LAYOUT, "VIRTIO_F_ANY_LAYOUT" },
|
|
};
|
|
UINT i;
|
|
for (i = 0; i < sizeof(Features)/sizeof(Features[0]); ++i)
|
|
{
|
|
if (VirtIODeviceGetHostFeature(pContext, Features[i].bitmask))
|
|
{
|
|
DPrintf(0, ("VirtIO Host Feature %s", Features[i].Name));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**********************************************************
|
|
Only for test. Prints out if the interrupt line is ON
|
|
Parameters:
|
|
Return value:
|
|
***********************************************************/
|
|
static void JustForCheckClearInterrupt(PARANDIS_ADAPTER *pContext, const char *Label)
|
|
{
|
|
if (pContext->bEnableInterruptChecking)
|
|
{
|
|
ULONG ulActive;
|
|
ulActive = virtio_read_isr_status(&pContext->IODevice);
|
|
if (ulActive)
|
|
{
|
|
DPrintf(0,("WARNING: Interrupt Line %d(%s)!", ulActive, Label));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**********************************************************
|
|
Prints out statistics
|
|
***********************************************************/
|
|
static void PrintStatistics(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
ULONG64 totalTxFrames =
|
|
pContext->Statistics.ifHCOutBroadcastPkts +
|
|
pContext->Statistics.ifHCOutMulticastPkts +
|
|
pContext->Statistics.ifHCOutUcastPkts;
|
|
ULONG64 totalRxFrames =
|
|
pContext->Statistics.ifHCInBroadcastPkts +
|
|
pContext->Statistics.ifHCInMulticastPkts +
|
|
pContext->Statistics.ifHCInUcastPkts;
|
|
|
|
DPrintf(0, ("[Diag!%X] RX buffers at VIRTIO %d of %d",
|
|
pContext->CurrentMacAddress[5],
|
|
pContext->NetNofReceiveBuffers,
|
|
pContext->NetMaxReceiveBuffers));
|
|
DPrintf(0, ("[Diag!] TX desc available %d/%d, buf %d/min. %d",
|
|
pContext->nofFreeTxDescriptors,
|
|
pContext->maxFreeTxDescriptors,
|
|
pContext->nofFreeHardwareBuffers,
|
|
pContext->minFreeHardwareBuffers));
|
|
pContext->minFreeHardwareBuffers = pContext->nofFreeHardwareBuffers;
|
|
if (pContext->NetTxPacketsToReturn)
|
|
{
|
|
DPrintf(0, ("[Diag!] TX packets to return %d", pContext->NetTxPacketsToReturn));
|
|
}
|
|
DPrintf(0, ("[Diag!] Bytes transmitted %I64u, received %I64u",
|
|
pContext->Statistics.ifHCOutOctets,
|
|
pContext->Statistics.ifHCInOctets));
|
|
DPrintf(0, ("[Diag!] Tx frames %I64u, CSO %d, LSO %d, indirect %d",
|
|
totalTxFrames,
|
|
pContext->extraStatistics.framesCSOffload,
|
|
pContext->extraStatistics.framesLSO,
|
|
pContext->extraStatistics.framesIndirect));
|
|
DPrintf(0, ("[Diag!] Rx frames %I64u, Rx.Pri %d, RxHwCS.OK %d, FiltOut %d",
|
|
totalRxFrames, pContext->extraStatistics.framesRxPriority,
|
|
pContext->extraStatistics.framesRxCSHwOK, pContext->extraStatistics.framesFilteredOut));
|
|
if (pContext->extraStatistics.framesRxCSHwMissedBad || pContext->extraStatistics.framesRxCSHwMissedGood)
|
|
{
|
|
DPrintf(0, ("[Diag!] RxHwCS mistakes: missed bad %d, missed good %d",
|
|
pContext->extraStatistics.framesRxCSHwMissedBad, pContext->extraStatistics.framesRxCSHwMissedGood));
|
|
}
|
|
}
|
|
|
|
static NDIS_STATUS NTStatusToNdisStatus(NTSTATUS nt_status) {
|
|
switch (nt_status) {
|
|
case STATUS_SUCCESS:
|
|
return NDIS_STATUS_SUCCESS;
|
|
case STATUS_NOT_FOUND:
|
|
case STATUS_DEVICE_NOT_CONNECTED:
|
|
return NDIS_STATUS_ADAPTER_NOT_FOUND;
|
|
case STATUS_INSUFFICIENT_RESOURCES:
|
|
return NDIS_STATUS_RESOURCES;
|
|
case STATUS_INVALID_PARAMETER:
|
|
return NDIS_STATUS_INVALID_DEVICE_REQUEST;
|
|
default:
|
|
return NDIS_STATUS_FAILURE;
|
|
}
|
|
}
|
|
|
|
static NDIS_STATUS FinalizeFeatures(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
NTSTATUS nt_status = virtio_set_features(&pContext->IODevice, pContext->ullGuestFeatures);
|
|
if (!NT_SUCCESS(nt_status)) {
|
|
DPrintf(0, ("[%s] virtio_set_features failed with %x\n", __FUNCTION__, nt_status));
|
|
}
|
|
return NTStatusToNdisStatus(nt_status);
|
|
}
|
|
|
|
/**********************************************************
|
|
Initializes the context structure
|
|
Major variables, received from NDIS on initialization, must be be set before this call
|
|
(for ex. pContext->MiniportHandle)
|
|
|
|
If this procedure fails, no need to call
|
|
ParaNdis_CleanupContext
|
|
|
|
|
|
Parameters:
|
|
Return value:
|
|
SUCCESS, if resources are OK
|
|
NDIS_STATUS_RESOURCE_CONFLICT if not
|
|
***********************************************************/
|
|
NDIS_STATUS ParaNdis_InitializeContext(
|
|
PARANDIS_ADAPTER *pContext,
|
|
PNDIS_RESOURCE_LIST pResourceList)
|
|
{
|
|
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
|
|
PUCHAR pNewMacAddress = NULL;
|
|
USHORT linkStatus = 0;
|
|
NTSTATUS nt_status;
|
|
|
|
DEBUG_ENTRY(0);
|
|
/* read first PCI IO bar*/
|
|
//ulIOAddress = ReadPCIConfiguration(miniportAdapterHandle, 0x10);
|
|
/* check this is IO and assigned */
|
|
ReadNicConfiguration(pContext, &pNewMacAddress);
|
|
if (pNewMacAddress)
|
|
{
|
|
if (ParaNdis_ValidateMacAddress(pNewMacAddress, TRUE))
|
|
{
|
|
DPrintf(0, ("[%s] WARNING: MAC address reloaded", __FUNCTION__));
|
|
NdisMoveMemory(pContext->CurrentMacAddress, pNewMacAddress, sizeof(pContext->CurrentMacAddress));
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, ("[%s] WARNING: Invalid MAC address ignored", __FUNCTION__));
|
|
}
|
|
NdisFreeMemory(pNewMacAddress, 0, 0);
|
|
}
|
|
|
|
pContext->MaxPacketSize.nMaxFullSizeOS = pContext->MaxPacketSize.nMaxDataSize + ETH_HEADER_SIZE;
|
|
pContext->MaxPacketSize.nMaxFullSizeHwTx = pContext->MaxPacketSize.nMaxFullSizeOS;
|
|
pContext->MaxPacketSize.nMaxFullSizeHwRx = pContext->MaxPacketSize.nMaxFullSizeOS + ETH_PRIORITY_HEADER_SIZE;
|
|
if (pContext->ulPriorityVlanSetting)
|
|
pContext->MaxPacketSize.nMaxFullSizeHwTx = pContext->MaxPacketSize.nMaxFullSizeHwRx;
|
|
|
|
if (GetAdapterResources(pContext->MiniportHandle, pResourceList, &pContext->AdapterResources))
|
|
{
|
|
if (pContext->AdapterResources.InterruptFlags & CM_RESOURCE_INTERRUPT_MESSAGE)
|
|
{
|
|
DPrintf(0, ("[%s] Message interrupt assigned", __FUNCTION__));
|
|
pContext->bUsingMSIX = TRUE;
|
|
}
|
|
|
|
nt_status = virtio_device_initialize(
|
|
&pContext->IODevice,
|
|
&ParaNdisSystemOps,
|
|
pContext,
|
|
pContext->bUsingMSIX);
|
|
if (!NT_SUCCESS(nt_status)) {
|
|
DPrintf(0, ("[%s] virtio_device_initialize failed with %x\n", __FUNCTION__, nt_status));
|
|
status = NTStatusToNdisStatus(nt_status);
|
|
DEBUG_EXIT_STATUS(0, status);
|
|
return status;
|
|
}
|
|
|
|
pContext->bIODeviceInitialized = TRUE;
|
|
JustForCheckClearInterrupt(pContext, "init 0");
|
|
ParaNdis_ResetVirtIONetDevice(pContext);
|
|
JustForCheckClearInterrupt(pContext, "init 1");
|
|
virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_ACKNOWLEDGE);
|
|
JustForCheckClearInterrupt(pContext, "init 2");
|
|
virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_DRIVER);
|
|
pContext->ullHostFeatures = virtio_get_features(&pContext->IODevice);
|
|
DumpVirtIOFeatures(pContext);
|
|
JustForCheckClearInterrupt(pContext, "init 3");
|
|
pContext->bLinkDetectSupported = 0 != VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_STATUS);
|
|
|
|
if(pContext->bLinkDetectSupported) {
|
|
virtio_get_config(&pContext->IODevice, sizeof(pContext->CurrentMacAddress), &linkStatus, sizeof(linkStatus));
|
|
pContext->bConnected = (linkStatus & VIRTIO_NET_S_LINK_UP) != 0;
|
|
DPrintf(0, ("[%s] Link status on driver startup: %d", __FUNCTION__, pContext->bConnected));
|
|
}
|
|
|
|
if (VirtIODeviceGetHostFeature(pContext, VIRTIO_F_VERSION_1))
|
|
{
|
|
// virtio 1.0 always uses the extended header
|
|
pContext->nVirtioHeaderSize = sizeof(virtio_net_hdr_ext);
|
|
VirtIODeviceEnableGuestFeature(pContext, VIRTIO_F_VERSION_1);
|
|
}
|
|
else
|
|
{
|
|
pContext->nVirtioHeaderSize = sizeof(virtio_net_hdr_basic);
|
|
}
|
|
|
|
if (VirtIODeviceGetHostFeature(pContext, VIRTIO_F_ANY_LAYOUT))
|
|
{
|
|
VirtIODeviceEnableGuestFeature(pContext, VIRTIO_F_ANY_LAYOUT);
|
|
}
|
|
if (VirtIODeviceGetHostFeature(pContext, VIRTIO_RING_F_EVENT_IDX))
|
|
{
|
|
VirtIODeviceEnableGuestFeature(pContext, VIRTIO_RING_F_EVENT_IDX);
|
|
}
|
|
|
|
if (!pContext->bUseMergedBuffers && VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_MRG_RXBUF))
|
|
{
|
|
DPrintf(0, ("[%s] Not using mergeable buffers", __FUNCTION__));
|
|
}
|
|
else
|
|
{
|
|
pContext->bUseMergedBuffers = VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_MRG_RXBUF) != 0;
|
|
if (pContext->bUseMergedBuffers)
|
|
{
|
|
pContext->nVirtioHeaderSize = sizeof(virtio_net_hdr_ext);
|
|
VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_MRG_RXBUF);
|
|
}
|
|
}
|
|
if (VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_MAC))
|
|
{
|
|
virtio_get_config(
|
|
&pContext->IODevice,
|
|
0, // + offsetof(struct virtio_net_config, mac)
|
|
&pContext->PermanentMacAddress,
|
|
ETH_LENGTH_OF_ADDRESS);
|
|
if (!ParaNdis_ValidateMacAddress(pContext->PermanentMacAddress, FALSE))
|
|
{
|
|
DPrintf(0,("Invalid device MAC ignored(%02x-%02x-%02x-%02x-%02x-%02x)",
|
|
pContext->PermanentMacAddress[0],
|
|
pContext->PermanentMacAddress[1],
|
|
pContext->PermanentMacAddress[2],
|
|
pContext->PermanentMacAddress[3],
|
|
pContext->PermanentMacAddress[4],
|
|
pContext->PermanentMacAddress[5]));
|
|
NdisZeroMemory(pContext->PermanentMacAddress, sizeof(pContext->PermanentMacAddress));
|
|
}
|
|
}
|
|
|
|
if (ETH_IS_EMPTY(pContext->PermanentMacAddress))
|
|
{
|
|
DPrintf(0, ("No device MAC present, use default"));
|
|
pContext->PermanentMacAddress[0] = 0x02;
|
|
pContext->PermanentMacAddress[1] = 0x50;
|
|
pContext->PermanentMacAddress[2] = 0xF2;
|
|
pContext->PermanentMacAddress[3] = 0x00;
|
|
pContext->PermanentMacAddress[4] = 0x01;
|
|
pContext->PermanentMacAddress[5] = 0x80 | (UCHAR)(pContext->ulUniqueID & 0xFF);
|
|
}
|
|
DPrintf(0,("Device MAC = %02x-%02x-%02x-%02x-%02x-%02x",
|
|
pContext->PermanentMacAddress[0],
|
|
pContext->PermanentMacAddress[1],
|
|
pContext->PermanentMacAddress[2],
|
|
pContext->PermanentMacAddress[3],
|
|
pContext->PermanentMacAddress[4],
|
|
pContext->PermanentMacAddress[5]));
|
|
|
|
if (ETH_IS_EMPTY(pContext->CurrentMacAddress))
|
|
{
|
|
NdisMoveMemory(
|
|
&pContext->CurrentMacAddress,
|
|
&pContext->PermanentMacAddress,
|
|
ETH_LENGTH_OF_ADDRESS);
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0,("Current MAC = %02x-%02x-%02x-%02x-%02x-%02x",
|
|
pContext->CurrentMacAddress[0],
|
|
pContext->CurrentMacAddress[1],
|
|
pContext->CurrentMacAddress[2],
|
|
pContext->CurrentMacAddress[3],
|
|
pContext->CurrentMacAddress[4],
|
|
pContext->CurrentMacAddress[5]));
|
|
}
|
|
if (VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_CTRL_VQ)) {
|
|
pContext->bHasControlQueue = TRUE;
|
|
VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_CTRL_VQ);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, ("[%s] Error: Incomplete resources", __FUNCTION__));
|
|
status = NDIS_STATUS_RESOURCE_CONFLICT;
|
|
}
|
|
|
|
|
|
if (pContext->bDoHardwareChecksum)
|
|
{
|
|
ULONG dependentOptions;
|
|
dependentOptions = osbT4TcpChecksum | osbT4UdpChecksum | osbT4TcpOptionsChecksum;
|
|
if (!VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_CSUM) &&
|
|
(pContext->Offload.flagsValue & dependentOptions))
|
|
{
|
|
DPrintf(0, ("[%s] Host does not support CSUM, disabling CS offload", __FUNCTION__) );
|
|
pContext->Offload.flagsValue &= ~dependentOptions;
|
|
}
|
|
}
|
|
|
|
if (pContext->bDoGuestChecksumOnReceive && VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_GUEST_CSUM))
|
|
{
|
|
DPrintf(0, ("[%s] Enabling guest checksum", __FUNCTION__) );
|
|
VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_GUEST_CSUM);
|
|
}
|
|
else
|
|
{
|
|
pContext->bDoGuestChecksumOnReceive = FALSE;
|
|
}
|
|
|
|
// now, after we checked the capabilities, we can initialize current
|
|
// configuration of offload tasks
|
|
ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
|
|
if (pContext->Offload.flags.fTxLso && !pContext->bUseScatterGather)
|
|
{
|
|
DisableBothLSOPermanently(pContext, __FUNCTION__, "SG is not active");
|
|
}
|
|
if (pContext->Offload.flags.fTxLso &&
|
|
!VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_HOST_TSO4))
|
|
{
|
|
DisableLSOv4Permanently(pContext, __FUNCTION__, "Host does not support TSOv4");
|
|
}
|
|
if (pContext->Offload.flags.fTxLsov6 &&
|
|
!VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_HOST_TSO6))
|
|
{
|
|
DisableLSOv6Permanently(pContext, __FUNCTION__, "Host does not support TSOv6");
|
|
}
|
|
if (pContext->bUseIndirect)
|
|
{
|
|
const char *reason = "";
|
|
if (!VirtIODeviceGetHostFeature(pContext, VIRTIO_RING_F_INDIRECT_DESC))
|
|
{
|
|
pContext->bUseIndirect = FALSE;
|
|
reason = "Host support";
|
|
}
|
|
else if (!pContext->bUseScatterGather)
|
|
{
|
|
pContext->bUseIndirect = FALSE;
|
|
reason = "SG";
|
|
}
|
|
DPrintf(0, ("[%s] %sable indirect Tx(!%s)", __FUNCTION__, pContext->bUseIndirect ? "En" : "Dis", reason) );
|
|
}
|
|
|
|
if (VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_CTRL_RX_EXTRA) &&
|
|
pContext->bDoHwPacketFiltering)
|
|
{
|
|
DPrintf(0, ("[%s] Using hardware packet filtering", __FUNCTION__));
|
|
pContext->bHasHardwareFilters = TRUE;
|
|
}
|
|
|
|
status = FinalizeFeatures(pContext);
|
|
|
|
pContext->ReuseBufferProc = (tReuseReceiveBufferProc)ReuseReceiveBufferRegular;
|
|
|
|
NdisInitializeEvent(&pContext->ResetEvent);
|
|
DEBUG_EXIT_STATUS(0, status);
|
|
return status;
|
|
}
|
|
|
|
/**********************************************************
|
|
Free the resources allocated for VirtIO buffer descriptor
|
|
Parameters:
|
|
PVOID pParam pIONetDescriptor to free
|
|
BOOLEAN bRemoveFromList TRUE, if also remove it from list
|
|
***********************************************************/
|
|
static void VirtIONetFreeBufferDescriptor(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBufferDescriptor)
|
|
{
|
|
if(pBufferDescriptor)
|
|
{
|
|
if (pBufferDescriptor->pHolder)
|
|
ParaNdis_UnbindBufferFromPacket(pContext, pBufferDescriptor);
|
|
if (pBufferDescriptor->DataInfo.Virtual)
|
|
ParaNdis_FreePhysicalMemory(pContext, &pBufferDescriptor->DataInfo);
|
|
if (pBufferDescriptor->HeaderInfo.Virtual)
|
|
ParaNdis_FreePhysicalMemory(pContext, &pBufferDescriptor->HeaderInfo);
|
|
NdisFreeMemory(pBufferDescriptor, 0, 0);
|
|
}
|
|
}
|
|
|
|
/**********************************************************
|
|
Free all the buffer descriptors from specified list
|
|
Parameters:
|
|
PLIST_ENTRY pListRoot list containing pIONetDescriptor structures
|
|
PNDIS_SPIN_LOCK pLock lock to protest this list
|
|
Return value:
|
|
***********************************************************/
|
|
static void FreeDescriptorsFromList(PARANDIS_ADAPTER *pContext, PLIST_ENTRY pListRoot, PNDIS_SPIN_LOCK pLock)
|
|
{
|
|
pIONetDescriptor pBufferDescriptor;
|
|
LIST_ENTRY TempList;
|
|
InitializeListHead(&TempList);
|
|
NdisAcquireSpinLock(pLock);
|
|
while(!IsListEmpty(pListRoot))
|
|
{
|
|
pBufferDescriptor = (pIONetDescriptor)RemoveHeadList(pListRoot);
|
|
InsertTailList(&TempList, &pBufferDescriptor->listEntry);
|
|
}
|
|
NdisReleaseSpinLock(pLock);
|
|
while(!IsListEmpty(&TempList))
|
|
{
|
|
pBufferDescriptor = (pIONetDescriptor)RemoveHeadList(&TempList);
|
|
VirtIONetFreeBufferDescriptor(pContext, pBufferDescriptor);
|
|
}
|
|
}
|
|
|
|
static pIONetDescriptor AllocatePairOfBuffersOnInit(
|
|
PARANDIS_ADAPTER *pContext,
|
|
ULONG size1,
|
|
ULONG size2,
|
|
BOOLEAN bForTx)
|
|
{
|
|
pIONetDescriptor p;
|
|
p = (pIONetDescriptor)ParaNdis_AllocateMemory(pContext, sizeof(*p));
|
|
if (p)
|
|
{
|
|
BOOLEAN b1 = FALSE, b2 = FALSE;
|
|
NdisZeroMemory(p, sizeof(*p));
|
|
p->HeaderInfo.size = size1;
|
|
p->DataInfo.size = size2;
|
|
p->HeaderInfo.IsCached = p->DataInfo.IsCached = 1;
|
|
p->HeaderInfo.IsTX = p->DataInfo.IsTX = bForTx;
|
|
p->nofUsedBuffers = 0;
|
|
b1 = ParaNdis_InitialAllocatePhysicalMemory(pContext, &p->HeaderInfo);
|
|
if (b1) b2 = ParaNdis_InitialAllocatePhysicalMemory(pContext, &p->DataInfo);
|
|
if (b1 && b2)
|
|
{
|
|
BOOLEAN b = bForTx || ParaNdis_BindBufferToPacket(pContext, p);
|
|
if (!b)
|
|
{
|
|
DPrintf(0, ("[INITPHYS](%s) Failed to bind memory to net packet", bForTx ? "TX" : "RX"));
|
|
VirtIONetFreeBufferDescriptor(pContext, p);
|
|
p = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (b1) ParaNdis_FreePhysicalMemory(pContext, &p->HeaderInfo);
|
|
if (b2) ParaNdis_FreePhysicalMemory(pContext, &p->DataInfo);
|
|
NdisFreeMemory(p, 0, 0);
|
|
p = NULL;
|
|
DPrintf(0, ("[INITPHYS](%s) Failed to allocate memory block", bForTx ? "TX" : "RX"));
|
|
}
|
|
}
|
|
if (p)
|
|
{
|
|
DPrintf(3, ("[INITPHYS](%s) Header v%p(p%08lX), Data v%p(p%08lX)", bForTx ? "TX" : "RX",
|
|
p->HeaderInfo.Virtual, p->HeaderInfo.Physical.LowPart,
|
|
p->DataInfo.Virtual, p->DataInfo.Physical.LowPart));
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/**********************************************************
|
|
Allocates TX buffers according to startup setting (pContext->maxFreeTxDescriptors as got from registry)
|
|
Buffers are chained in NetFreeSendBuffers
|
|
Parameters:
|
|
context
|
|
***********************************************************/
|
|
static void PrepareTransmitBuffers(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
UINT nBuffers, nMaxBuffers;
|
|
DEBUG_ENTRY(4);
|
|
nMaxBuffers = virtio_get_queue_size(pContext->NetSendQueue) / 2;
|
|
if (nMaxBuffers > pContext->maxFreeTxDescriptors) nMaxBuffers = pContext->maxFreeTxDescriptors;
|
|
|
|
for (nBuffers = 0; nBuffers < nMaxBuffers; ++nBuffers)
|
|
{
|
|
pIONetDescriptor pBuffersDescriptor =
|
|
AllocatePairOfBuffersOnInit(
|
|
pContext,
|
|
pContext->nVirtioHeaderSize,
|
|
pContext->MaxPacketSize.nMaxFullSizeHwTx,
|
|
TRUE);
|
|
if (!pBuffersDescriptor) break;
|
|
|
|
NdisZeroMemory(pBuffersDescriptor->HeaderInfo.Virtual, pBuffersDescriptor->HeaderInfo.size);
|
|
InsertTailList(&pContext->NetFreeSendBuffers, &pBuffersDescriptor->listEntry);
|
|
pContext->nofFreeTxDescriptors++;
|
|
}
|
|
|
|
pContext->maxFreeTxDescriptors = pContext->nofFreeTxDescriptors;
|
|
pContext->nofFreeHardwareBuffers = pContext->nofFreeTxDescriptors * 2;
|
|
pContext->maxFreeHardwareBuffers = pContext->minFreeHardwareBuffers = pContext->nofFreeHardwareBuffers;
|
|
DPrintf(0, ("[%s] available %d Tx descriptors, %d hw buffers",
|
|
__FUNCTION__, pContext->nofFreeTxDescriptors, pContext->nofFreeHardwareBuffers));
|
|
}
|
|
|
|
static BOOLEAN AddRxBufferToQueue(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBufferDescriptor)
|
|
{
|
|
UINT nBuffersToSubmit = 2;
|
|
struct VirtIOBufferDescriptor sg[2];
|
|
if (!pContext->bUseMergedBuffers)
|
|
{
|
|
sg[0].physAddr = pBufferDescriptor->HeaderInfo.Physical;
|
|
sg[0].length = pBufferDescriptor->HeaderInfo.size;
|
|
sg[1].physAddr = pBufferDescriptor->DataInfo.Physical;
|
|
sg[1].length = pBufferDescriptor->DataInfo.size;
|
|
}
|
|
else
|
|
{
|
|
sg[0].physAddr = pBufferDescriptor->DataInfo.Physical;
|
|
sg[0].length = pBufferDescriptor->DataInfo.size;
|
|
nBuffersToSubmit = 1;
|
|
}
|
|
return 0 <= virtqueue_add_buf(
|
|
pContext->NetReceiveQueue,
|
|
sg,
|
|
0,
|
|
nBuffersToSubmit,
|
|
pBufferDescriptor,
|
|
NULL,
|
|
0);
|
|
}
|
|
|
|
|
|
/**********************************************************
|
|
Allocates maximum RX buffers for incoming packets
|
|
Buffers are chained in NetReceiveBuffers
|
|
Parameters:
|
|
context
|
|
***********************************************************/
|
|
static int PrepareReceiveBuffers(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
int nRet = 0;
|
|
UINT i;
|
|
DEBUG_ENTRY(4);
|
|
|
|
for (i = 0; i < pContext->NetMaxReceiveBuffers; ++i)
|
|
{
|
|
ULONG size1 = pContext->bUseMergedBuffers ? 4 : pContext->nVirtioHeaderSize;
|
|
ULONG size2 = pContext->MaxPacketSize.nMaxFullSizeHwRx +
|
|
(pContext->bUseMergedBuffers ? pContext->nVirtioHeaderSize : 0);
|
|
pIONetDescriptor pBuffersDescriptor =
|
|
AllocatePairOfBuffersOnInit(pContext, size1, size2, FALSE);
|
|
if (!pBuffersDescriptor) break;
|
|
|
|
if (!AddRxBufferToQueue(pContext, pBuffersDescriptor))
|
|
{
|
|
VirtIONetFreeBufferDescriptor(pContext, pBuffersDescriptor);
|
|
break;
|
|
}
|
|
|
|
InsertTailList(&pContext->NetReceiveBuffers, &pBuffersDescriptor->listEntry);
|
|
|
|
pContext->NetNofReceiveBuffers++;
|
|
}
|
|
|
|
pContext->NetMaxReceiveBuffers = pContext->NetNofReceiveBuffers;
|
|
DPrintf(0, ("[%s] MaxReceiveBuffers %d\n", __FUNCTION__, pContext->NetMaxReceiveBuffers) );
|
|
|
|
virtqueue_kick(pContext->NetReceiveQueue);
|
|
|
|
return nRet;
|
|
}
|
|
|
|
static NDIS_STATUS FindNetQueues(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
struct virtqueue *queues[3];
|
|
unsigned nvqs = pContext->bHasControlQueue ? 3 : 2;
|
|
NTSTATUS status;
|
|
|
|
// We work with two or three virtqueues, 0 - receive, 1 - send, 2 - control
|
|
status = virtio_find_queues(
|
|
&pContext->IODevice,
|
|
nvqs,
|
|
queues);
|
|
if (!NT_SUCCESS(status)) {
|
|
DPrintf(0, ("[%s] virtio_find_queues failed with %x\n", __FUNCTION__, status));
|
|
return NTStatusToNdisStatus(status);
|
|
}
|
|
|
|
pContext->NetReceiveQueue = queues[0];
|
|
pContext->NetSendQueue = queues[1];
|
|
if (pContext->bHasControlQueue) {
|
|
pContext->NetControlQueue = queues[2];
|
|
}
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
// called on PASSIVE upon unsuccessful Init or upon Halt
|
|
static void DeleteNetQueues(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
virtio_delete_queues(&pContext->IODevice);
|
|
}
|
|
|
|
/**********************************************************
|
|
Initializes VirtIO buffering and related stuff:
|
|
Allocates RX and TX queues and buffers
|
|
Parameters:
|
|
context
|
|
Return value:
|
|
TRUE if both queues are allocated
|
|
***********************************************************/
|
|
static NDIS_STATUS ParaNdis_VirtIONetInit(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
NDIS_STATUS status;
|
|
DEBUG_ENTRY(0);
|
|
|
|
pContext->ControlData.IsCached = 1;
|
|
pContext->ControlData.size = 512;
|
|
|
|
status = FindNetQueues(pContext);
|
|
if (status != NDIS_STATUS_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
if (pContext->NetReceiveQueue && pContext->NetSendQueue)
|
|
{
|
|
PrepareTransmitBuffers(pContext);
|
|
PrepareReceiveBuffers(pContext);
|
|
|
|
if (pContext->NetControlQueue)
|
|
ParaNdis_InitialAllocatePhysicalMemory(pContext, &pContext->ControlData);
|
|
if (!pContext->NetControlQueue || !pContext->ControlData.Virtual)
|
|
{
|
|
DPrintf(0, ("[%s] The Control vQueue does not work!\n", __FUNCTION__) );
|
|
pContext->bHasHardwareFilters = FALSE;
|
|
}
|
|
if (pContext->nofFreeTxDescriptors &&
|
|
pContext->NetMaxReceiveBuffers &&
|
|
pContext->maxFreeHardwareBuffers)
|
|
{
|
|
pContext->sgTxGatherTable = ParaNdis_AllocateMemory(pContext,
|
|
pContext->maxFreeHardwareBuffers * sizeof(pContext->sgTxGatherTable[0]));
|
|
if (!pContext->sgTxGatherTable)
|
|
{
|
|
DisableBothLSOPermanently(pContext, __FUNCTION__, "Can not allocate SG table");
|
|
}
|
|
status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DeleteNetQueues(pContext);
|
|
status = NDIS_STATUS_RESOURCES;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static void VirtIODeviceRemoveStatus(VirtIODevice *vdev, u8 status)
|
|
{
|
|
virtio_set_status(
|
|
vdev,
|
|
virtio_get_status(vdev) & ~status);
|
|
}
|
|
|
|
/**********************************************************
|
|
Finishes initialization of context structure, calling also version dependent part
|
|
If this procedure failed, ParaNdis_CleanupContext must be called
|
|
Parameters:
|
|
context
|
|
Return value:
|
|
SUCCESS or some kind of failure
|
|
***********************************************************/
|
|
NDIS_STATUS ParaNdis_FinishInitialization(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
|
|
DEBUG_ENTRY(0);
|
|
|
|
NdisAllocateSpinLock(&pContext->SendLock);
|
|
#if !defined(UNIFY_LOCKS)
|
|
NdisAllocateSpinLock(&pContext->ReceiveLock);
|
|
#endif
|
|
|
|
InitializeListHead(&pContext->NetReceiveBuffers);
|
|
InitializeListHead(&pContext->NetReceiveBuffersWaiting);
|
|
InitializeListHead(&pContext->NetSendBuffersInUse);
|
|
InitializeListHead(&pContext->NetFreeSendBuffers);
|
|
|
|
status = ParaNdis_FinishSpecificInitialization(pContext);
|
|
|
|
if (status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
status = ParaNdis_VirtIONetInit(pContext);
|
|
}
|
|
|
|
pContext->Limits.nReusedRxBuffers = pContext->NetMaxReceiveBuffers / 4 + 1;
|
|
|
|
if (status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
JustForCheckClearInterrupt(pContext, "start 3");
|
|
pContext->bEnableInterruptHandlingDPC = TRUE;
|
|
ParaNdis_SetPowerState(pContext, NdisDeviceStateD0);
|
|
virtio_device_ready(&pContext->IODevice);
|
|
JustForCheckClearInterrupt(pContext, "start 4");
|
|
ParaNdis_UpdateDeviceFilters(pContext);
|
|
}
|
|
else
|
|
{
|
|
virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_FAILED);
|
|
}
|
|
DEBUG_EXIT_STATUS(0, status);
|
|
return status;
|
|
}
|
|
|
|
/**********************************************************
|
|
Releases VirtIO related resources - queues and buffers
|
|
Parameters:
|
|
context
|
|
Return value:
|
|
***********************************************************/
|
|
static void VirtIONetRelease(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
BOOLEAN b;
|
|
DEBUG_ENTRY(0);
|
|
|
|
/* list NetReceiveBuffersWaiting must be free */
|
|
do
|
|
{
|
|
NdisAcquireSpinLock(&pContext->ReceiveLock);
|
|
b = !IsListEmpty(&pContext->NetReceiveBuffersWaiting);
|
|
NdisReleaseSpinLock(&pContext->ReceiveLock);
|
|
if (b)
|
|
{
|
|
DPrintf(0, ("[%s] There are waiting buffers", __FUNCTION__));
|
|
PrintStatistics(pContext);
|
|
NdisMSleep(5000000);
|
|
}
|
|
}while (b);
|
|
|
|
DeleteNetQueues(pContext);
|
|
virtio_device_shutdown(&pContext->IODevice);
|
|
pContext->bIODeviceInitialized = FALSE;
|
|
|
|
/* intentionally commented out
|
|
FreeDescriptorsFromList(
|
|
pContext,
|
|
&pContext->NetReceiveBuffersWaiting,
|
|
&pContext->ReceiveLock);
|
|
*/
|
|
|
|
/* this can be freed, queue shut down */
|
|
FreeDescriptorsFromList(
|
|
pContext,
|
|
&pContext->NetReceiveBuffers,
|
|
&pContext->ReceiveLock);
|
|
|
|
/* this can be freed, queue shut down */
|
|
FreeDescriptorsFromList(
|
|
pContext,
|
|
&pContext->NetSendBuffersInUse,
|
|
&pContext->SendLock);
|
|
|
|
/* this can be freed, send disabled */
|
|
FreeDescriptorsFromList(
|
|
pContext,
|
|
&pContext->NetFreeSendBuffers,
|
|
&pContext->SendLock);
|
|
|
|
if (pContext->ControlData.Virtual)
|
|
ParaNdis_FreePhysicalMemory(pContext, &pContext->ControlData);
|
|
|
|
PrintStatistics(pContext);
|
|
if (pContext->sgTxGatherTable)
|
|
{
|
|
NdisFreeMemory(pContext->sgTxGatherTable, 0, 0);
|
|
}
|
|
}
|
|
|
|
static void PreventDPCServicing(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
LONG inside;;
|
|
pContext->bEnableInterruptHandlingDPC = FALSE;
|
|
do
|
|
{
|
|
inside = InterlockedIncrement(&pContext->counterDPCInside);
|
|
InterlockedDecrement(&pContext->counterDPCInside);
|
|
if (inside > 1)
|
|
{
|
|
DPrintf(0, ("[%s] waiting!", __FUNCTION__));
|
|
NdisMSleep(20000);
|
|
}
|
|
} while (inside > 1);
|
|
}
|
|
|
|
/**********************************************************
|
|
Frees all the resources allocated when the context initialized,
|
|
calling also version-dependent part
|
|
Parameters:
|
|
context
|
|
***********************************************************/
|
|
VOID ParaNdis_CleanupContext(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
UINT i;
|
|
|
|
/* disable any interrupt generation */
|
|
if (pContext->IODevice.addr)
|
|
{
|
|
//int nActive;
|
|
//nActive = virtio_read_isr_status(&pContext->IODevice);
|
|
/* back compat - remove the OK flag only in legacy mode */
|
|
VirtIODeviceRemoveStatus(&pContext->IODevice, VIRTIO_CONFIG_S_DRIVER_OK);
|
|
JustForCheckClearInterrupt(pContext, "exit 1");
|
|
//nActive += virtio_read_isr_status(&pContext->IODevice);
|
|
//nActive += virtio_read_isr_status(&pContext->IODevice);
|
|
//DPrintf(0, ("cleanup %d", nActive));
|
|
}
|
|
|
|
PreventDPCServicing(pContext);
|
|
|
|
/****************************************
|
|
ensure all the incoming packets returned,
|
|
free all the buffers and their descriptors
|
|
*****************************************/
|
|
|
|
if (pContext->bIODeviceInitialized)
|
|
{
|
|
JustForCheckClearInterrupt(pContext, "exit 2");
|
|
ParaNdis_ResetVirtIONetDevice(pContext);
|
|
JustForCheckClearInterrupt(pContext, "exit 3");
|
|
}
|
|
|
|
ParaNdis_SetPowerState(pContext, NdisDeviceStateD3);
|
|
VirtIONetRelease(pContext);
|
|
|
|
ParaNdis_FinalizeCleanup(pContext);
|
|
|
|
if (pContext->SendLock.SpinLock)
|
|
{
|
|
NdisFreeSpinLock(&pContext->SendLock);
|
|
}
|
|
|
|
#if !defined(UNIFY_LOCKS)
|
|
if (pContext->ReceiveLock.SpinLock)
|
|
{
|
|
NdisFreeSpinLock(&pContext->ReceiveLock);
|
|
}
|
|
#endif
|
|
|
|
/* free queue shared memory */
|
|
for (i = 0; i < MAX_NUM_OF_QUEUES; i++) {
|
|
if (pContext->SharedMemoryRanges[i].pBase != NULL) {
|
|
NdisMFreeSharedMemory(
|
|
pContext->MiniportHandle,
|
|
pContext->SharedMemoryRanges[i].uLength,
|
|
TRUE /* Cached */,
|
|
pContext->SharedMemoryRanges[i].pBase,
|
|
pContext->SharedMemoryRanges[i].BasePA);
|
|
pContext->SharedMemoryRanges[i].pBase = NULL;
|
|
}
|
|
}
|
|
|
|
/* unmap our port and memory IO resources */
|
|
for (i = 0; i < PCI_TYPE0_ADDRESSES; i++)
|
|
{
|
|
tBusResource *pRes = &pContext->AdapterResources.PciBars[i];
|
|
if (pRes->pBase != NULL)
|
|
{
|
|
if (pRes->bPortSpace)
|
|
{
|
|
NdisMDeregisterIoPortRange(
|
|
pContext->MiniportHandle,
|
|
pRes->BasePA.LowPart,
|
|
pRes->uLength,
|
|
pRes->pBase);
|
|
}
|
|
else
|
|
{
|
|
NdisMUnmapIoSpace(
|
|
pContext->MiniportHandle,
|
|
pRes->pBase,
|
|
pRes->uLength);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************
|
|
System shutdown handler (shutdown, restart, bugcheck)
|
|
Parameters:
|
|
context
|
|
***********************************************************/
|
|
VOID ParaNdis_OnShutdown(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
DEBUG_ENTRY(0); // this is only for kdbg :)
|
|
ParaNdis_ResetVirtIONetDevice(pContext);
|
|
}
|
|
|
|
/**********************************************************
|
|
Handles hardware interrupt
|
|
Parameters:
|
|
context
|
|
ULONG knownInterruptSources - bitmask of
|
|
Return value:
|
|
TRUE, if it is our interrupt
|
|
sets *pRunDpc to TRUE if the DPC should be fired
|
|
***********************************************************/
|
|
BOOLEAN ParaNdis_OnLegacyInterrupt(
|
|
PARANDIS_ADAPTER *pContext,
|
|
OUT BOOLEAN *pRunDpc)
|
|
{
|
|
ULONG status = virtio_read_isr_status(&pContext->IODevice);
|
|
|
|
if((status == 0) ||
|
|
(status == VIRTIO_NET_INVALID_INTERRUPT_STATUS) ||
|
|
(pContext->powerState != NdisDeviceStateD0))
|
|
{
|
|
*pRunDpc = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
PARANDIS_STORE_LAST_INTERRUPT_TIMESTAMP(pContext);
|
|
ParaNdis_VirtIODisableIrqSynchronized(pContext, isAny);
|
|
InterlockedOr(&pContext->InterruptStatus, (LONG) ((status & isControl) | isReceive | isTransmit));
|
|
*pRunDpc = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN ParaNdis_OnQueuedInterrupt(
|
|
PARANDIS_ADAPTER *pContext,
|
|
OUT BOOLEAN *pRunDpc,
|
|
ULONG knownInterruptSources)
|
|
{
|
|
struct virtqueue* _vq = ParaNdis_GetQueueForInterrupt(pContext, knownInterruptSources);
|
|
|
|
/* If interrupts for this queue disabled do nothing */
|
|
if((_vq != NULL) && !ParaNDIS_IsQueueInterruptEnabled(_vq))
|
|
{
|
|
*pRunDpc = FALSE;
|
|
}
|
|
else
|
|
{
|
|
PARANDIS_STORE_LAST_INTERRUPT_TIMESTAMP(pContext);
|
|
InterlockedOr(&pContext->InterruptStatus, (LONG)knownInterruptSources);
|
|
ParaNdis_VirtIODisableIrqSynchronized(pContext, knownInterruptSources);
|
|
*pRunDpc = TRUE;
|
|
}
|
|
|
|
return *pRunDpc;
|
|
}
|
|
|
|
|
|
/**********************************************************
|
|
It is called from Rx processing routines in regular mode of operation.
|
|
Returns received buffer back to VirtIO queue, inserting it to NetReceiveBuffers.
|
|
If needed, signals end of RX pause operation
|
|
|
|
Must be called with &pContext->ReceiveLock acquired
|
|
|
|
Parameters:
|
|
context
|
|
void *pDescriptor - pIONetDescriptor to return
|
|
***********************************************************/
|
|
void ReuseReceiveBufferRegular(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBuffersDescriptor)
|
|
{
|
|
DEBUG_ENTRY(4);
|
|
|
|
if(!pBuffersDescriptor)
|
|
return;
|
|
|
|
RemoveEntryList(&pBuffersDescriptor->listEntry);
|
|
|
|
if(AddRxBufferToQueue(pContext, pBuffersDescriptor))
|
|
{
|
|
InsertTailList(&pContext->NetReceiveBuffers, &pBuffersDescriptor->listEntry);
|
|
|
|
pContext->NetNofReceiveBuffers++;
|
|
|
|
if (pContext->NetNofReceiveBuffers > pContext->NetMaxReceiveBuffers)
|
|
{
|
|
DPrintf(0, (" Error: NetNofReceiveBuffers > NetMaxReceiveBuffers(%d>%d)",
|
|
pContext->NetNofReceiveBuffers, pContext->NetMaxReceiveBuffers));
|
|
}
|
|
|
|
if (++pContext->Counters.nReusedRxBuffers >= pContext->Limits.nReusedRxBuffers)
|
|
{
|
|
pContext->Counters.nReusedRxBuffers = 0;
|
|
virtqueue_kick_always(pContext->NetReceiveQueue);
|
|
}
|
|
|
|
if (IsListEmpty(&pContext->NetReceiveBuffersWaiting))
|
|
{
|
|
if (pContext->ReceiveState == srsPausing || pContext->ReceivePauseCompletionProc)
|
|
{
|
|
ONPAUSECOMPLETEPROC callback = pContext->ReceivePauseCompletionProc;
|
|
pContext->ReceiveState = srsDisabled;
|
|
pContext->ReceivePauseCompletionProc = NULL;
|
|
ParaNdis_DebugHistory(pContext, hopInternalReceivePause, NULL, 0, 0, 0);
|
|
if (callback) callback(pContext);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, ("FAILED TO REUSE THE BUFFER!!!!"));
|
|
VirtIONetFreeBufferDescriptor(pContext, pBuffersDescriptor);
|
|
pContext->NetMaxReceiveBuffers--;
|
|
}
|
|
}
|
|
|
|
/**********************************************************
|
|
It is called from Rx processing routines between power off and power on in non-paused mode (Win8).
|
|
Returns received buffer to NetReceiveBuffers.
|
|
All the buffers will be placed into Virtio queue during power-on procedure
|
|
|
|
Must be called with &pContext->ReceiveLock acquired
|
|
|
|
Parameters:
|
|
context
|
|
void *pDescriptor - pIONetDescriptor to return
|
|
***********************************************************/
|
|
static void ReuseReceiveBufferPowerOff(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBuffersDescriptor)
|
|
{
|
|
RemoveEntryList(&pBuffersDescriptor->listEntry);
|
|
InsertTailList(&pContext->NetReceiveBuffers, &pBuffersDescriptor->listEntry);
|
|
}
|
|
|
|
/**********************************************************
|
|
It is called from Tx processing routines
|
|
Gets all the finished buffer from VirtIO TX path and
|
|
returns them to NetFreeSendBuffers
|
|
|
|
Must be called with &pContext->SendLock acquired
|
|
|
|
Parameters:
|
|
context
|
|
Return value:
|
|
(for reference) number of TX buffers returned
|
|
***********************************************************/
|
|
UINT ParaNdis_VirtIONetReleaseTransmitBuffers(
|
|
PARANDIS_ADAPTER *pContext)
|
|
{
|
|
UINT len, i = 0;
|
|
pIONetDescriptor pBufferDescriptor;
|
|
|
|
DEBUG_ENTRY(4);
|
|
|
|
while(NULL != (pBufferDescriptor = virtqueue_get_buf(pContext->NetSendQueue, &len)))
|
|
{
|
|
RemoveEntryList(&pBufferDescriptor->listEntry);
|
|
pContext->nofFreeTxDescriptors++;
|
|
if (!pBufferDescriptor->nofUsedBuffers)
|
|
{
|
|
DPrintf(0, ("[%s] ERROR: nofUsedBuffers not set!", __FUNCTION__));
|
|
}
|
|
pContext->nofFreeHardwareBuffers += pBufferDescriptor->nofUsedBuffers;
|
|
ParaNdis_OnTransmitBufferReleased(pContext, pBufferDescriptor);
|
|
InsertTailList(&pContext->NetFreeSendBuffers, &pBufferDescriptor->listEntry);
|
|
DPrintf(3, ("[%s] Free Tx: desc %d, buff %d", __FUNCTION__, pContext->nofFreeTxDescriptors, pContext->nofFreeHardwareBuffers));
|
|
pBufferDescriptor->nofUsedBuffers = 0;
|
|
++i;
|
|
}
|
|
if (i)
|
|
{
|
|
NdisGetCurrentSystemTime(&pContext->LastTxCompletionTimeStamp);
|
|
pContext->bDoKickOnNoBuffer = TRUE;
|
|
pContext->nDetectedStoppedTx = 0;
|
|
}
|
|
DEBUG_EXIT_STATUS((i ? 3 : 5), i);
|
|
return i;
|
|
}
|
|
|
|
static ULONG FORCEINLINE QueryTcpHeaderOffset(PVOID packetData, ULONG ipHeaderOffset, ULONG ipPacketLength)
|
|
{
|
|
ULONG res;
|
|
tTcpIpPacketParsingResult ppr = ParaNdis_ReviewIPPacket(
|
|
(PUCHAR)packetData + ipHeaderOffset,
|
|
ipPacketLength,
|
|
__FUNCTION__);
|
|
if (ppr.xxpStatus == ppresXxpKnown)
|
|
{
|
|
res = ipHeaderOffset + ppr.ipHeaderSize;
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, ("[%s] ERROR: NOT a TCP or UDP packet - expected troubles!", __FUNCTION__));
|
|
res = 0;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
/*********************************************************
|
|
Called with from ProcessTx routine with TxLock held
|
|
Uses pContext->sgTxGatherTable
|
|
***********************************************************/
|
|
tCopyPacketResult ParaNdis_DoSubmitPacket(PARANDIS_ADAPTER *pContext, tTxOperationParameters *Params)
|
|
{
|
|
tCopyPacketResult result;
|
|
tMapperResult mapResult = {0,0,0};
|
|
// populating priority tag or LSO MAY require additional SG element
|
|
UINT nRequiredBuffers;
|
|
BOOLEAN bUseCopy = FALSE;
|
|
struct VirtIOBufferDescriptor *sg = pContext->sgTxGatherTable;
|
|
|
|
nRequiredBuffers = Params->nofSGFragments + 1 + ((Params->flags & (pcrPriorityTag | pcrLSO)) ? 1 : 0);
|
|
|
|
result.size = 0;
|
|
result.error = cpeOK;
|
|
if (!pContext->bUseScatterGather || // only copy available
|
|
Params->nofSGFragments == 0 || // theoretical case
|
|
!sg || // only copy available
|
|
((~Params->flags & pcrLSO) && nRequiredBuffers > pContext->maxFreeHardwareBuffers) // to many fragments and normal size of packet
|
|
)
|
|
{
|
|
nRequiredBuffers = 2;
|
|
bUseCopy = TRUE;
|
|
}
|
|
else if (pContext->bUseIndirect && !(Params->flags & pcrNoIndirect))
|
|
{
|
|
nRequiredBuffers = 1;
|
|
}
|
|
|
|
// I do not think this will help, but at least we can try freeing some buffers right now
|
|
if (pContext->nofFreeHardwareBuffers < nRequiredBuffers || !pContext->nofFreeTxDescriptors)
|
|
{
|
|
ParaNdis_VirtIONetReleaseTransmitBuffers(pContext);
|
|
}
|
|
|
|
if (nRequiredBuffers > pContext->maxFreeHardwareBuffers)
|
|
{
|
|
// LSO and too many buffers, impossible to send
|
|
result.error = cpeTooLarge;
|
|
DPrintf(0, ("[%s] ERROR: too many fragments(%d required, %d max.avail)!", __FUNCTION__,
|
|
nRequiredBuffers, pContext->maxFreeHardwareBuffers));
|
|
}
|
|
else if (pContext->nofFreeHardwareBuffers < nRequiredBuffers || !pContext->nofFreeTxDescriptors)
|
|
{
|
|
virtqueue_enable_cb_delayed(pContext->NetSendQueue);
|
|
result.error = cpeNoBuffer;
|
|
}
|
|
else if (Params->offloadMss && bUseCopy)
|
|
{
|
|
result.error = cpeInternalError;
|
|
DPrintf(0, ("[%s] ERROR: expecting SG for TSO! (%d buffers, %d bytes)", __FUNCTION__,
|
|
Params->nofSGFragments, Params->ulDataSize));
|
|
}
|
|
else if (bUseCopy)
|
|
{
|
|
result = ParaNdis_DoCopyPacketData(pContext, Params);
|
|
}
|
|
else
|
|
{
|
|
UINT nMappedBuffers;
|
|
ULONGLONG paOfIndirectArea = 0;
|
|
PVOID vaOfIndirectArea = NULL;
|
|
pIONetDescriptor pBuffersDescriptor = (pIONetDescriptor)RemoveHeadList(&pContext->NetFreeSendBuffers);
|
|
pContext->nofFreeTxDescriptors--;
|
|
NdisZeroMemory(pBuffersDescriptor->HeaderInfo.Virtual, pBuffersDescriptor->HeaderInfo.size);
|
|
sg[0].physAddr = pBuffersDescriptor->HeaderInfo.Physical;
|
|
sg[0].length = pBuffersDescriptor->HeaderInfo.size;
|
|
ParaNdis_PacketMapper(
|
|
pContext,
|
|
Params->packet,
|
|
Params->ReferenceValue,
|
|
sg + 1,
|
|
pBuffersDescriptor,
|
|
&mapResult);
|
|
nMappedBuffers = mapResult.usBuffersMapped;
|
|
if (nMappedBuffers)
|
|
{
|
|
nMappedBuffers++;
|
|
if (pContext->bUseIndirect && !(Params->flags & pcrNoIndirect))
|
|
{
|
|
ULONG space1 = (mapResult.usBufferSpaceUsed + 7) & ~7;
|
|
ULONG space2 = nMappedBuffers * SIZE_OF_SINGLE_INDIRECT_DESC;
|
|
if (pBuffersDescriptor->DataInfo.size >= (space1 + space2))
|
|
{
|
|
vaOfIndirectArea = RtlOffsetToPointer(pBuffersDescriptor->DataInfo.Virtual, space1);
|
|
paOfIndirectArea = pBuffersDescriptor->DataInfo.Physical.QuadPart + space1;
|
|
pContext->extraStatistics.framesIndirect++;
|
|
}
|
|
else if (nMappedBuffers <= pContext->nofFreeHardwareBuffers)
|
|
{
|
|
// send as is, no indirect
|
|
}
|
|
else
|
|
{
|
|
result.error = cpeNoIndirect;
|
|
DPrintf(0, ("[%s] Unexpected ERROR of placement!", __FUNCTION__));
|
|
}
|
|
}
|
|
if (result.error == cpeOK)
|
|
{
|
|
if (Params->flags & (pcrTcpChecksum | pcrUdpChecksum))
|
|
{
|
|
unsigned short addPriorityLen = (Params->flags & pcrPriorityTag) ? ETH_PRIORITY_HEADER_SIZE : 0;
|
|
if (pContext->bDoHardwareChecksum)
|
|
{
|
|
virtio_net_hdr_basic *pheader = pBuffersDescriptor->HeaderInfo.Virtual;
|
|
pheader->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
|
|
if (!Params->tcpHeaderOffset)
|
|
{
|
|
Params->tcpHeaderOffset = QueryTcpHeaderOffset(
|
|
pBuffersDescriptor->DataInfo.Virtual,
|
|
pContext->Offload.ipHeaderOffset + addPriorityLen,
|
|
mapResult.usBufferSpaceUsed - pContext->Offload.ipHeaderOffset - addPriorityLen);
|
|
}
|
|
else
|
|
{
|
|
Params->tcpHeaderOffset += addPriorityLen;
|
|
}
|
|
pheader->csum_start = (USHORT)Params->tcpHeaderOffset;
|
|
pheader->csum_offset = (Params->flags & pcrTcpChecksum) ? TCP_CHECKSUM_OFFSET : UDP_CHECKSUM_OFFSET;
|
|
}
|
|
else
|
|
{
|
|
// emulation mode - it is slow and intended only for test of flows
|
|
// and debugging of WLK test cases
|
|
PVOID pCopy = ParaNdis_AllocateMemory(pContext, Params->ulDataSize);
|
|
if (pCopy)
|
|
{
|
|
tTcpIpPacketParsingResult ppr;
|
|
// duplicate entire packet
|
|
ParaNdis_PacketCopier(Params->packet, pCopy, Params->ulDataSize, Params->ReferenceValue, FALSE);
|
|
// calculate complete TCP/UDP checksum
|
|
ppr = ParaNdis_CheckSumVerify(
|
|
RtlOffsetToPointer(pCopy, pContext->Offload.ipHeaderOffset + addPriorityLen),
|
|
Params->ulDataSize - pContext->Offload.ipHeaderOffset - addPriorityLen,
|
|
pcrAnyChecksum | pcrFixXxpChecksum,
|
|
__FUNCTION__);
|
|
// data portion in aside buffer contains complete IP+TCP header
|
|
// rewrite copy of original buffer by one new with calculated data
|
|
NdisMoveMemory(
|
|
pBuffersDescriptor->DataInfo.Virtual,
|
|
pCopy,
|
|
mapResult.usBufferSpaceUsed);
|
|
NdisFreeMemory(pCopy, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (0 <= virtqueue_add_buf(
|
|
pContext->NetSendQueue,
|
|
sg,
|
|
nMappedBuffers,
|
|
0,
|
|
pBuffersDescriptor,
|
|
vaOfIndirectArea,
|
|
paOfIndirectArea))
|
|
{
|
|
pBuffersDescriptor->nofUsedBuffers = nMappedBuffers;
|
|
pContext->nofFreeHardwareBuffers -= nMappedBuffers;
|
|
if (pContext->minFreeHardwareBuffers > pContext->nofFreeHardwareBuffers)
|
|
pContext->minFreeHardwareBuffers = pContext->nofFreeHardwareBuffers;
|
|
pBuffersDescriptor->ReferenceValue = Params->ReferenceValue;
|
|
result.size = Params->ulDataSize;
|
|
DPrintf(2, ("[%s] Submitted %d buffers (%d bytes), avail %d desc, %d bufs",
|
|
__FUNCTION__, nMappedBuffers, result.size,
|
|
pContext->nofFreeTxDescriptors, pContext->nofFreeHardwareBuffers
|
|
));
|
|
}
|
|
else
|
|
{
|
|
result.error = cpeInternalError;
|
|
DPrintf(0, ("[%s] Unexpected ERROR adding buffer to TX engine!..", __FUNCTION__));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, ("[%s] Unexpected ERROR: packet not mapped!", __FUNCTION__));
|
|
result.error = cpeInternalError;
|
|
}
|
|
|
|
if (result.error == cpeOK)
|
|
{
|
|
UCHAR ethernetHeader[sizeof(ETH_HEADER)];
|
|
eInspectedPacketType packetType;
|
|
/* get the ethernet header for review */
|
|
ParaNdis_PacketCopier(Params->packet, ethernetHeader, sizeof(ethernetHeader), Params->ReferenceValue, TRUE);
|
|
packetType = QueryPacketType(ethernetHeader);
|
|
DebugDumpPacket("sending", ethernetHeader, 3);
|
|
InsertTailList(&pContext->NetSendBuffersInUse, &pBuffersDescriptor->listEntry);
|
|
pContext->Statistics.ifHCOutOctets += result.size;
|
|
switch (packetType)
|
|
{
|
|
case iptBroadcast:
|
|
pContext->Statistics.ifHCOutBroadcastOctets += result.size;
|
|
pContext->Statistics.ifHCOutBroadcastPkts++;
|
|
break;
|
|
case iptMulticast:
|
|
pContext->Statistics.ifHCOutMulticastOctets += result.size;
|
|
pContext->Statistics.ifHCOutMulticastPkts++;
|
|
break;
|
|
default:
|
|
pContext->Statistics.ifHCOutUcastOctets += result.size;
|
|
pContext->Statistics.ifHCOutUcastPkts++;
|
|
break;
|
|
}
|
|
|
|
if (Params->flags & pcrLSO)
|
|
pContext->extraStatistics.framesLSO++;
|
|
}
|
|
else
|
|
{
|
|
pContext->nofFreeTxDescriptors++;
|
|
InsertHeadList(&pContext->NetFreeSendBuffers, &pBuffersDescriptor->listEntry);
|
|
}
|
|
}
|
|
if (result.error == cpeNoBuffer && pContext->bDoKickOnNoBuffer)
|
|
{
|
|
virtqueue_kick_always(pContext->NetSendQueue);
|
|
pContext->bDoKickOnNoBuffer = FALSE;
|
|
}
|
|
if (result.error == cpeOK)
|
|
{
|
|
if (Params->flags & (pcrTcpChecksum | pcrUdpChecksum))
|
|
pContext->extraStatistics.framesCSOffload++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/**********************************************************
|
|
It is called from Tx processing routines
|
|
Prepares the VirtIO buffer and copies to it the data from provided packet
|
|
|
|
Must be called with &pContext->SendLock acquired
|
|
|
|
Parameters:
|
|
context
|
|
tPacketType packet specific type is NDIS dependent
|
|
tCopyPacketDataFunction PacketCopier procedure for NDIS-specific type of packet
|
|
Return value:
|
|
(for reference) number of TX buffers returned
|
|
***********************************************************/
|
|
tCopyPacketResult ParaNdis_DoCopyPacketData(
|
|
PARANDIS_ADAPTER *pContext,
|
|
tTxOperationParameters *pParams)
|
|
{
|
|
tCopyPacketResult result;
|
|
tCopyPacketResult CopierResult;
|
|
struct VirtIOBufferDescriptor sg[2];
|
|
pIONetDescriptor pBuffersDescriptor = NULL;
|
|
ULONG flags = pParams->flags;
|
|
UINT nRequiredHardwareBuffers = 2;
|
|
result.size = 0;
|
|
result.error = cpeOK;
|
|
if (pContext->nofFreeHardwareBuffers < nRequiredHardwareBuffers ||
|
|
IsListEmpty(&pContext->NetFreeSendBuffers))
|
|
{
|
|
result.error = cpeNoBuffer;
|
|
}
|
|
if(result.error == cpeOK)
|
|
{
|
|
pBuffersDescriptor = (pIONetDescriptor)RemoveHeadList(&pContext->NetFreeSendBuffers);
|
|
NdisZeroMemory(pBuffersDescriptor->HeaderInfo.Virtual, pBuffersDescriptor->HeaderInfo.size);
|
|
sg[0].physAddr = pBuffersDescriptor->HeaderInfo.Physical;
|
|
sg[0].length = pBuffersDescriptor->HeaderInfo.size;
|
|
sg[1].physAddr = pBuffersDescriptor->DataInfo.Physical;
|
|
CopierResult = ParaNdis_PacketCopier(
|
|
pParams->packet,
|
|
pBuffersDescriptor->DataInfo.Virtual,
|
|
pBuffersDescriptor->DataInfo.size,
|
|
pParams->ReferenceValue,
|
|
FALSE);
|
|
sg[1].length = result.size = CopierResult.size;
|
|
// did NDIS ask us to compute CS?
|
|
if ((flags & (pcrTcpChecksum | pcrUdpChecksum | pcrIpChecksum)) != 0)
|
|
{
|
|
// we asked
|
|
unsigned short addPriorityLen = (pParams->flags & pcrPriorityTag) ? ETH_PRIORITY_HEADER_SIZE : 0;
|
|
PVOID ipPacket = RtlOffsetToPointer(
|
|
pBuffersDescriptor->DataInfo.Virtual, pContext->Offload.ipHeaderOffset + addPriorityLen);
|
|
ULONG ipPacketLength = CopierResult.size - pContext->Offload.ipHeaderOffset - addPriorityLen;
|
|
if (!pParams->tcpHeaderOffset &&
|
|
(flags & (pcrTcpChecksum | pcrUdpChecksum)) )
|
|
{
|
|
pParams->tcpHeaderOffset = QueryTcpHeaderOffset(
|
|
pBuffersDescriptor->DataInfo.Virtual,
|
|
pContext->Offload.ipHeaderOffset + addPriorityLen,
|
|
ipPacketLength);
|
|
}
|
|
else
|
|
{
|
|
pParams->tcpHeaderOffset += addPriorityLen;
|
|
}
|
|
|
|
if (pContext->bDoHardwareChecksum)
|
|
{
|
|
if (flags & (pcrTcpChecksum | pcrUdpChecksum))
|
|
{
|
|
// hardware offload
|
|
virtio_net_hdr_basic *pvnh = (virtio_net_hdr_basic *)pBuffersDescriptor->HeaderInfo.Virtual;
|
|
pvnh->csum_start = (USHORT)pParams->tcpHeaderOffset;
|
|
pvnh->csum_offset = (flags & pcrTcpChecksum) ? TCP_CHECKSUM_OFFSET : UDP_CHECKSUM_OFFSET;
|
|
pvnh->flags |= VIRTIO_NET_HDR_F_NEEDS_CSUM;
|
|
}
|
|
if (flags & (pcrIpChecksum))
|
|
{
|
|
ParaNdis_CheckSumVerify(
|
|
ipPacket,
|
|
ipPacketLength,
|
|
pcrIpChecksum | pcrFixIPChecksum,
|
|
__FUNCTION__);
|
|
}
|
|
}
|
|
else if (CopierResult.size > pContext->Offload.ipHeaderOffset)
|
|
{
|
|
ULONG csFlags = 0;
|
|
if (flags & pcrIpChecksum) csFlags |= pcrIpChecksum | pcrFixIPChecksum;
|
|
if (flags & (pcrTcpChecksum | pcrUdpChecksum)) csFlags |= pcrTcpChecksum | pcrUdpChecksum| pcrFixXxpChecksum;
|
|
// software offload
|
|
ParaNdis_CheckSumVerify(
|
|
ipPacket,
|
|
ipPacketLength,
|
|
csFlags,
|
|
__FUNCTION__);
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, ("[%s] ERROR: Invalid buffer size for offload!", __FUNCTION__));
|
|
result.size = 0;
|
|
result.error = cpeInternalError;
|
|
}
|
|
}
|
|
pContext->nofFreeTxDescriptors--;
|
|
if (result.size)
|
|
{
|
|
eInspectedPacketType packetType;
|
|
packetType = QueryPacketType(pBuffersDescriptor->DataInfo.Virtual);
|
|
DebugDumpPacket("sending", pBuffersDescriptor->DataInfo.Virtual, 3);
|
|
|
|
pBuffersDescriptor->nofUsedBuffers = nRequiredHardwareBuffers;
|
|
pContext->nofFreeHardwareBuffers -= nRequiredHardwareBuffers;
|
|
if (pContext->minFreeHardwareBuffers > pContext->nofFreeHardwareBuffers)
|
|
pContext->minFreeHardwareBuffers = pContext->nofFreeHardwareBuffers;
|
|
if (0 > virtqueue_add_buf(
|
|
pContext->NetSendQueue,
|
|
sg,
|
|
2,
|
|
0,
|
|
pBuffersDescriptor,
|
|
NULL,
|
|
0
|
|
))
|
|
{
|
|
pBuffersDescriptor->nofUsedBuffers = 0;
|
|
pContext->nofFreeHardwareBuffers += nRequiredHardwareBuffers;
|
|
result.error = cpeInternalError;
|
|
result.size = 0;
|
|
DPrintf(0, ("[%s] Unexpected ERROR adding buffer to TX engine!..", __FUNCTION__));
|
|
}
|
|
else
|
|
{
|
|
DPrintf(2, ("[%s] Submitted %d buffers (%d bytes), avail %d desc, %d bufs",
|
|
__FUNCTION__, nRequiredHardwareBuffers, result.size,
|
|
pContext->nofFreeTxDescriptors, pContext->nofFreeHardwareBuffers
|
|
));
|
|
}
|
|
if (result.error != cpeOK)
|
|
{
|
|
InsertTailList(&pContext->NetFreeSendBuffers, &pBuffersDescriptor->listEntry);
|
|
pContext->nofFreeTxDescriptors++;
|
|
}
|
|
else
|
|
{
|
|
ULONG reportedSize = pParams->ulDataSize;
|
|
pBuffersDescriptor->ReferenceValue = pParams->ReferenceValue;
|
|
InsertTailList(&pContext->NetSendBuffersInUse, &pBuffersDescriptor->listEntry);
|
|
pContext->Statistics.ifHCOutOctets += reportedSize;
|
|
switch (packetType)
|
|
{
|
|
case iptBroadcast:
|
|
pContext->Statistics.ifHCOutBroadcastOctets += reportedSize;
|
|
pContext->Statistics.ifHCOutBroadcastPkts++;
|
|
break;
|
|
case iptMulticast:
|
|
pContext->Statistics.ifHCOutMulticastOctets += reportedSize;
|
|
pContext->Statistics.ifHCOutMulticastPkts++;
|
|
break;
|
|
default:
|
|
pContext->Statistics.ifHCOutUcastOctets += reportedSize;
|
|
pContext->Statistics.ifHCOutUcastPkts++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, ("[%s] Unexpected ERROR in copying packet data! Continue...", __FUNCTION__));
|
|
InsertTailList(&pContext->NetFreeSendBuffers, &pBuffersDescriptor->listEntry);
|
|
pContext->nofFreeTxDescriptors++;
|
|
// the buffer is not copied and the callback will not be called
|
|
result.error = cpeInternalError;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ULONG ShallPassPacket(PARANDIS_ADAPTER *pContext, PVOID address, UINT len, eInspectedPacketType *pType)
|
|
{
|
|
ULONG b;
|
|
if (len <= sizeof(ETH_HEADER)) return FALSE;
|
|
if (len > pContext->MaxPacketSize.nMaxFullSizeHwRx) return FALSE;
|
|
if (len > pContext->MaxPacketSize.nMaxFullSizeOS && !ETH_HAS_PRIO_HEADER(address)) return FALSE;
|
|
*pType = QueryPacketType(address);
|
|
if (pContext->PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) return TRUE;
|
|
|
|
switch(*pType)
|
|
{
|
|
case iptBroadcast:
|
|
b = pContext->PacketFilter & NDIS_PACKET_TYPE_BROADCAST;
|
|
break;
|
|
case iptMulticast:
|
|
b = pContext->PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST;
|
|
if (!b && (pContext->PacketFilter & NDIS_PACKET_TYPE_MULTICAST))
|
|
{
|
|
UINT i, n = pContext->MulticastData.nofMulticastEntries * ETH_LENGTH_OF_ADDRESS;
|
|
b = 1;
|
|
for (i = 0; b && i < n; i += ETH_LENGTH_OF_ADDRESS)
|
|
{
|
|
ETH_COMPARE_NETWORK_ADDRESSES((PUCHAR)address, &pContext->MulticastData.MulticastList[i], &b)
|
|
}
|
|
b = !b;
|
|
}
|
|
break;
|
|
default:
|
|
ETH_COMPARE_NETWORK_ADDRESSES((PUCHAR)address, pContext->CurrentMacAddress, &b);
|
|
b = !b && (pContext->PacketFilter & NDIS_PACKET_TYPE_DIRECTED);
|
|
break;
|
|
}
|
|
if (!b)
|
|
{
|
|
pContext->extraStatistics.framesFilteredOut++;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
void
|
|
ParaNdis_PadPacketReceived(PVOID pDataBuffer, PULONG pLength)
|
|
{
|
|
// Ethernet standard declares minimal possible packet size
|
|
// Packets smaller than that must be padded before transfer
|
|
// Ethernet HW pads packets on transmit, however in our case
|
|
// some packets do not travel over Ethernet but being routed
|
|
// guest-to-guest by virtual switch.
|
|
// In this case padding is not performed and we may
|
|
// receive packet smaller than minimal allowed size. This is not
|
|
// a problem for real life scenarios however WHQL/HCK contains
|
|
// tests that check padding of received packets.
|
|
// To make these tests happy we have to pad small packets on receive
|
|
|
|
//NOTE: This function assumes that VLAN header has been already stripped out
|
|
|
|
if(*pLength < ETH_MIN_PACKET_SIZE)
|
|
{
|
|
RtlZeroMemory(RtlOffsetToPointer(pDataBuffer, *pLength), ETH_MIN_PACKET_SIZE - *pLength);
|
|
*pLength = ETH_MIN_PACKET_SIZE;
|
|
}
|
|
}
|
|
|
|
/**********************************************************
|
|
Manages RX path, calling NDIS-specific procedure for packet indication
|
|
Parameters:
|
|
context
|
|
***********************************************************/
|
|
static UINT ParaNdis_ProcessRxPath(PARANDIS_ADAPTER *pContext, ULONG ulMaxPacketsToIndicate)
|
|
{
|
|
pIONetDescriptor pBuffersDescriptor;
|
|
UINT len, headerSize = pContext->nVirtioHeaderSize;
|
|
eInspectedPacketType packetType = iptInvalid;
|
|
UINT nReceived = 0, nRetrieved = 0, nReported = 0;
|
|
tPacketIndicationType *pBatchOfPackets;
|
|
UINT maxPacketsInBatch = pContext->NetMaxReceiveBuffers;
|
|
pBatchOfPackets = pContext->bBatchReceive ?
|
|
ParaNdis_AllocateMemory(pContext, maxPacketsInBatch * sizeof(tPacketIndicationType)) : NULL;
|
|
NdisAcquireSpinLock(&pContext->ReceiveLock);
|
|
while ((nReported < ulMaxPacketsToIndicate) && NULL != (pBuffersDescriptor = virtqueue_get_buf(pContext->NetReceiveQueue, &len)))
|
|
{
|
|
PVOID pDataBuffer = RtlOffsetToPointer(pBuffersDescriptor->DataInfo.Virtual, pContext->bUseMergedBuffers ? pContext->nVirtioHeaderSize : 0);
|
|
RemoveEntryList(&pBuffersDescriptor->listEntry);
|
|
InsertTailList(&pContext->NetReceiveBuffersWaiting, &pBuffersDescriptor->listEntry);
|
|
pContext->NetNofReceiveBuffers--;
|
|
nRetrieved++;
|
|
DPrintf(2, ("[%s] retrieved header+%d b.", __FUNCTION__, len - headerSize));
|
|
DebugDumpPacket("receive", pDataBuffer, 3);
|
|
|
|
if( !pContext->bSurprizeRemoved &&
|
|
ShallPassPacket(pContext, pDataBuffer, len - headerSize, &packetType) &&
|
|
pContext->ReceiveState == srsEnabled &&
|
|
pContext->bConnected)
|
|
{
|
|
BOOLEAN b = FALSE;
|
|
ULONG length = len - headerSize;
|
|
if (!pBatchOfPackets)
|
|
{
|
|
NdisReleaseSpinLock(&pContext->ReceiveLock);
|
|
b = NULL != ParaNdis_IndicateReceivedPacket(
|
|
pContext,
|
|
pDataBuffer,
|
|
&length,
|
|
FALSE,
|
|
pBuffersDescriptor);
|
|
NdisAcquireSpinLock(&pContext->ReceiveLock);
|
|
}
|
|
else
|
|
{
|
|
tPacketIndicationType packet;
|
|
packet = ParaNdis_IndicateReceivedPacket(
|
|
pContext,
|
|
pDataBuffer,
|
|
&length,
|
|
TRUE,
|
|
pBuffersDescriptor);
|
|
b = packet != NULL;
|
|
if (b) pBatchOfPackets[nReceived] = packet;
|
|
}
|
|
if (!b)
|
|
{
|
|
pContext->ReuseBufferProc(pContext, pBuffersDescriptor);
|
|
//only possible reason for that is unexpected Vlan tag
|
|
//shall I count it as error?
|
|
pContext->Statistics.ifInErrors++;
|
|
pContext->Statistics.ifInDiscards++;
|
|
}
|
|
else
|
|
{
|
|
nReceived++;
|
|
nReported++;
|
|
pContext->Statistics.ifHCInOctets += length;
|
|
switch(packetType)
|
|
{
|
|
case iptBroadcast:
|
|
pContext->Statistics.ifHCInBroadcastPkts++;
|
|
pContext->Statistics.ifHCInBroadcastOctets += length;
|
|
break;
|
|
case iptMulticast:
|
|
pContext->Statistics.ifHCInMulticastPkts++;
|
|
pContext->Statistics.ifHCInMulticastOctets += length;
|
|
break;
|
|
default:
|
|
pContext->Statistics.ifHCInUcastPkts++;
|
|
pContext->Statistics.ifHCInUcastOctets += length;
|
|
break;
|
|
}
|
|
if (pBatchOfPackets && nReceived == maxPacketsInBatch)
|
|
{
|
|
DPrintf(1, ("[%s] received %d buffers of max %d", __FUNCTION__, nReceived, ulMaxPacketsToIndicate));
|
|
NdisReleaseSpinLock(&pContext->ReceiveLock);
|
|
ParaNdis_IndicateReceivedBatch(pContext, pBatchOfPackets, nReceived);
|
|
NdisAcquireSpinLock(&pContext->ReceiveLock);
|
|
nReceived = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// reuse packet, there is no data or the RX is suppressed
|
|
pContext->ReuseBufferProc(pContext, pBuffersDescriptor);
|
|
}
|
|
}
|
|
ParaNdis_DebugHistory(pContext, hopReceiveStat, NULL, nRetrieved, nReported, pContext->NetNofReceiveBuffers);
|
|
NdisReleaseSpinLock(&pContext->ReceiveLock);
|
|
if (nReceived && pBatchOfPackets)
|
|
{
|
|
DPrintf(1, ("[%s]%d: received %d buffers of max %d", __FUNCTION__, KeGetCurrentProcessorNumber(), nReceived, ulMaxPacketsToIndicate));
|
|
ParaNdis_IndicateReceivedBatch(pContext, pBatchOfPackets, nReceived);
|
|
}
|
|
if (pBatchOfPackets) NdisFreeMemory(pBatchOfPackets, 0, 0);
|
|
return nReported;
|
|
}
|
|
|
|
void ParaNdis_ReportLinkStatus(PARANDIS_ADAPTER *pContext, BOOLEAN bForce)
|
|
{
|
|
BOOLEAN bConnected = TRUE;
|
|
if (pContext->bLinkDetectSupported)
|
|
{
|
|
USHORT linkStatus = 0;
|
|
USHORT offset = sizeof(pContext->CurrentMacAddress);
|
|
// link changed
|
|
virtio_get_config(&pContext->IODevice, offset, &linkStatus, sizeof(linkStatus));
|
|
bConnected = (linkStatus & VIRTIO_NET_S_LINK_UP) != 0;
|
|
}
|
|
ParaNdis_IndicateConnect(pContext, bConnected, bForce);
|
|
}
|
|
|
|
static BOOLEAN RestartQueueSynchronously(tSynchronizedContext *SyncContext)
|
|
{
|
|
struct virtqueue * _vq = (struct virtqueue *) SyncContext->Parameter;
|
|
bool res = true;
|
|
if (!virtqueue_enable_cb(_vq))
|
|
{
|
|
virtqueue_disable_cb(_vq);
|
|
res = false;
|
|
}
|
|
|
|
ParaNdis_DebugHistory(SyncContext->pContext, hopDPC, (PVOID)SyncContext->Parameter, 0x20, res, 0);
|
|
return !res;
|
|
}
|
|
/**********************************************************
|
|
DPC implementation, common for both NDIS
|
|
Parameters:
|
|
context
|
|
***********************************************************/
|
|
ULONG ParaNdis_DPCWorkBody(PARANDIS_ADAPTER *pContext, ULONG ulMaxPacketsToIndicate)
|
|
{
|
|
ULONG stillRequiresProcessing = 0;
|
|
ULONG interruptSources;
|
|
UINT uIndicatedRXPackets = 0;
|
|
UINT numOfPacketsToIndicate = min(ulMaxPacketsToIndicate, pContext->uNumberOfHandledRXPacketsInDPC);
|
|
|
|
DEBUG_ENTRY(5);
|
|
if (pContext->bEnableInterruptHandlingDPC)
|
|
{
|
|
InterlockedIncrement(&pContext->counterDPCInside);
|
|
if (pContext->bEnableInterruptHandlingDPC)
|
|
{
|
|
BOOLEAN bDoKick = FALSE;
|
|
|
|
InterlockedExchange(&pContext->bDPCInactive, 0);
|
|
interruptSources = InterlockedExchange(&pContext->InterruptStatus, 0);
|
|
ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)1, interruptSources, 0, 0);
|
|
if ((interruptSources & isControl) && pContext->bLinkDetectSupported)
|
|
{
|
|
ParaNdis_ReportLinkStatus(pContext, FALSE);
|
|
}
|
|
if (interruptSources & isTransmit)
|
|
{
|
|
bDoKick = ParaNdis_ProcessTx(pContext, TRUE, TRUE);
|
|
}
|
|
if (interruptSources & isReceive)
|
|
{
|
|
int nRestartResult = 0;
|
|
|
|
do
|
|
{
|
|
LONG rxActive = InterlockedIncrement(&pContext->dpcReceiveActive);
|
|
if (rxActive == 1)
|
|
{
|
|
uIndicatedRXPackets += ParaNdis_ProcessRxPath(pContext, numOfPacketsToIndicate - uIndicatedRXPackets);
|
|
InterlockedDecrement(&pContext->dpcReceiveActive);
|
|
NdisAcquireSpinLock(&pContext->ReceiveLock);
|
|
nRestartResult = ParaNdis_SynchronizeWithInterrupt(
|
|
pContext, pContext->ulRxMessage, RestartQueueSynchronously, pContext->NetReceiveQueue);
|
|
ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)3, nRestartResult, 0, 0);
|
|
NdisReleaseSpinLock(&pContext->ReceiveLock);
|
|
DPrintf(nRestartResult ? 2 : 6, ("[%s] queue restarted%s", __FUNCTION__, nRestartResult ? "(Rerun)" : "(Done)"));
|
|
|
|
if (uIndicatedRXPackets < numOfPacketsToIndicate)
|
|
{
|
|
|
|
}
|
|
else if (uIndicatedRXPackets == numOfPacketsToIndicate)
|
|
{
|
|
DPrintf(1, ("[%s] Breaking Rx loop after %d indications", __FUNCTION__, uIndicatedRXPackets));
|
|
ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)4, nRestartResult, 0, 0);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, ("[%s] Glitch found: %d allowed, %d indicated", __FUNCTION__, numOfPacketsToIndicate, uIndicatedRXPackets));
|
|
ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)6, nRestartResult, 0, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InterlockedDecrement(&pContext->dpcReceiveActive);
|
|
if (!nRestartResult)
|
|
{
|
|
NdisAcquireSpinLock(&pContext->ReceiveLock);
|
|
nRestartResult = ParaNdis_SynchronizeWithInterrupt(
|
|
pContext, pContext->ulRxMessage, RestartQueueSynchronously, pContext->NetReceiveQueue);
|
|
ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)5, nRestartResult, 0, 0);
|
|
NdisReleaseSpinLock(&pContext->ReceiveLock);
|
|
}
|
|
DPrintf(1, ("[%s] Skip Rx processing no.%d", __FUNCTION__, rxActive));
|
|
break;
|
|
}
|
|
} while (nRestartResult);
|
|
|
|
if (nRestartResult) stillRequiresProcessing |= isReceive;
|
|
}
|
|
|
|
if (interruptSources & isTransmit)
|
|
{
|
|
NdisAcquireSpinLock(&pContext->SendLock);
|
|
if (ParaNdis_SynchronizeWithInterrupt(pContext, pContext->ulTxMessage, RestartQueueSynchronously, pContext->NetSendQueue))
|
|
stillRequiresProcessing |= isTransmit;
|
|
if(bDoKick)
|
|
{
|
|
#ifdef PARANDIS_TEST_TX_KICK_ALWAYS
|
|
virtqueue_kick_always(pContext->NetSendQueue);
|
|
#else
|
|
virtqueue_kick(pContext->NetSendQueue);
|
|
#endif
|
|
}
|
|
NdisReleaseSpinLock(&pContext->SendLock);
|
|
}
|
|
}
|
|
InterlockedDecrement(&pContext->counterDPCInside);
|
|
ParaNdis_DebugHistory(pContext, hopDPC, NULL, stillRequiresProcessing, pContext->nofFreeHardwareBuffers, pContext->nofFreeTxDescriptors);
|
|
}
|
|
return stillRequiresProcessing;
|
|
}
|
|
|
|
/**********************************************************
|
|
Periodically called procedure, checking dpc activity
|
|
If DPC are not running, it does exactly the same that the DPC
|
|
Parameters:
|
|
context
|
|
***********************************************************/
|
|
static BOOLEAN CheckRunningDpc(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
BOOLEAN bStopped;
|
|
BOOLEAN bReportHang = FALSE;
|
|
bStopped = 0 != InterlockedExchange(&pContext->bDPCInactive, TRUE);
|
|
|
|
if (bStopped)
|
|
{
|
|
pContext->nDetectedInactivity++;
|
|
if (pContext->nEnableDPCChecker)
|
|
{
|
|
if (pContext->NetTxPacketsToReturn)
|
|
{
|
|
DPrintf(0, ("[%s] - NO ACTIVITY!", __FUNCTION__));
|
|
if (!pContext->Limits.nPrintDiagnostic) PrintStatistics(pContext);
|
|
if (pContext->nEnableDPCChecker > 1)
|
|
{
|
|
int isrStatus1, isrStatus2;
|
|
isrStatus1 = virtio_read_isr_status(&pContext->IODevice);
|
|
isrStatus2 = virtio_read_isr_status(&pContext->IODevice);
|
|
if (isrStatus1 || isrStatus2)
|
|
{
|
|
DPrintf(0, ("WARNING: Interrupt status %d=>%d", isrStatus1, isrStatus2));
|
|
}
|
|
}
|
|
// simulateDPC
|
|
InterlockedOr(&pContext->InterruptStatus, isAny);
|
|
ParaNdis_DPCWorkBody(pContext, PARANDIS_UNLIMITED_PACKETS_TO_INDICATE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pContext->nDetectedInactivity = 0;
|
|
}
|
|
|
|
NdisAcquireSpinLock(&pContext->SendLock);
|
|
if (pContext->nofFreeHardwareBuffers != pContext->maxFreeHardwareBuffers)
|
|
{
|
|
if (pContext->nDetectedStoppedTx++ > 1)
|
|
{
|
|
DPrintf(0, ("[%s] - Suspicious Tx inactivity (%d)!", __FUNCTION__, pContext->nofFreeHardwareBuffers));
|
|
//bReportHang = TRUE;
|
|
#ifdef DBG_USE_VIRTIO_PCI_ISR_FOR_HOST_REPORT
|
|
WriteVirtIODeviceByte(pContext->IODevice.isr, 0);
|
|
#endif
|
|
}
|
|
}
|
|
NdisReleaseSpinLock(&pContext->SendLock);
|
|
|
|
|
|
if (pContext->Limits.nPrintDiagnostic &&
|
|
++pContext->Counters.nPrintDiagnostic >= pContext->Limits.nPrintDiagnostic)
|
|
{
|
|
pContext->Counters.nPrintDiagnostic = 0;
|
|
// todo - collect more and put out optionally
|
|
PrintStatistics(pContext);
|
|
}
|
|
|
|
if (pContext->Statistics.ifHCInOctets == pContext->Counters.prevIn)
|
|
{
|
|
pContext->Counters.nRxInactivity++;
|
|
if (pContext->Counters.nRxInactivity >= 10)
|
|
{
|
|
//#define CRASH_ON_NO_RX
|
|
#if defined(CRASH_ON_NO_RX)
|
|
ONPAUSECOMPLETEPROC proc = (ONPAUSECOMPLETEPROC)(PVOID)1;
|
|
proc(pContext);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pContext->Counters.nRxInactivity = 0;
|
|
pContext->Counters.prevIn = pContext->Statistics.ifHCInOctets;
|
|
}
|
|
return bReportHang;
|
|
}
|
|
|
|
/**********************************************************
|
|
Common implementation of periodic poll
|
|
Parameters:
|
|
context
|
|
Return:
|
|
TRUE, if reset required
|
|
***********************************************************/
|
|
BOOLEAN ParaNdis_CheckForHang(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
static int nHangOn = 0;
|
|
BOOLEAN b = nHangOn >= 3 && nHangOn < 6;
|
|
DEBUG_ENTRY(3);
|
|
b |= CheckRunningDpc(pContext);
|
|
//uncomment to cause 3 consecutive resets
|
|
//nHangOn++;
|
|
DEBUG_EXIT_STATUS(b ? 0 : 6, b);
|
|
return b;
|
|
}
|
|
|
|
/**********************************************************
|
|
Common handler of multicast address configuration
|
|
Parameters:
|
|
PVOID Buffer array of addresses from NDIS
|
|
ULONG BufferSize size of incoming buffer
|
|
PUINT pBytesRead update on success
|
|
PUINT pBytesNeeded update on wrong buffer size
|
|
Return value:
|
|
SUCCESS or kind of failure
|
|
***********************************************************/
|
|
NDIS_STATUS ParaNdis_SetMulticastList(
|
|
PARANDIS_ADAPTER *pContext,
|
|
PVOID Buffer,
|
|
ULONG BufferSize,
|
|
PUINT pBytesRead,
|
|
PUINT pBytesNeeded)
|
|
{
|
|
NDIS_STATUS status;
|
|
ULONG length = BufferSize;
|
|
if (length > sizeof(pContext->MulticastData.MulticastList))
|
|
{
|
|
status = NDIS_STATUS_MULTICAST_FULL;
|
|
*pBytesNeeded = sizeof(pContext->MulticastData.MulticastList);
|
|
}
|
|
else if (length % ETH_LENGTH_OF_ADDRESS)
|
|
{
|
|
status = NDIS_STATUS_INVALID_LENGTH;
|
|
*pBytesNeeded = (length / ETH_LENGTH_OF_ADDRESS) * ETH_LENGTH_OF_ADDRESS;
|
|
}
|
|
else
|
|
{
|
|
NdisZeroMemory(pContext->MulticastData.MulticastList, sizeof(pContext->MulticastData.MulticastList));
|
|
if (length)
|
|
NdisMoveMemory(pContext->MulticastData.MulticastList, Buffer, length);
|
|
pContext->MulticastData.nofMulticastEntries = length / ETH_LENGTH_OF_ADDRESS;
|
|
DPrintf(1, ("[%s] New multicast list of %d bytes", __FUNCTION__, length));
|
|
*pBytesRead = length;
|
|
status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/**********************************************************
|
|
Callable from synchronized routine or interrupt handler
|
|
to enable or disable Rx and/or Tx interrupt generation
|
|
Parameters:
|
|
context
|
|
interruptSource - isReceive, isTransmit
|
|
b - 1/0 enable/disable
|
|
***********************************************************/
|
|
VOID ParaNdis_VirtIOEnableIrqSynchronized(PARANDIS_ADAPTER *pContext, ULONG interruptSource)
|
|
{
|
|
if (interruptSource & isTransmit)
|
|
virtqueue_enable_cb(pContext->NetSendQueue);
|
|
if (interruptSource & isReceive)
|
|
virtqueue_enable_cb(pContext->NetReceiveQueue);
|
|
ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)0x10, interruptSource, TRUE, 0);
|
|
}
|
|
|
|
VOID ParaNdis_VirtIODisableIrqSynchronized(PARANDIS_ADAPTER *pContext, ULONG interruptSource)
|
|
{
|
|
if (interruptSource & isTransmit)
|
|
virtqueue_disable_cb(pContext->NetSendQueue);
|
|
if (interruptSource & isReceive)
|
|
virtqueue_disable_cb(pContext->NetReceiveQueue);
|
|
ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)0x10, interruptSource, FALSE, 0);
|
|
}
|
|
|
|
/**********************************************************
|
|
Common handler of PnP events
|
|
Parameters:
|
|
Return value:
|
|
***********************************************************/
|
|
VOID ParaNdis_OnPnPEvent(
|
|
PARANDIS_ADAPTER *pContext,
|
|
NDIS_DEVICE_PNP_EVENT pEvent,
|
|
PVOID pInfo,
|
|
ULONG ulSize)
|
|
{
|
|
const char *pName = "";
|
|
DEBUG_ENTRY(0);
|
|
#undef MAKECASE
|
|
#define MAKECASE(x) case (x): pName = #x; break;
|
|
switch (pEvent)
|
|
{
|
|
MAKECASE(NdisDevicePnPEventQueryRemoved)
|
|
MAKECASE(NdisDevicePnPEventRemoved)
|
|
MAKECASE(NdisDevicePnPEventSurpriseRemoved)
|
|
MAKECASE(NdisDevicePnPEventQueryStopped)
|
|
MAKECASE(NdisDevicePnPEventStopped)
|
|
MAKECASE(NdisDevicePnPEventPowerProfileChanged)
|
|
default:
|
|
break;
|
|
}
|
|
ParaNdis_DebugHistory(pContext, hopPnpEvent, NULL, pEvent, 0, 0);
|
|
DPrintf(0, ("[%s] (%s)", __FUNCTION__, pName));
|
|
if (pEvent == NdisDevicePnPEventSurpriseRemoved)
|
|
{
|
|
// on simulated surprise removal (under PnpTest) we need to reset the device
|
|
// to prevent any access of device queues to memory buffers
|
|
pContext->bSurprizeRemoved = TRUE;
|
|
ParaNdis_ResetVirtIONetDevice(pContext);
|
|
}
|
|
pContext->PnpEvents[pContext->nPnpEventIndex++] = pEvent;
|
|
if (pContext->nPnpEventIndex > sizeof(pContext->PnpEvents)/sizeof(pContext->PnpEvents[0]))
|
|
pContext->nPnpEventIndex = 0;
|
|
}
|
|
|
|
static BOOLEAN SendControlMessage(
|
|
PARANDIS_ADAPTER *pContext,
|
|
UCHAR cls,
|
|
UCHAR cmd,
|
|
PVOID buffer1,
|
|
ULONG size1,
|
|
PVOID buffer2,
|
|
ULONG size2,
|
|
int levelIfOK
|
|
)
|
|
{
|
|
BOOLEAN bOK = FALSE;
|
|
NdisAcquireSpinLock(&pContext->ReceiveLock);
|
|
if (pContext->ControlData.Virtual && pContext->ControlData.size > (size1 + size2 + 16))
|
|
{
|
|
struct VirtIOBufferDescriptor sg[4];
|
|
PUCHAR pBase = (PUCHAR)pContext->ControlData.Virtual;
|
|
PHYSICAL_ADDRESS phBase = pContext->ControlData.Physical;
|
|
ULONG offset = 0;
|
|
UINT nOut = 1;
|
|
|
|
((virtio_net_ctrl_hdr *)pBase)->class_of_command = cls;
|
|
((virtio_net_ctrl_hdr *)pBase)->cmd = cmd;
|
|
sg[0].physAddr = phBase;
|
|
sg[0].length = sizeof(virtio_net_ctrl_hdr);
|
|
offset += sg[0].length;
|
|
offset = (offset + 3) & ~3;
|
|
if (size1)
|
|
{
|
|
NdisMoveMemory(pBase + offset, buffer1, size1);
|
|
sg[nOut].physAddr = phBase;
|
|
sg[nOut].physAddr.QuadPart += offset;
|
|
sg[nOut].length = size1;
|
|
offset += size1;
|
|
offset = (offset + 3) & ~3;
|
|
nOut++;
|
|
}
|
|
if (size2)
|
|
{
|
|
NdisMoveMemory(pBase + offset, buffer2, size2);
|
|
sg[nOut].physAddr = phBase;
|
|
sg[nOut].physAddr.QuadPart += offset;
|
|
sg[nOut].length = size2;
|
|
offset += size2;
|
|
offset = (offset + 3) & ~3;
|
|
nOut++;
|
|
}
|
|
sg[nOut].physAddr = phBase;
|
|
sg[nOut].physAddr.QuadPart += offset;
|
|
sg[nOut].length = sizeof(virtio_net_ctrl_ack);
|
|
*(virtio_net_ctrl_ack *)(pBase + offset) = VIRTIO_NET_ERR;
|
|
|
|
if (0 <= virtqueue_add_buf(pContext->NetControlQueue, sg, nOut, 1, (PVOID)1, NULL, 0))
|
|
{
|
|
UINT len;
|
|
void *p;
|
|
virtqueue_kick_always(pContext->NetControlQueue);
|
|
p = virtqueue_get_buf(pContext->NetControlQueue, &len);
|
|
if (!p)
|
|
{
|
|
DPrintf(0, ("%s - ERROR: get_buf failed", __FUNCTION__));
|
|
}
|
|
else if (len != sizeof(virtio_net_ctrl_ack))
|
|
{
|
|
DPrintf(0, ("%s - ERROR: wrong len %d", __FUNCTION__, len));
|
|
}
|
|
else if (*(virtio_net_ctrl_ack *)(pBase + offset) != VIRTIO_NET_OK)
|
|
{
|
|
DPrintf(0, ("%s - ERROR: error %d returned", __FUNCTION__, *(virtio_net_ctrl_ack *)(pBase + offset)));
|
|
}
|
|
else
|
|
{
|
|
// everything is OK
|
|
DPrintf(levelIfOK, ("%s OK(%d.%d,buffers of %d and %d) ", __FUNCTION__, cls, cmd, size1, size2));
|
|
bOK = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, ("%s - ERROR: add_buf failed", __FUNCTION__));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, ("%s (buffer %d,%d) - ERROR: message too LARGE", __FUNCTION__, size1, size2));
|
|
}
|
|
NdisReleaseSpinLock(&pContext->ReceiveLock);
|
|
return bOK;
|
|
}
|
|
|
|
static VOID ParaNdis_DeviceFiltersUpdateRxMode(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
u8 val;
|
|
ULONG f = pContext->PacketFilter;
|
|
val = (f & NDIS_PACKET_TYPE_ALL_MULTICAST) ? 1 : 0;
|
|
SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_ALLMULTI, &val, sizeof(val), NULL, 0, 2);
|
|
//SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_ALLUNI, &val, sizeof(val), NULL, 0, 2);
|
|
val = (f & (NDIS_PACKET_TYPE_MULTICAST | NDIS_PACKET_TYPE_ALL_MULTICAST)) ? 0 : 1;
|
|
SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_NOMULTI, &val, sizeof(val), NULL, 0, 2);
|
|
val = (f & NDIS_PACKET_TYPE_DIRECTED) ? 0 : 1;
|
|
SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_NOUNI, &val, sizeof(val), NULL, 0, 2);
|
|
val = (f & NDIS_PACKET_TYPE_BROADCAST) ? 0 : 1;
|
|
SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_NOBCAST, &val, sizeof(val), NULL, 0, 2);
|
|
val = (f & NDIS_PACKET_TYPE_PROMISCUOUS) ? 1 : 0;
|
|
SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_PROMISC, &val, sizeof(val), NULL, 0, 2);
|
|
}
|
|
|
|
static VOID ParaNdis_DeviceFiltersUpdateAddresses(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
struct
|
|
{
|
|
struct virtio_net_ctrl_mac header;
|
|
UCHAR addr[ETH_LENGTH_OF_ADDRESS];
|
|
} uCast;
|
|
uCast.header.entries = 1;
|
|
NdisMoveMemory(uCast.addr, pContext->CurrentMacAddress, sizeof(uCast.addr));
|
|
SendControlMessage(pContext, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_TABLE_SET,
|
|
&uCast, sizeof(uCast), &pContext->MulticastData,sizeof(pContext->MulticastData.nofMulticastEntries) + pContext->MulticastData.nofMulticastEntries * ETH_ALEN, 2);
|
|
}
|
|
|
|
static VOID SetSingleVlanFilter(PARANDIS_ADAPTER *pContext, ULONG vlanId, BOOLEAN bOn, int levelIfOK)
|
|
{
|
|
u16 val = vlanId & 0xfff;
|
|
UCHAR cmd = bOn ? VIRTIO_NET_CTRL_VLAN_ADD : VIRTIO_NET_CTRL_VLAN_DEL;
|
|
SendControlMessage(pContext, VIRTIO_NET_CTRL_VLAN, cmd, &val, sizeof(val), NULL, 0, levelIfOK);
|
|
}
|
|
|
|
static VOID SetAllVlanFilters(PARANDIS_ADAPTER *pContext, BOOLEAN bOn)
|
|
{
|
|
ULONG i;
|
|
for (i = 0; i <= MAX_VLAN_ID; ++i)
|
|
SetSingleVlanFilter(pContext, i, bOn, 7);
|
|
}
|
|
|
|
/*
|
|
possible values of filter set (pContext->ulCurrentVlansFilterSet):
|
|
0 - all disabled
|
|
1..4095 - one selected enabled
|
|
4096 - all enabled
|
|
Note that only 0th vlan can't be enabled
|
|
*/
|
|
VOID ParaNdis_DeviceFiltersUpdateVlanId(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
if (pContext->bHasHardwareFilters)
|
|
{
|
|
ULONG newFilterSet;
|
|
if (IsVlanSupported(pContext))
|
|
newFilterSet = pContext->VlanId ? pContext->VlanId : (MAX_VLAN_ID + 1);
|
|
else
|
|
newFilterSet = IsPrioritySupported(pContext) ? (MAX_VLAN_ID + 1) : 0;
|
|
if (newFilterSet != pContext->ulCurrentVlansFilterSet)
|
|
{
|
|
if (pContext->ulCurrentVlansFilterSet > MAX_VLAN_ID)
|
|
SetAllVlanFilters(pContext, FALSE);
|
|
else if (pContext->ulCurrentVlansFilterSet)
|
|
SetSingleVlanFilter(pContext, pContext->ulCurrentVlansFilterSet, FALSE, 2);
|
|
|
|
pContext->ulCurrentVlansFilterSet = newFilterSet;
|
|
|
|
if (pContext->ulCurrentVlansFilterSet > MAX_VLAN_ID)
|
|
SetAllVlanFilters(pContext, TRUE);
|
|
else if (pContext->ulCurrentVlansFilterSet)
|
|
SetSingleVlanFilter(pContext, pContext->ulCurrentVlansFilterSet, TRUE, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID ParaNdis_UpdateDeviceFilters(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
if (pContext->bHasHardwareFilters)
|
|
{
|
|
ParaNdis_DeviceFiltersUpdateRxMode(pContext);
|
|
ParaNdis_DeviceFiltersUpdateAddresses(pContext);
|
|
ParaNdis_DeviceFiltersUpdateVlanId(pContext);
|
|
}
|
|
}
|
|
|
|
NDIS_STATUS ParaNdis_PowerOn(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
LIST_ENTRY TempList;
|
|
NDIS_STATUS status;
|
|
DEBUG_ENTRY(0);
|
|
ParaNdis_DebugHistory(pContext, hopPowerOn, NULL, 1, 0, 0);
|
|
ParaNdis_ResetVirtIONetDevice(pContext);
|
|
virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER);
|
|
/* virtio_get_features must be called once upon device initialization:
|
|
otherwise the device will not work properly */
|
|
(void)virtio_get_features(&pContext->IODevice);
|
|
|
|
if (pContext->bUseMergedBuffers)
|
|
VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_MRG_RXBUF);
|
|
if (VirtIODeviceGetHostFeature(pContext, VIRTIO_RING_F_EVENT_IDX))
|
|
VirtIODeviceEnableGuestFeature(pContext, VIRTIO_RING_F_EVENT_IDX);
|
|
if (pContext->bDoGuestChecksumOnReceive)
|
|
VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_GUEST_CSUM);
|
|
if (VirtIODeviceGetHostFeature(pContext, VIRTIO_F_VERSION_1))
|
|
VirtIODeviceEnableGuestFeature(pContext, VIRTIO_F_VERSION_1);
|
|
if (VirtIODeviceGetHostFeature(pContext, VIRTIO_F_ANY_LAYOUT))
|
|
VirtIODeviceEnableGuestFeature(pContext, VIRTIO_F_ANY_LAYOUT);
|
|
|
|
status = FinalizeFeatures(pContext);
|
|
if (status == NDIS_STATUS_SUCCESS) {
|
|
status = FindNetQueues(pContext);
|
|
}
|
|
if (status != NDIS_STATUS_SUCCESS) {
|
|
virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_FAILED);
|
|
return status;
|
|
}
|
|
|
|
ParaNdis_RestoreDeviceConfigurationAfterReset(pContext);
|
|
|
|
ParaNdis_UpdateDeviceFilters(pContext);
|
|
|
|
InitializeListHead(&TempList);
|
|
|
|
/* submit all the receive buffers */
|
|
NdisAcquireSpinLock(&pContext->ReceiveLock);
|
|
|
|
pContext->ReuseBufferProc = (tReuseReceiveBufferProc)ReuseReceiveBufferRegular;
|
|
|
|
while (!IsListEmpty(&pContext->NetReceiveBuffers))
|
|
{
|
|
pIONetDescriptor pBufferDescriptor =
|
|
(pIONetDescriptor)RemoveHeadList(&pContext->NetReceiveBuffers);
|
|
InsertTailList(&TempList, &pBufferDescriptor->listEntry);
|
|
}
|
|
pContext->NetNofReceiveBuffers = 0;
|
|
while (!IsListEmpty(&TempList))
|
|
{
|
|
pIONetDescriptor pBufferDescriptor =
|
|
(pIONetDescriptor)RemoveHeadList(&TempList);
|
|
if (AddRxBufferToQueue(pContext, pBufferDescriptor))
|
|
{
|
|
InsertTailList(&pContext->NetReceiveBuffers, &pBufferDescriptor->listEntry);
|
|
pContext->NetNofReceiveBuffers++;
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, ("FAILED TO REUSE THE BUFFER!!!!"));
|
|
VirtIONetFreeBufferDescriptor(pContext, pBufferDescriptor);
|
|
pContext->NetMaxReceiveBuffers--;
|
|
}
|
|
}
|
|
virtqueue_kick(pContext->NetReceiveQueue);
|
|
ParaNdis_SetPowerState(pContext, NdisDeviceStateD0);
|
|
pContext->bEnableInterruptHandlingDPC = TRUE;
|
|
virtio_device_ready(&pContext->IODevice);
|
|
|
|
NdisReleaseSpinLock(&pContext->ReceiveLock);
|
|
|
|
// if bFastSuspendInProcess is set by Win8 power-off procedure,
|
|
// the ParaNdis_Resume enables Tx and RX
|
|
// otherwise it does not do anything in Vista+ (Tx and RX are enabled after power-on by Restart)
|
|
ParaNdis_Resume(pContext);
|
|
pContext->bFastSuspendInProcess = FALSE;
|
|
|
|
ParaNdis_ReportLinkStatus(pContext, TRUE);
|
|
ParaNdis_DebugHistory(pContext, hopPowerOn, NULL, 0, 0, 0);
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID ParaNdis_PowerOff(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
DEBUG_ENTRY(0);
|
|
ParaNdis_DebugHistory(pContext, hopPowerOff, NULL, 1, 0, 0);
|
|
|
|
ParaNdis_IndicateConnect(pContext, FALSE, FALSE);
|
|
|
|
// if bFastSuspendInProcess is set by Win8 power-off procedure
|
|
// the ParaNdis_Suspend does fast Rx stop without waiting (=>srsPausing, if there are some RX packets in Ndis)
|
|
pContext->bFastSuspendInProcess = pContext->bNoPauseOnSuspend && pContext->ReceiveState == srsEnabled;
|
|
ParaNdis_Suspend(pContext);
|
|
if (pContext->IODevice.addr)
|
|
{
|
|
/* back compat - remove the OK flag only in legacy mode */
|
|
VirtIODeviceRemoveStatus(&pContext->IODevice, VIRTIO_CONFIG_S_DRIVER_OK);
|
|
}
|
|
|
|
if (pContext->bFastSuspendInProcess)
|
|
{
|
|
NdisAcquireSpinLock(&pContext->ReceiveLock);
|
|
pContext->ReuseBufferProc = (tReuseReceiveBufferProc)ReuseReceiveBufferPowerOff;
|
|
NdisReleaseSpinLock(&pContext->ReceiveLock);
|
|
}
|
|
|
|
ParaNdis_SetPowerState(pContext, NdisDeviceStateD3);
|
|
|
|
PreventDPCServicing(pContext);
|
|
|
|
/*******************************************************************
|
|
shutdown queues to have all the receive buffers under our control
|
|
all the transmit buffers move to list of free buffers
|
|
********************************************************************/
|
|
|
|
NdisAcquireSpinLock(&pContext->SendLock);
|
|
virtqueue_shutdown(pContext->NetSendQueue);
|
|
while (!IsListEmpty(&pContext->NetSendBuffersInUse))
|
|
{
|
|
pIONetDescriptor pBufferDescriptor =
|
|
(pIONetDescriptor)RemoveHeadList(&pContext->NetSendBuffersInUse);
|
|
InsertTailList(&pContext->NetFreeSendBuffers, &pBufferDescriptor->listEntry);
|
|
pContext->nofFreeTxDescriptors++;
|
|
pContext->nofFreeHardwareBuffers += pBufferDescriptor->nofUsedBuffers;
|
|
}
|
|
NdisReleaseSpinLock(&pContext->SendLock);
|
|
|
|
NdisAcquireSpinLock(&pContext->ReceiveLock);
|
|
virtqueue_shutdown(pContext->NetReceiveQueue);
|
|
NdisReleaseSpinLock(&pContext->ReceiveLock);
|
|
if (pContext->NetControlQueue) {
|
|
virtqueue_shutdown(pContext->NetControlQueue);
|
|
}
|
|
|
|
DPrintf(0, ("WARNING: deleting queues!!!!!!!!!"));
|
|
DeleteNetQueues(pContext);
|
|
pContext->NetSendQueue = NULL;
|
|
pContext->NetReceiveQueue = NULL;
|
|
pContext->NetControlQueue = NULL;
|
|
|
|
ParaNdis_ResetVirtIONetDevice(pContext);
|
|
ParaNdis_DebugHistory(pContext, hopPowerOff, NULL, 0, 0, 0);
|
|
}
|
|
|
|
void ParaNdis_CallOnBugCheck(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
if (pContext->IODevice.isr)
|
|
{
|
|
#ifdef DBG_USE_VIRTIO_PCI_ISR_FOR_HOST_REPORT
|
|
WriteVirtIODeviceByte(pContext->IODevice.isr, 1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
tChecksumCheckResult ParaNdis_CheckRxChecksum(PARANDIS_ADAPTER *pContext, ULONG virtioFlags, PVOID pRxPacket, ULONG len)
|
|
{
|
|
tOffloadSettingsFlags f = pContext->Offload.flags;
|
|
tChecksumCheckResult res, resIp;
|
|
PVOID pIpHeader = RtlOffsetToPointer(pRxPacket, ETH_HEADER_SIZE);
|
|
tTcpIpPacketParsingResult ppr;
|
|
ULONG flagsToCalculate = 0;
|
|
res.value = 0;
|
|
resIp.value = 0;
|
|
|
|
//VIRTIO_NET_HDR_F_NEEDS_CSUM - we need to calculate TCP/UDP CS
|
|
//VIRTIO_NET_HDR_F_DATA_VALID - host tells us TCP/UDP CS is OK
|
|
|
|
if (f.fRxIPChecksum) flagsToCalculate |= pcrIpChecksum; // check only
|
|
|
|
if (!(virtioFlags & VIRTIO_NET_HDR_F_DATA_VALID))
|
|
{
|
|
if (virtioFlags & VIRTIO_NET_HDR_F_NEEDS_CSUM)
|
|
{
|
|
flagsToCalculate |= pcrFixXxpChecksum | pcrTcpChecksum | pcrUdpChecksum;
|
|
}
|
|
else
|
|
{
|
|
if (f.fRxTCPChecksum) flagsToCalculate |= pcrTcpV4Checksum;
|
|
if (f.fRxUDPChecksum) flagsToCalculate |= pcrUdpV4Checksum;
|
|
if (f.fRxTCPv6Checksum) flagsToCalculate |= pcrTcpV6Checksum;
|
|
if (f.fRxUDPv6Checksum) flagsToCalculate |= pcrUdpV6Checksum;
|
|
}
|
|
}
|
|
|
|
ppr = ParaNdis_CheckSumVerify(pIpHeader, len - ETH_HEADER_SIZE, flagsToCalculate, __FUNCTION__);
|
|
|
|
if (virtioFlags & VIRTIO_NET_HDR_F_DATA_VALID)
|
|
{
|
|
pContext->extraStatistics.framesRxCSHwOK++;
|
|
ppr.xxpCheckSum = ppresCSOK;
|
|
}
|
|
|
|
if (ppr.ipStatus == ppresIPV4 && !ppr.IsFragment)
|
|
{
|
|
if (f.fRxIPChecksum)
|
|
{
|
|
res.flags.IpOK = ppr.ipCheckSum == ppresCSOK;
|
|
res.flags.IpFailed = ppr.ipCheckSum == ppresCSBad;
|
|
}
|
|
if(ppr.xxpStatus == ppresXxpKnown)
|
|
{
|
|
if(ppr.TcpUdp == ppresIsTCP) /* TCP */
|
|
{
|
|
if (f.fRxTCPChecksum)
|
|
{
|
|
res.flags.TcpOK = ppr.xxpCheckSum == ppresCSOK || ppr.fixedXxpCS;
|
|
res.flags.TcpFailed = !res.flags.TcpOK;
|
|
}
|
|
}
|
|
else /* UDP */
|
|
{
|
|
if (f.fRxUDPChecksum)
|
|
{
|
|
res.flags.UdpOK = ppr.xxpCheckSum == ppresCSOK || ppr.fixedXxpCS;
|
|
res.flags.UdpFailed = !res.flags.UdpOK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ppr.ipStatus == ppresIPV6)
|
|
{
|
|
if(ppr.xxpStatus == ppresXxpKnown)
|
|
{
|
|
if(ppr.TcpUdp == ppresIsTCP) /* TCP */
|
|
{
|
|
if (f.fRxTCPv6Checksum)
|
|
{
|
|
res.flags.TcpOK = ppr.xxpCheckSum == ppresCSOK || ppr.fixedXxpCS;
|
|
res.flags.TcpFailed = !res.flags.TcpOK;
|
|
}
|
|
}
|
|
else /* UDP */
|
|
{
|
|
if (f.fRxUDPv6Checksum)
|
|
{
|
|
res.flags.UdpOK = ppr.xxpCheckSum == ppresCSOK || ppr.fixedXxpCS;
|
|
res.flags.UdpFailed = !res.flags.UdpOK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pContext->bDoIPCheckRx &&
|
|
(f.fRxIPChecksum || f.fRxTCPChecksum || f.fRxUDPChecksum || f.fRxTCPv6Checksum || f.fRxUDPv6Checksum))
|
|
{
|
|
ppr = ParaNdis_CheckSumVerify(pIpHeader, len - ETH_HEADER_SIZE, pcrAnyChecksum, __FUNCTION__);
|
|
if (ppr.ipStatus == ppresIPV4 && !ppr.IsFragment)
|
|
{
|
|
resIp.flags.IpOK = !!f.fRxIPChecksum && ppr.ipCheckSum == ppresCSOK;
|
|
resIp.flags.IpFailed = !!f.fRxIPChecksum && ppr.ipCheckSum == ppresCSBad;
|
|
if (f.fRxTCPChecksum && ppr.xxpStatus == ppresXxpKnown && ppr.TcpUdp == ppresIsTCP)
|
|
{
|
|
resIp.flags.TcpOK = ppr.xxpCheckSum == ppresCSOK;
|
|
resIp.flags.TcpFailed = ppr.xxpCheckSum == ppresCSBad;
|
|
}
|
|
if (f.fRxUDPChecksum && ppr.xxpStatus == ppresXxpKnown && ppr.TcpUdp == ppresIsUDP)
|
|
{
|
|
resIp.flags.UdpOK = ppr.xxpCheckSum == ppresCSOK;
|
|
resIp.flags.UdpFailed = ppr.xxpCheckSum == ppresCSBad;
|
|
}
|
|
}
|
|
else if (ppr.ipStatus == ppresIPV6)
|
|
{
|
|
if (f.fRxTCPv6Checksum && ppr.xxpStatus == ppresXxpKnown && ppr.TcpUdp == ppresIsTCP)
|
|
{
|
|
resIp.flags.TcpOK = ppr.xxpCheckSum == ppresCSOK;
|
|
resIp.flags.TcpFailed = ppr.xxpCheckSum == ppresCSBad;
|
|
}
|
|
if (f.fRxUDPv6Checksum && ppr.xxpStatus == ppresXxpKnown && ppr.TcpUdp == ppresIsUDP)
|
|
{
|
|
resIp.flags.UdpOK = ppr.xxpCheckSum == ppresCSOK;
|
|
resIp.flags.UdpFailed = ppr.xxpCheckSum == ppresCSBad;
|
|
}
|
|
}
|
|
|
|
if (res.value != resIp.value)
|
|
{
|
|
// if HW did not set some bits that IP checker set, it is a mistake:
|
|
// or GOOD CS is not labeled, or BAD checksum is not labeled
|
|
tChecksumCheckResult diff;
|
|
diff.value = resIp.value & ~res.value;
|
|
if (diff.flags.IpFailed || diff.flags.TcpFailed || diff.flags.UdpFailed)
|
|
pContext->extraStatistics.framesRxCSHwMissedBad++;
|
|
if (diff.flags.IpOK || diff.flags.TcpOK || diff.flags.UdpOK)
|
|
pContext->extraStatistics.framesRxCSHwMissedGood++;
|
|
if (diff.value)
|
|
{
|
|
DPrintf(0, ("[%s] real %X <> %X (virtio %X)", __FUNCTION__, resIp.value, res.value, virtioFlags));
|
|
}
|
|
res.value = resIp.value;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|