Files
yttrium-virtio-gpu/NetKVM/Common/ParaNdis-RX.cpp
T
Joseph Hindin d328395de0 NetKVM: BZ#1190960: Vista compilation fix
The logic of dispatching the packet toward different ReceiveQueues
depending on scaling data, was placed under #ifdef. In case
of no RSS support, all packets goes to the same receive queue.

In order to avoid #ifdef duplication, declarations of variables
used only for RSS-related computations, were moved under same
 ifdef - with varaibels not ifdef'd, compiler complains
on unreferenced variables.

Also, parameter 'nCurrCpuReceiveQueue' is used only for RSS
computation, so in the absence of RSS, 'unused parameter' warning
was disabled with UNREFERENCED_PARAMETER macro.

Signed-off-by: Joseph Hindin <[email protected]>
2015-05-03 12:34:55 +10:00

303 lines
9.8 KiB
C++

#include "ndis56common.h"
CParaNdisRX::CParaNdisRX() : m_nReusedRxBuffersCounter(0), m_NetNofReceiveBuffers(0)
{
InitializeListHead(&m_NetReceiveBuffers);
NdisAllocateSpinLock(&m_UnclassifiedPacketsQueue.Lock);
InitializeListHead(&m_UnclassifiedPacketsQueue.BuffersList);
m_UnclassifiedPacketsQueue.ActiveProcessorsCount = 0;
}
CParaNdisRX::~CParaNdisRX()
{
NdisFreeSpinLock(&m_UnclassifiedPacketsQueue.Lock);
}
bool CParaNdisRX::Create(PPARANDIS_ADAPTER Context, UINT DeviceQueueIndex)
{
m_Context = Context;
m_queueIndex = (u16)DeviceQueueIndex;
if (!m_VirtQueue.Create(DeviceQueueIndex,
m_Context->IODevice,
m_Context->MiniportHandle,
m_Context->bDoPublishIndices ? true : false))
{
DPrintf(0, ("CParaNdisRX::Create - virtqueue creation failed\n"));
return false;
}
m_nReusedRxBuffersLimit = m_Context->NetMaxReceiveBuffers / 4 + 1;
PrepareReceiveBuffers();
return true;
}
int CParaNdisRX::PrepareReceiveBuffers()
{
int nRet = 0;
UINT i;
DEBUG_ENTRY(4);
for (i = 0; i < m_Context->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++;
}
/* TODO - NetMaxReceiveBuffers should take into account all queues */
m_Context->NetMaxReceiveBuffers = m_NetNofReceiveBuffers;
DPrintf(0, ("[%s] MaxReceiveBuffers %d\n", __FUNCTION__, m_Context->NetMaxReceiveBuffers));
m_Reinsert = true;
m_VirtQueue.Kick();
return nRet;
}
pRxNetDescriptor CParaNdisRX::CreateRxDescriptorOnInit()
{
//For RX packets we allocate following pages
// 1 page for virtio header and indirect buffers array
// X pages needed to fit maximal length buffer of data
// The assumption is virtio header and indirect buffers array fit 1 page
ULONG ulNumPages = m_Context->MaxPacketSize.nMaxDataSizeHwRx / PAGE_SIZE + 2;
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) * ulNumPages);
if (p->BufferSGArray == NULL) goto error_exit;
p->PhysicalPages = (tCompletePhysicalAddress *)
ParaNdis_AllocateMemory(m_Context, sizeof(*p->PhysicalPages) * ulNumPages);
if (p->PhysicalPages == NULL) goto error_exit;
for (p->PagesAllocated = 0; p->PagesAllocated < ulNumPages; p->PagesAllocated++)
{
p->PhysicalPages[p->PagesAllocated].size = PAGE_SIZE;
if (!ParaNdis_InitialAllocatePhysicalMemory(m_Context, &p->PhysicalPages[p->PagesAllocated]))
goto error_exit;
p->BufferSGArray[p->PagesAllocated].physAddr = p->PhysicalPages[p->PagesAllocated].Physical;
p->BufferSGArray[p->PagesAllocated].length = PAGE_SIZE;
}
//First page is for virtio header, size needs to be adjusted correspondingly
p->BufferSGArray[0].length = m_Context->nVirtioHeaderSize;
//Pre-cache indirect area addresses
p->IndirectArea.Physical.QuadPart = p->PhysicalPages[0].Physical.QuadPart + m_Context->nVirtioHeaderSize;
p->IndirectArea.Virtual = RtlOffsetToPointer(p->PhysicalPages[0].Virtual, m_Context->nVirtioHeaderSize);
p->IndirectArea.size = PAGE_SIZE - m_Context->nVirtioHeaderSize;
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->PagesAllocated,
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);
m_Context->m_rxPacketsOutsideRing.Release();
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_Context->NetMaxReceiveBuffers)
{
DPrintf(0, (" Error: NetNofReceiveBuffers > NetMaxReceiveBuffers(%d>%d)\n",
m_NetNofReceiveBuffers, m_Context->NetMaxReceiveBuffers));
}
/* TODO - nReusedRXBuffes per queue or per context ?*/
if (++m_nReusedRxBuffersCounter >= m_nReusedRxBuffersLimit)
{
m_nReusedRxBuffersCounter = 0;
m_VirtQueue.KickAlways();
}
}
else
{
/* TODO - NetMaxReceiveBuffers per queue or per context ?*/
DPrintf(0, ("FAILED TO REUSE THE BUFFER!!!!\n"));
ParaNdis_FreeRxBufferDescriptor(m_Context, pBuffersDescriptor);
m_Context->NetMaxReceiveBuffers--;
}
}
VOID CParaNdisRX::ProcessRxRing(CCHAR nCurrCpuReceiveQueue)
{
pRxNetDescriptor pBufferDescriptor;
unsigned int nFullLength;
#ifndef PARANDIS_SUPPORT_RSS
UNREFERENCED_PARAMETER(nCurrCpuReceiveQueue);
#endif
CLockedContext<CNdisSpinLock> autoLock(m_Lock);
while (NULL != (pBufferDescriptor = (pRxNetDescriptor)m_VirtQueue.GetBuf(&nFullLength)))
{
/* The counter m_rxPacketsOutsideRing is increased when the packet is removed from ring; it is decreased
in CParaNdisRX::ReuseReceiveBuffer either in case of error or when NDIS calls ParaNdis6_ReturnNetBufferLists
indicating the return of a receive buffer under miniport driver ownership */
m_Context->m_rxPacketsOutsideRing.AddRef();
RemoveEntryList(&pBufferDescriptor->listEntry);
m_NetNofReceiveBuffers--;
BOOLEAN packetAnalyzisRC;
packetAnalyzisRC = ParaNdis_PerformPacketAnalyzis(
#if PARANDIS_SUPPORT_RSS
&m_Context->RSSParameters,
#endif
&pBufferDescriptor->PacketInfo,
pBufferDescriptor->PhysicalPages[PARANDIS_FIRST_RX_DATA_PAGE].Virtual,
nFullLength - m_Context->nVirtioHeaderSize);
if (!packetAnalyzisRC)
{
pBufferDescriptor->Queue->ReuseReceiveBufferNoLock(pBufferDescriptor);
m_Context->Statistics.ifInErrors++;
m_Context->Statistics.ifInDiscards++;
continue;
}
#ifdef PARANDIS_SUPPORT_RSS
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);
}
else
{
ParaNdis_ReceiveQueueAddBuffer(&m_Context->ReceiveQueues[nTargetReceiveQueueNum], pBufferDescriptor);
if (nTargetReceiveQueueNum != nCurrCpuReceiveQueue)
{
ParaNdis_ProcessorNumberToGroupAffinity(&TargetAffinity, &TargetProcessor);
ParaNdis_QueueRSSDpc(m_Context, m_messageIndex, &TargetAffinity);
}
}
#else
ParaNdis_ReceiveQueueAddBuffer(&m_UnclassifiedPacketsQueue, pBufferDescriptor);
#endif
}
}
void CParaNdisRX::PopulateQueue()
{
LIST_ENTRY TempList;
CLockedContext<CNdisSpinLock> 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_Context->NetMaxReceiveBuffers--;
}
}
m_Reinsert = true;
m_VirtQueue.Kick();
}
BOOLEAN _Function_class_(MINIPORT_SYNCHRONIZE_INTERRUPT) CParaNdisRX::RestartQueueSynchronously(tSynchronizedContext *ctx)
{
CVirtQueue *queue = (CVirtQueue *) ctx->Parameter;
bool res = queue->Restart();
ParaNdis_DebugHistory(ctx->pContext, hopDPC, (PVOID)ctx->Parameter, 0x20, res, 0);
return !res;
}
BOOLEAN CParaNdisRX::RestartQueue()
{
return ParaNdis_SynchronizeWithInterrupt(m_Context,
m_messageIndex,
RestartQueueSynchronously,
&m_VirtQueue);
}