mirror of
https://github.com/arehnman/yttrium-virtio-gpu.git
synced 2026-10-31 02:05:04 +00:00
Global value of max RX buffers, shared among queues, is bad idea. It is especially bad when memory allocation during queue initialization may fail, for example in case of driver running under driver verifier. Changing the global field to per-queue field. Signed-off-by: Yuri Benditovich <[email protected]>
622 lines
20 KiB
C++
622 lines
20 KiB
C++
#include "ndis56common.h"
|
|
#include "kdebugprint.h"
|
|
#include "ParaNdis_DebugHistory.h"
|
|
#include "Trace.h"
|
|
#ifdef NETKVM_WPP_ENABLED
|
|
#include "ParaNdis_RX.tmh"
|
|
#endif
|
|
|
|
static FORCEINLINE VOID ParaNdis_ReceiveQueueAddBuffer(PPARANDIS_RECEIVE_QUEUE pQueue, pRxNetDescriptor pBuffer)
|
|
{
|
|
NdisInterlockedInsertTailList(&pQueue->BuffersList,
|
|
&pBuffer->ReceiveQueueListEntry,
|
|
&pQueue->Lock);
|
|
}
|
|
|
|
static void ParaNdis_UnbindRxBufferFromPacket(
|
|
pRxNetDescriptor p)
|
|
{
|
|
PMDL NextMdlLinkage = p->Holder;
|
|
ULONG ulPageDescIndex = PARANDIS_FIRST_RX_DATA_PAGE;
|
|
|
|
while (NextMdlLinkage != NULL)
|
|
{
|
|
PMDL pThisMDL = NextMdlLinkage;
|
|
NextMdlLinkage = NDIS_MDL_LINKAGE(pThisMDL);
|
|
|
|
NdisAdjustMdlLength(pThisMDL, p->PhysicalPages[ulPageDescIndex].size);
|
|
NdisFreeMdl(pThisMDL);
|
|
ulPageDescIndex++;
|
|
}
|
|
}
|
|
|
|
static BOOLEAN ParaNdis_BindRxBufferToPacket(
|
|
PARANDIS_ADAPTER *pContext,
|
|
pRxNetDescriptor p)
|
|
{
|
|
ULONG i;
|
|
PMDL *NextMdlLinkage = &p->Holder;
|
|
|
|
for (i = PARANDIS_FIRST_RX_DATA_PAGE; i < p->BufferSGLength; i++)
|
|
{
|
|
*NextMdlLinkage = NdisAllocateMdl(
|
|
pContext->MiniportHandle,
|
|
p->PhysicalPages[i].Virtual,
|
|
p->PhysicalPages[i].size);
|
|
if (*NextMdlLinkage == NULL) goto error_exit;
|
|
|
|
NextMdlLinkage = &(NDIS_MDL_LINKAGE(*NextMdlLinkage));
|
|
}
|
|
*NextMdlLinkage = NULL;
|
|
|
|
return TRUE;
|
|
|
|
error_exit:
|
|
|
|
ParaNdis_UnbindRxBufferFromPacket(p);
|
|
return FALSE;
|
|
}
|
|
|
|
static bool IsRegionInside(const tCompletePhysicalAddress& a1, const tCompletePhysicalAddress& a2)
|
|
{
|
|
const LONGLONG& p1 = a1.Physical.QuadPart;
|
|
const LONGLONG& p2 = a2.Physical.QuadPart;
|
|
return p1 >= p2 && p1 <= p2 + a2.size;
|
|
}
|
|
|
|
static void ParaNdis_FreeRxBufferDescriptor(PARANDIS_ADAPTER *pContext, pRxNetDescriptor p)
|
|
{
|
|
ULONG i;
|
|
|
|
ParaNdis_UnbindRxBufferFromPacket(p);
|
|
for (i = 0; i < p->BufferSGLength; i++)
|
|
{
|
|
if (!p->PhysicalPages[i].Virtual)
|
|
break;
|
|
// do not try do free the region derived from header block
|
|
if (i != 0 && IsRegionInside(p->PhysicalPages[i], p->PhysicalPages[0]))
|
|
continue;
|
|
ParaNdis_FreePhysicalMemory(pContext, &p->PhysicalPages[i]);
|
|
}
|
|
|
|
if (p->BufferSGArray) NdisFreeMemory(p->BufferSGArray, 0, 0);
|
|
if (p->PhysicalPages) NdisFreeMemory(p->PhysicalPages, 0, 0);
|
|
NdisFreeMemory(p, 0, 0);
|
|
}
|
|
|
|
CParaNdisRX::CParaNdisRX()
|
|
{
|
|
InitializeListHead(&m_NetReceiveBuffers);
|
|
}
|
|
|
|
CParaNdisRX::~CParaNdisRX()
|
|
{
|
|
}
|
|
|
|
bool CParaNdisRX::Create(PPARANDIS_ADAPTER Context, UINT DeviceQueueIndex)
|
|
{
|
|
m_Context = Context;
|
|
m_queueIndex = (u16)DeviceQueueIndex;
|
|
m_NetMaxReceiveBuffers = Context->maxRxBufferPerQueue;
|
|
|
|
if (!m_VirtQueue.Create(DeviceQueueIndex,
|
|
&m_Context->IODevice,
|
|
m_Context->MiniportHandle))
|
|
{
|
|
DPrintf(0, ("CParaNdisRX::Create - virtqueue creation failed\n"));
|
|
return false;
|
|
}
|
|
|
|
PrepareReceiveBuffers();
|
|
|
|
m_nReusedRxBuffersLimit = m_NetMaxReceiveBuffers / 4 + 1;
|
|
|
|
CreatePath();
|
|
|
|
return true;
|
|
}
|
|
|
|
int CParaNdisRX::PrepareReceiveBuffers()
|
|
{
|
|
int nRet = 0;
|
|
UINT i;
|
|
DEBUG_ENTRY(4);
|
|
|
|
for (i = 0; i < m_NetMaxReceiveBuffers; ++i)
|
|
{
|
|
pRxNetDescriptor pBuffersDescriptor = CreateRxDescriptorOnInit();
|
|
if (!pBuffersDescriptor) break;
|
|
|
|
pBuffersDescriptor->Queue = this;
|
|
|
|
if (!AddRxBufferToQueue(pBuffersDescriptor))
|
|
{
|
|
ParaNdis_FreeRxBufferDescriptor(m_Context, pBuffersDescriptor);
|
|
break;
|
|
}
|
|
|
|
InsertTailList(&m_NetReceiveBuffers, &pBuffersDescriptor->listEntry);
|
|
|
|
m_NetNofReceiveBuffers++;
|
|
}
|
|
m_NetMaxReceiveBuffers = m_NetNofReceiveBuffers;
|
|
m_MinRxBufferLimit = m_NetNofReceiveBuffers * m_Context->MinRxBufferPercent / 100;
|
|
DPrintf(0, "[%s] m_NetMaxReceiveBuffers %d, m_MinRxBufferLimit %u\n", __FUNCTION__, m_NetMaxReceiveBuffers, m_MinRxBufferLimit);
|
|
if (m_Context->extraStatistics.minFreeRxBuffers == 0 || m_Context->extraStatistics.minFreeRxBuffers > m_NetNofReceiveBuffers)
|
|
{
|
|
m_Context->extraStatistics.minFreeRxBuffers = m_NetNofReceiveBuffers;
|
|
}
|
|
m_Reinsert = true;
|
|
|
|
return nRet;
|
|
}
|
|
|
|
pRxNetDescriptor CParaNdisRX::CreateRxDescriptorOnInit()
|
|
{
|
|
//For RX packets we allocate following pages
|
|
// X pages needed to fit most of data payload (or all the payload)
|
|
// 1 page or less for virtio header, indirect buffers array and the data tail if any
|
|
// virtio header and indirect buffers take ~300 bytes
|
|
// if the data tail (payload % page size) is small it is also goes to the header block
|
|
ULONG ulNumDataPages = m_Context->RxLayout.TotalAllocationsPerBuffer; // including header block
|
|
ULONG sgArraySize = m_Context->RxLayout.IndirectEntries;
|
|
|
|
pRxNetDescriptor p = (pRxNetDescriptor)ParaNdis_AllocateMemory(m_Context, sizeof(*p));
|
|
if (p == NULL) return NULL;
|
|
|
|
NdisZeroMemory(p, sizeof(*p));
|
|
|
|
p->BufferSGArray = (struct VirtIOBufferDescriptor *)
|
|
ParaNdis_AllocateMemory(m_Context, sizeof(*p->BufferSGArray) * sgArraySize);
|
|
if (p->BufferSGArray == NULL) goto error_exit;
|
|
|
|
p->PhysicalPages = (tCompletePhysicalAddress *)
|
|
ParaNdis_AllocateMemory(m_Context, sizeof(*p->PhysicalPages) * sgArraySize);
|
|
if (p->PhysicalPages == NULL) goto error_exit;
|
|
|
|
// must initialize for case of exit in the middle of the loop
|
|
NdisZeroMemory(p->PhysicalPages, sizeof(*p->PhysicalPages) * sgArraySize);
|
|
|
|
p->BufferSGLength = 0;
|
|
|
|
while (ulNumDataPages > 0)
|
|
{
|
|
// Allocate the first block separately, the rest can be one contiguous block
|
|
ULONG ulPagesToAlloc = (p->BufferSGLength == 0) ?
|
|
1 : ulNumDataPages;
|
|
ULONG sizeToAlloc = (p->BufferSGLength == 0) ?
|
|
m_Context->RxLayout.HeaderPageAllocation : PAGE_SIZE * ulPagesToAlloc;
|
|
|
|
while (!ParaNdis_InitialAllocatePhysicalMemory(
|
|
m_Context,
|
|
sizeToAlloc,
|
|
&p->PhysicalPages[p->BufferSGLength]))
|
|
{
|
|
// Retry with half the pages
|
|
if (ulPagesToAlloc == 1)
|
|
goto error_exit;
|
|
else
|
|
ulPagesToAlloc /= 2;
|
|
}
|
|
|
|
p->BufferSGArray[p->BufferSGLength].physAddr = p->PhysicalPages[p->BufferSGLength].Physical;
|
|
p->BufferSGArray[p->BufferSGLength].length = p->PhysicalPages[p->BufferSGLength].size;
|
|
|
|
ulNumDataPages -= ulPagesToAlloc;
|
|
p->BufferSGLength++;
|
|
}
|
|
|
|
//First page is for virtio header, size needs to be adjusted correspondingly
|
|
p->BufferSGArray[0].length = m_Context->nVirtioHeaderSize;
|
|
|
|
ULONG offsetInTheHeader = m_Context->RxLayout.ReserveForHeader;
|
|
//Pre-cache indirect area addresses
|
|
p->IndirectArea.Physical.QuadPart = p->PhysicalPages[0].Physical.QuadPart + offsetInTheHeader;
|
|
p->IndirectArea.Virtual = RtlOffsetToPointer(p->PhysicalPages[0].Virtual, offsetInTheHeader);
|
|
p->IndirectArea.size = m_Context->RxLayout.ReserveForIndirectArea;
|
|
|
|
if (m_Context->RxLayout.ReserveForPacketTail)
|
|
{
|
|
// the payload tail is located in the header block
|
|
offsetInTheHeader += m_Context->RxLayout.ReserveForIndirectArea;
|
|
|
|
// fill the tail's physical page fields
|
|
p->PhysicalPages[p->BufferSGLength].Physical.QuadPart =
|
|
p->PhysicalPages[0].Physical.QuadPart + offsetInTheHeader;
|
|
p->PhysicalPages[p->BufferSGLength].Virtual =
|
|
RtlOffsetToPointer(p->PhysicalPages[0].Virtual, offsetInTheHeader);
|
|
p->PhysicalPages[p->BufferSGLength].size = m_Context->RxLayout.ReserveForPacketTail;
|
|
|
|
// fill the tail's SG buffer fields
|
|
p->BufferSGArray[p->BufferSGLength].physAddr.QuadPart =
|
|
p->PhysicalPages[p->BufferSGLength].Physical.QuadPart;
|
|
p->BufferSGArray[p->BufferSGLength].length =
|
|
p->PhysicalPages[p->BufferSGLength].size;
|
|
p->BufferSGLength++;
|
|
}
|
|
else
|
|
{
|
|
// the payload tail is located in the full data block
|
|
// and was already allocated and counted
|
|
}
|
|
|
|
if (!ParaNdis_BindRxBufferToPacket(m_Context, p))
|
|
goto error_exit;
|
|
|
|
return p;
|
|
|
|
error_exit:
|
|
ParaNdis_FreeRxBufferDescriptor(m_Context, p);
|
|
return NULL;
|
|
}
|
|
|
|
/* TODO - make it method in pRXNetDescriptor */
|
|
BOOLEAN CParaNdisRX::AddRxBufferToQueue(pRxNetDescriptor pBufferDescriptor)
|
|
{
|
|
return 0 <= pBufferDescriptor->Queue->m_VirtQueue.AddBuf(
|
|
pBufferDescriptor->BufferSGArray,
|
|
0,
|
|
pBufferDescriptor->BufferSGLength,
|
|
pBufferDescriptor,
|
|
m_Context->bUseIndirect ? pBufferDescriptor->IndirectArea.Virtual : NULL,
|
|
m_Context->bUseIndirect ? pBufferDescriptor->IndirectArea.Physical.QuadPart : 0);
|
|
}
|
|
|
|
void CParaNdisRX::FreeRxDescriptorsFromList()
|
|
{
|
|
while (!IsListEmpty(&m_NetReceiveBuffers))
|
|
{
|
|
pRxNetDescriptor pBufferDescriptor = (pRxNetDescriptor)RemoveHeadList(&m_NetReceiveBuffers);
|
|
ParaNdis_FreeRxBufferDescriptor(m_Context, pBufferDescriptor);
|
|
}
|
|
}
|
|
|
|
void CParaNdisRX::ReuseReceiveBufferNoLock(pRxNetDescriptor pBuffersDescriptor)
|
|
{
|
|
DEBUG_ENTRY(4);
|
|
|
|
if (!m_Reinsert)
|
|
{
|
|
InsertTailList(&m_NetReceiveBuffers, &pBuffersDescriptor->listEntry);
|
|
m_NetNofReceiveBuffers++;
|
|
return;
|
|
}
|
|
else if (AddRxBufferToQueue(pBuffersDescriptor))
|
|
{
|
|
InsertTailList(&m_NetReceiveBuffers, &pBuffersDescriptor->listEntry);
|
|
m_NetNofReceiveBuffers++;
|
|
|
|
if (m_NetNofReceiveBuffers > m_NetMaxReceiveBuffers)
|
|
{
|
|
DPrintf(0, " Error: m_NetNofReceiveBuffers > m_NetMaxReceiveBuffers (%d>%d)\n",
|
|
m_NetNofReceiveBuffers, m_NetMaxReceiveBuffers);
|
|
}
|
|
|
|
/* TODO - nReusedRXBuffers per queue or per context ?*/
|
|
if (++m_nReusedRxBuffersCounter >= m_nReusedRxBuffersLimit)
|
|
{
|
|
m_nReusedRxBuffersCounter = 0;
|
|
m_VirtQueue.Kick();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* TODO - NetMaxReceiveBuffers per queue or per context ?*/
|
|
DPrintf(0, "FAILED TO REUSE THE BUFFER!!!!\n");
|
|
ParaNdis_FreeRxBufferDescriptor(m_Context, pBuffersDescriptor);
|
|
m_NetMaxReceiveBuffers--;
|
|
}
|
|
}
|
|
|
|
VOID CParaNdisRX::KickRXRing()
|
|
{
|
|
m_VirtQueue.Kick();
|
|
}
|
|
|
|
#if PARANDIS_SUPPORT_RSS
|
|
static FORCEINLINE VOID ParaNdis_QueueRSSDpc(PARANDIS_ADAPTER *pContext, ULONG MessageIndex, PGROUP_AFFINITY pTargetAffinity)
|
|
{
|
|
NdisMQueueDpcEx(pContext->InterruptHandle, MessageIndex, pTargetAffinity, NULL);
|
|
}
|
|
|
|
static FORCEINLINE CCHAR ParaNdis_GetScalingDataForPacket(PARANDIS_ADAPTER *pContext, PNET_PACKET_INFO pPacketInfo, PPROCESSOR_NUMBER pTargetProcessor)
|
|
{
|
|
return ParaNdis6_RSSGetScalingDataForPacket(&pContext->RSSParameters, pPacketInfo, pTargetProcessor);
|
|
}
|
|
#endif
|
|
|
|
static ULONG ShallPassPacket(PARANDIS_ADAPTER* pContext, PNET_PACKET_INFO pPacketInfo)
|
|
{
|
|
ULONG i;
|
|
|
|
if (pPacketInfo->dataLength > pContext->MaxPacketSize.nMaxFullSizeOsRx + ETH_PRIORITY_HEADER_SIZE)
|
|
return FALSE;
|
|
|
|
if ((pPacketInfo->dataLength > pContext->MaxPacketSize.nMaxFullSizeOsRx) && !pPacketInfo->hasVlanHeader)
|
|
return FALSE;
|
|
|
|
if (IsVlanSupported(pContext) && pPacketInfo->hasVlanHeader)
|
|
{
|
|
if (pContext->VlanId && pContext->VlanId != pPacketInfo->Vlan.VlanId)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (pContext->PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS)
|
|
return TRUE;
|
|
|
|
if (pPacketInfo->isUnicast)
|
|
{
|
|
ULONG Res;
|
|
|
|
if (!(pContext->PacketFilter & NDIS_PACKET_TYPE_DIRECTED))
|
|
return FALSE;
|
|
|
|
ETH_COMPARE_NETWORK_ADDRESSES_EQ_SAFE(pPacketInfo->ethDestAddr, pContext->CurrentMacAddress, &Res);
|
|
return !Res;
|
|
}
|
|
|
|
if (pPacketInfo->isBroadcast)
|
|
return (pContext->PacketFilter & NDIS_PACKET_TYPE_BROADCAST);
|
|
|
|
// Multi-cast
|
|
|
|
if (pContext->PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST)
|
|
return TRUE;
|
|
|
|
if (!(pContext->PacketFilter & NDIS_PACKET_TYPE_MULTICAST))
|
|
return FALSE;
|
|
|
|
for (i = 0; i < pContext->MulticastData.nofMulticastEntries; i++)
|
|
{
|
|
ULONG Res;
|
|
PUCHAR CurrMcastAddr = &pContext->MulticastData.MulticastList[i * ETH_ALEN];
|
|
|
|
ETH_COMPARE_NETWORK_ADDRESSES_EQ_SAFE(pPacketInfo->ethDestAddr, CurrMcastAddr, &Res);
|
|
|
|
if (!Res)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#define LogRedirectedPacket(p)
|
|
#if !defined(LogRedirectedPacket)
|
|
static void LogRedirectedPacket(pRxNetDescriptor pBufferDescriptor)
|
|
{
|
|
NET_PACKET_INFO *pi = &pBufferDescriptor->PacketInfo;
|
|
LPCSTR packetType = "Unknown";
|
|
IPv4Header *pIp4Header = NULL;
|
|
TCPHeader *pTcpHeader = NULL;
|
|
UDPHeader *pUdpHeader = NULL;
|
|
//IPv6Header *pIp6Header = NULL;
|
|
switch (pi->RSSHash.Type)
|
|
{
|
|
case NDIS_HASH_TCP_IPV4:
|
|
packetType = "TCP_IPV4";
|
|
pIp4Header = (IPv4Header *)RtlOffsetToPointer(pi->headersBuffer, pi->L2HdrLen);
|
|
pTcpHeader = (TCPHeader *)RtlOffsetToPointer(pIp4Header, pi->L3HdrLen);
|
|
break;
|
|
case NDIS_HASH_IPV4:
|
|
packetType = "IPV4";
|
|
pIp4Header = (IPv4Header *)RtlOffsetToPointer(pi->headersBuffer, pi->L2HdrLen);
|
|
break;
|
|
case NDIS_HASH_TCP_IPV6:
|
|
packetType = "TCP_IPV6";
|
|
break;
|
|
case NDIS_HASH_TCP_IPV6_EX:
|
|
packetType = "TCP_IPV6EX";
|
|
break;
|
|
case NDIS_HASH_IPV6_EX:
|
|
packetType = "IPV6EX";
|
|
break;
|
|
case NDIS_HASH_IPV6:
|
|
packetType = "IPV6";
|
|
break;
|
|
#if (NDIS_SUPPORT_NDIS680)
|
|
case NDIS_HASH_UDP_IPV4:
|
|
packetType = "UDP_IPV4";
|
|
pIp4Header = (IPv4Header *)RtlOffsetToPointer(pi->headersBuffer, pi->L2HdrLen);
|
|
pUdpHeader = (UDPHeader *)RtlOffsetToPointer(pIp4Header, pi->L3HdrLen);
|
|
break;
|
|
case NDIS_HASH_UDP_IPV6:
|
|
packetType = "UDP_IPV6";
|
|
break;
|
|
case NDIS_HASH_UDP_IPV6_EX:
|
|
packetType = "UDP_IPV6EX";
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
if (pTcpHeader)
|
|
{
|
|
TraceNoPrefix(0, "%s: %s %d.%d.%d.%d:%d->%d.%d.%d.%d:%d\n", __FUNCTION__, packetType,
|
|
pIp4Header->ip_srca[0], pIp4Header->ip_srca[1], pIp4Header->ip_srca[2], pIp4Header->ip_srca[3],
|
|
RtlUshortByteSwap(pTcpHeader->tcp_src),
|
|
pIp4Header->ip_desta[0], pIp4Header->ip_desta[1], pIp4Header->ip_desta[2], pIp4Header->ip_desta[3],
|
|
RtlUshortByteSwap(pTcpHeader->tcp_dest));
|
|
}
|
|
else if (pUdpHeader)
|
|
{
|
|
TraceNoPrefix(0, "%s: %s %d.%d.%d.%d:%d->%d.%d.%d.%d:%d\n", __FUNCTION__, packetType,
|
|
pIp4Header->ip_srca[0], pIp4Header->ip_srca[1], pIp4Header->ip_srca[2], pIp4Header->ip_srca[3],
|
|
RtlUshortByteSwap(pUdpHeader->udp_src),
|
|
pIp4Header->ip_desta[0], pIp4Header->ip_desta[1], pIp4Header->ip_desta[2], pIp4Header->ip_desta[3],
|
|
RtlUshortByteSwap(pUdpHeader->udp_dest));
|
|
}
|
|
else if (pIp4Header)
|
|
{
|
|
TraceNoPrefix(0, "%s: %s %d.%d.%d.%d(%d)->%d.%d.%d.%d\n", __FUNCTION__, packetType,
|
|
pIp4Header->ip_srca[0], pIp4Header->ip_srca[1], pIp4Header->ip_srca[2], pIp4Header->ip_srca[3],
|
|
pIp4Header->ip_protocol,
|
|
pIp4Header->ip_desta[0], pIp4Header->ip_desta[1], pIp4Header->ip_desta[2], pIp4Header->ip_desta[3]);
|
|
}
|
|
else
|
|
{
|
|
TraceNoPrefix(0, "%s: %s\n", __FUNCTION__, packetType);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
VOID CParaNdisRX::ProcessRxRing(CCHAR nCurrCpuReceiveQueue)
|
|
{
|
|
pRxNetDescriptor pBufferDescriptor;
|
|
unsigned int nFullLength;
|
|
|
|
#ifndef PARANDIS_SUPPORT_RSS
|
|
UNREFERENCED_PARAMETER(nCurrCpuReceiveQueue);
|
|
#endif
|
|
|
|
TDPCSpinLocker autoLock(m_Lock);
|
|
|
|
if (m_Context->extraStatistics.minFreeRxBuffers > m_NetNofReceiveBuffers)
|
|
{
|
|
m_Context->extraStatistics.minFreeRxBuffers = m_NetNofReceiveBuffers;
|
|
}
|
|
|
|
while (NULL != (pBufferDescriptor = (pRxNetDescriptor)m_VirtQueue.GetBuf(&nFullLength)))
|
|
{
|
|
RemoveEntryList(&pBufferDescriptor->listEntry);
|
|
m_NetNofReceiveBuffers--;
|
|
|
|
// basic MAC-based analysis + L3 header info
|
|
BOOLEAN packetAnalysisRC = ParaNdis_AnalyzeReceivedPacket(
|
|
pBufferDescriptor->PhysicalPages[PARANDIS_FIRST_RX_DATA_PAGE].Virtual,
|
|
nFullLength - m_Context->nVirtioHeaderSize,
|
|
&pBufferDescriptor->PacketInfo);
|
|
|
|
if (!packetAnalysisRC)
|
|
{
|
|
pBufferDescriptor->Queue->ReuseReceiveBufferNoLock(pBufferDescriptor);
|
|
m_Context->Statistics.ifInErrors++;
|
|
m_Context->Statistics.ifInDiscards++;
|
|
continue;
|
|
}
|
|
|
|
// filtering based on prev stage analysis
|
|
if (!ShallPassPacket(m_Context, &pBufferDescriptor->PacketInfo))
|
|
{
|
|
pBufferDescriptor->Queue->ReuseReceiveBufferNoLock(pBufferDescriptor);
|
|
m_Context->Statistics.ifInDiscards++;
|
|
m_Context->extraStatistics.framesFilteredOut++;
|
|
continue;
|
|
}
|
|
#ifdef PARANDIS_SUPPORT_RSS
|
|
if (m_Context->RSSParameters.RSSMode != PARANDIS_RSS_MODE::PARANDIS_RSS_DISABLED)
|
|
{
|
|
ParaNdis6_RSSAnalyzeReceivedPacket(
|
|
&m_Context->RSSParameters,
|
|
pBufferDescriptor->PhysicalPages[PARANDIS_FIRST_RX_DATA_PAGE].Virtual,
|
|
&pBufferDescriptor->PacketInfo);
|
|
}
|
|
CCHAR nTargetReceiveQueueNum;
|
|
GROUP_AFFINITY TargetAffinity;
|
|
PROCESSOR_NUMBER TargetProcessor;
|
|
|
|
nTargetReceiveQueueNum = ParaNdis_GetScalingDataForPacket(
|
|
m_Context,
|
|
&pBufferDescriptor->PacketInfo,
|
|
&TargetProcessor);
|
|
|
|
if (nTargetReceiveQueueNum == PARANDIS_RECEIVE_UNCLASSIFIED_PACKET)
|
|
{
|
|
ParaNdis_ReceiveQueueAddBuffer(&m_UnclassifiedPacketsQueue, pBufferDescriptor);
|
|
m_Context->extraStatistics.framesRSSUnclassified++;
|
|
}
|
|
else
|
|
{
|
|
ParaNdis_ReceiveQueueAddBuffer(&m_Context->ReceiveQueues[nTargetReceiveQueueNum], pBufferDescriptor);
|
|
|
|
if (nTargetReceiveQueueNum != nCurrCpuReceiveQueue)
|
|
{
|
|
if (m_Context->bPollModeEnabled)
|
|
{
|
|
ParaNdisPollNotify(m_Context, nTargetReceiveQueueNum, "RSS");
|
|
}
|
|
else
|
|
{
|
|
ParaNdis_ProcessorNumberToGroupAffinity(&TargetAffinity, &TargetProcessor);
|
|
ParaNdis_QueueRSSDpc(m_Context, m_messageIndex, &TargetAffinity);
|
|
}
|
|
m_Context->extraStatistics.framesRSSMisses++;
|
|
LogRedirectedPacket(pBufferDescriptor);
|
|
}
|
|
else
|
|
{
|
|
m_Context->extraStatistics.framesRSSHits++;
|
|
}
|
|
}
|
|
#else
|
|
ParaNdis_ReceiveQueueAddBuffer(&m_UnclassifiedPacketsQueue, pBufferDescriptor);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void CParaNdisRX::PopulateQueue()
|
|
{
|
|
LIST_ENTRY TempList;
|
|
TPassiveSpinLocker autoLock(m_Lock);
|
|
|
|
|
|
InitializeListHead(&TempList);
|
|
|
|
while (!IsListEmpty(&m_NetReceiveBuffers))
|
|
{
|
|
pRxNetDescriptor pBufferDescriptor =
|
|
(pRxNetDescriptor)RemoveHeadList(&m_NetReceiveBuffers);
|
|
InsertTailList(&TempList, &pBufferDescriptor->listEntry);
|
|
}
|
|
m_NetNofReceiveBuffers = 0;
|
|
while (!IsListEmpty(&TempList))
|
|
{
|
|
pRxNetDescriptor pBufferDescriptor =
|
|
(pRxNetDescriptor)RemoveHeadList(&TempList);
|
|
if (AddRxBufferToQueue(pBufferDescriptor))
|
|
{
|
|
InsertTailList(&m_NetReceiveBuffers, &pBufferDescriptor->listEntry);
|
|
m_NetNofReceiveBuffers++;
|
|
}
|
|
else
|
|
{
|
|
/* TODO - NetMaxReceiveBuffers should take into account all queues */
|
|
DPrintf(0, "FAILED TO REUSE THE BUFFER!!!!\n");
|
|
ParaNdis_FreeRxBufferDescriptor(m_Context, pBufferDescriptor);
|
|
m_NetMaxReceiveBuffers--;
|
|
}
|
|
}
|
|
m_Reinsert = true;
|
|
}
|
|
|
|
BOOLEAN CParaNdisRX::RestartQueue()
|
|
{
|
|
return ParaNdis_SynchronizeWithInterrupt(m_Context,
|
|
m_messageIndex,
|
|
RestartQueueSynchronously,
|
|
this);
|
|
}
|
|
|
|
#ifdef PARANDIS_SUPPORT_RSS
|
|
VOID ParaNdis_ResetRxClassification(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = PARANDIS_FIRST_RSS_RECEIVE_QUEUE; i < ARRAYSIZE(pContext->ReceiveQueues); i++)
|
|
{
|
|
PPARANDIS_RECEIVE_QUEUE pCurrQueue = &pContext->ReceiveQueues[i];
|
|
NdisAcquireSpinLock(&pCurrQueue->Lock);
|
|
|
|
while (!IsListEmpty(&pCurrQueue->BuffersList))
|
|
{
|
|
PLIST_ENTRY pListEntry = RemoveHeadList(&pCurrQueue->BuffersList);
|
|
pRxNetDescriptor pBufferDescriptor = CONTAINING_RECORD(pListEntry, RxNetDescriptor, ReceiveQueueListEntry);
|
|
ParaNdis_ReceiveQueueAddBuffer(&pBufferDescriptor->Queue->UnclassifiedPacketsQueue(), pBufferDescriptor);
|
|
}
|
|
|
|
NdisReleaseSpinLock(&pCurrQueue->Lock);
|
|
}
|
|
}
|
|
#endif
|