JustKernel

Ray Of Hope

QEMU + crude and simple rate limiter

For Windows paravirtualized guest NDIS driver. Manage the flow in ring buffer.

iff –git a/NetKVM/NDIS5/Common/ParaNdis-Common.c b/NetKVM/NDIS5/Common/ParaNdis-Common.c
index fe84133..c2cbf00 100644
— a/NetKVM/NDIS5/Common/ParaNdis-Common.c
+++ b/NetKVM/NDIS5/Common/ParaNdis-Common.c
@@ -805,7 +805,7 @@ NDIS_STATUS ParaNdis_InitializeContext(

pContext->ReuseBufferProc = ReuseReceiveBufferRegular;


+
NdisInitializeEvent(&pContext->ResetEvent);
DEBUG_EXIT_STATUS(0, status);
return status;
@@ -1433,7 +1433,7 @@ void ReuseReceiveBufferRegular(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBuf

/**********************************************************
It is called from Rx processing routines between power off and power on in non-paused mode (Win8).
-Returns received buffer to NetReceiveBuffers.
+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
@@ -1526,6 +1526,9 @@ tCopyPacketResult ParaNdis_DoSubmitPacket(PARANDIS_ADAPTER *pContext, tTxOperati
BOOLEAN bUseCopy = FALSE;
struct VirtIOBufferDescriptor *sg = pContext->sgTxGatherTable;

+ /* anshul: this is the end function that performs the actual task for xmit. Its called irresctive of
+ * method of transmit whether dpc or interrupt
+ */
nRequiredBuffers = Params->nofSGFragments + 1 + ((Params->flags & (pcrPriorityTag | pcrLSO)) ? 1 : 0);

result.size = 0;
@@ -1706,6 +1709,7 @@ tCopyPacketResult ParaNdis_DoSubmitPacket(PARANDIS_ADAPTER *pContext, tTxOperati
DebugDumpPacket(“sending”, ethernetHeader, 3);
InsertTailList(&pContext->NetSendBuffersInUse, &pBuffersDescriptor->listEntry);
pContext->Statistics.ifHCOutOctets += result.size;
+ /* anshul: Calculating stats*/
switch (packetType)
{
case iptBroadcast:
@@ -2721,12 +2725,9 @@ VOID ParaNdis_PowerOn(PARANDIS_ADAPTER *pContext)
ParaNdis_UpdateDeviceFilters(pContext);

InitializeListHead(&TempList);

/* submit all the receive buffers */
NdisAcquireSpinLock(&pContext->ReceiveLock);

pContext->ReuseBufferProc = ReuseReceiveBufferRegular;

while (!IsListEmpty(&pContext->NetReceiveBuffers))
{
pIONetDescriptor pBufferDescriptor =
@@ -2754,7 +2755,6 @@ VOID ParaNdis_PowerOn(PARANDIS_ADAPTER *pContext)
ParaNdis_SetPowerState(pContext, NdisDeviceStateD0);
pContext->bEnableInterruptHandlingDPC = TRUE;
VirtIODeviceAddStatus(&pContext->IODevice, VIRTIO_CONFIG_S_DRIVER_OK);

NdisReleaseSpinLock(&pContext->ReceiveLock);

// if bFastSuspendInProcess is set by Win8 power-off procedure,
@@ -2762,7 +2762,6 @@ VOID ParaNdis_PowerOn(PARANDIS_ADAPTER *pContext)
// 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);
}
@@ -2779,14 +2778,12 @@ VOID ParaNdis_PowerOff(PARANDIS_ADAPTER *pContext)
pContext->bFastSuspendInProcess = pContext->bNoPauseOnSuspend && pContext->ReceiveState == srsEnabled;
ParaNdis_Suspend(pContext);
VirtIODeviceRemoveStatus(&pContext->IODevice, VIRTIO_CONFIG_S_DRIVER_OK);

if (pContext->bFastSuspendInProcess)
{
NdisAcquireSpinLock(&pContext->ReceiveLock);
pContext->ReuseBufferProc = ReuseReceiveBufferPowerOff;
NdisReleaseSpinLock(&pContext->ReceiveLock);
}

ParaNdis_SetPowerState(pContext, NdisDeviceStateD3);

PreventDPCServicing(pContext);
diff –git a/NetKVM/NDIS5/Common/ndis56common.h b/NetKVM/NDIS5/Common/ndis56common.h
index f245daf..73d721e 100644
— a/NetKVM/NDIS5/Common/ndis56common.h
+++ b/NetKVM/NDIS5/Common/ndis56common.h
@@ -256,6 +256,19 @@ typedef struct _tagNdisStatustics
ULONG64 ifHCOutBroadcastOctets;
ULONG64 ifOutDiscards;
ULONG64 ifOutErrors;
+ /* for calculation of packet rate per sec*/
+ ULONG64 PrevIfHCOutUcastPkts;
+ ULONG64 PrevIfHCOutMulticastPkts;
+ ULONG64 PrevIfHCOutBroadcastPkts;
+ ULONG64 PrevIfHCOutUcastOctets;
+ ULONG64 PrevIfHCOutMulticastOctets;
+ ULONG64 PrevIfHCOutBroadcastOctets;
+ ULONG64 XmitBytesPerSec;
+ ULONG64 ifOutPktsPerSec;
+ ULONG64 BandwidthUsed;
+ /* statistics for bandwidth controlling */
+ ULONG64 CountDelayQueuePkts;
+ ULONG64 CountSendQueuePkts;
}NDIS_STATISTICS_INFO;

typedef PNDIS_PACKET tPacketType;
@@ -480,9 +493,11 @@ typedef struct _tagPARANDIS_ADAPTER
NDIS_HANDLE BuffersPool;
NDIS_HANDLE WrapperConfigurationHandle;
LIST_ENTRY SendQueue;
+ LIST_ENTRY DelayQueue;
LIST_ENTRY TxWaitingList;
NDIS_EVENT HaltEvent;
NDIS_TIMER ConnectTimer;
+ NDIS_TIMER RateCalculatorTimer;
NDIS_TIMER DPCPostProcessTimer;
BOOLEAN bDmaInitialized;
#endif
diff –git a/NetKVM/NDIS5/wxp/ParaNdis5-Driver.c b/NetKVM/NDIS5/wxp/ParaNdis5-Driver.c
index f6892da..01cb1f1 100644
— a/NetKVM/NDIS5/wxp/ParaNdis5-Driver.c
+++ b/NetKVM/NDIS5/wxp/ParaNdis5-Driver.c
@@ -324,6 +324,7 @@ static VOID ParaNdis5_Halt(
WaitHaltEvent(pContext, “Receive”);
ParaNdis_CleanupContext(pContext);
NdisCancelTimer(&pContext->DPCPostProcessTimer, &bUnused);
+ NdisCancelTimer(&pContext->RateCalculatorTimer, &bUnused);
ParaNdis_DebugHistory(pContext, hopHalt, NULL, 0, 0, 0);
ParaNdis_DebugRegisterMiniport(pContext, FALSE);
NdisFreeMemory(pContext, 0, 0);
diff –git a/NetKVM/NDIS5/wxp/ParaNdis5-Impl.c b/NetKVM/NDIS5/wxp/ParaNdis5-Impl.c
index a1c6da1..ab990f0 100644
— a/NetKVM/NDIS5/wxp/ParaNdis5-Impl.c
+++ b/NetKVM/NDIS5/wxp/ParaNdis5-Impl.c
@@ -29,7 +29,8 @@ Per-packet information holder
#define SEND_ENTRY_UDP_CS 0x0010
#define SEND_ENTRY_IP_CS 0x0020


+#define BANDWIDTH_LIMIT 10 /* In Mega bits per second*/
+#define MAX_DELAY_QUEUE_LIMIT 100000 /* packets */

typedef struct _tagSendEntry
{
@@ -194,6 +195,48 @@ static VOID OnDPCPostProcessTimer(
}

/**********************************************************
+periodic call back for ratecalulatortimer to calculate packet rate after every second.
+Parameters:
+ context (on FunctionContext)
+ all the rest are irrelevant
+***********************************************************/
+static VOID OnRateCalculatorProcessTimer(
+ IN PVOID SystemSpecific1,
+ IN PVOID FunctionContext,
+ IN PVOID SystemSpecific2,
+ IN PVOID SystemSpecific3
+ )
+{
+ PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)FunctionContext;
+ UINT64 rate;
+ rate = (pContext->Statistics.ifHCOutUcastPkts + pContext->Statistics.ifHCOutBroadcastPkts +
+ pContext->Statistics.ifHCOutMulticastPkts) –
+ (pContext->Statistics.PrevIfHCOutUcastPkts + pContext->Statistics.PrevIfHCOutBroadcastPkts +
+ pContext->Statistics.PrevIfHCOutMulticastPkts);
+
+ pContext->Statistics.PrevIfHCOutUcastPkts = pContext->Statistics.ifHCOutUcastPkts;
+ pContext->Statistics.PrevIfHCOutBroadcastPkts = pContext->Statistics.ifHCOutBroadcastPkts;
+ pContext->Statistics.PrevIfHCOutMulticastPkts = pContext->Statistics.ifHCOutMulticastPkts;
+
+ pContext->Statistics.XmitBytesPerSec = (pContext->Statistics.ifHCOutUcastOctets + pContext->Statistics.ifHCOutBroadcastOctets +
+ pContext->Statistics.ifHCOutMulticastOctets) –
+ (pContext->Statistics.PrevIfHCOutUcastPkts + pContext->Statistics.PrevIfHCOutBroadcastOctets +
+ pContext->Statistics.PrevIfHCOutMulticastOctets);
+
+ pContext->Statistics.PrevIfHCOutUcastOctets = pContext->Statistics.ifHCOutUcastOctets;
+ pContext->Statistics.PrevIfHCOutBroadcastOctets = pContext->Statistics.ifHCOutBroadcastOctets;
+ pContext->Statistics.PrevIfHCOutMulticastOctets = pContext->Statistics.ifHCOutMulticastOctets;
+ /* reset the bandwidth used timer after every second.*/
+ pContext->Statistics.BandwidthUsed = 0;
+
+ DPrintf(0, (“Packet Rate per second = %d\n”, rate));
+ /* initialize and fire the timer again.*/
+ NdisInitializeTimer(&pContext->RateCalculatorTimer, OnRateCalculatorProcessTimer, pContext);
+ /* fire the timer again so that after every 1 sec we can calculate the rate.. */
+ NdisSetTimer(&pContext->RateCalculatorTimer, 1000);
+}
+
+/**********************************************************
NDIS5 implementation of shared memory freeing
Parameters:
context
@@ -270,7 +313,10 @@ NDIS_STATUS ParaNdis_FinishSpecificInitialization(
InitializeListHead(&pContext->TxWaitingList);
NdisInitializeTimer(&pContext->ConnectTimer, OnConnectTimer, pContext);
NdisInitializeTimer(&pContext->DPCPostProcessTimer, OnDPCPostProcessTimer, pContext);

+ /* initialize the rate calculator timer that fires after every 1 sec.*/
+ NdisInitializeTimer(&pContext->RateCalculatorTimer, OnRateCalculatorProcessTimer, pContext);
+ /* fire the timer to start working right at the initial stage. Call at the starting. */
+ NdisSetTimer(&pContext->RateCalculatorTimer, 1000);
status = NdisMRegisterInterrupt(
&pContext->Interrupt,
pContext->MiniportHandle,
@@ -532,6 +578,7 @@ tPacketIndicationType ParaNdis_IndicateReceivedPacket(
DPrintf(4, (“[%s] buffer %p(%d b.)”, __FUNCTION__, pBuffersDesc, length));
if (!bPrepareOnly)
{
+ /* anshul: indicate to NDIS that packet is avaialble to be trasmitted to bound protocol driver.*/
NdisMIndicateReceivePacket(
pContext->MiniportHandle,
&Packet,
@@ -926,7 +973,7 @@ static void InitializeTransferParameters(tTxOperationParameters *pParams, tSendE
if (pEntry->PriorityDataLong) flags |= pcrPriorityTag;
pParams->flags = flags;
}

+/* anshul: This is the commong function called for xmiting whether Dpc mechanism is used or interrupt mechanism is used*/
BOOLEAN ParaNdis_ProcessTx(
PARANDIS_ADAPTER *pContext,
BOOLEAN IsDpc,
@@ -1238,6 +1285,9 @@ VOID ParaNdis5_SendPackets(IN NDIS_HANDLE MiniportAdapterContext,
UINT i;
LIST_ENTRY FailedList, DoneList;
PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
+ UINT64 BandwidthBytes = (BANDWIDTH_LIMIT * 1024 * 1024) / 8;
+ ULONG PacketLength;
+ tSendEntry *pEntry;
InitializeListHead(&FailedList);
InitializeListHead(&DoneList);
DPrintf(3, (“[%s] %d packets”, __FUNCTION__, NumberOfPackets));
@@ -1264,6 +1314,7 @@ VOID ParaNdis5_SendPackets(IN NDIS_HANDLE MiniportAdapterContext,
UINT nFragments = 0;
GET_NUMBER_OF_SG_ELEMENTS(PacketArray[i], &nFragments);
ParaNdis_DebugHistory(pContext, hopSendPacketMapped, PacketArray[i], 0, nFragments, 0);
+ pContext->Statistics.CountSendQueuePkts++;
InsertTailList(&pContext->SendQueue, &pse->list);
}
}
@@ -1278,7 +1329,66 @@ VOID ParaNdis5_SendPackets(IN NDIS_HANDLE MiniportAdapterContext,
}

NdisReleaseSpinLock(&pContext->SendLock);

+ /* make a queue to ratelimit the packets to be sent for xmit. Implement our own queue here
+ * so as to rate limit the packets to be added to the virtio transmit queue.
+ */
+ InitializeListHead(&pContext->DelayQueue);
+ NdisAcquireSpinLock(&pContext->SendLock);/* for packet delay also I need this lock.*/
+
+ /* Rate Limiter: Crude/simplistic rate limiter. Not considering the priority of the packets and other parameters. */
+ if (pContext->Statistics.XmitBytesPerSec > BandwidthBytes) /* bandwidth used is more than allowed. */
+ {
+ while (!IsListEmpty(&pContext->SendQueue))
+ {
+ /* transfer all the packets from the sendQ to the DelayQ and then transfer only the required number
+ * of NDIS packets (based on the bandwidth ) limitation back to Sendqueu. Dirty mechanism but so
+ * currently achieves it purpose.
+ */
+ pEntry = (tSendEntry *)RemoveHeadList(&pContext->SendQueue);
+ /* remove from head of send queue and insert into tail of Delay queue so that the order is maintained.*/
+ InsertTailList(&pContext->DelayQueue, &pEntry->list);
+ pContext->Statistics.CountDelayQueuePkts++;
+ }
+ while (pContext->Statistics.BandwidthUsed <= BandwidthBytes) + { + pEntry = (tSendEntry *)RemoveHeadList(&pContext->DelayQueue);
+ InsertTailList(&pContext->SendQueue, &pEntry->list);
+ pContext->Statistics.CountDelayQueuePkts–;
+ NdisQueryPacket(pEntry->packet, NULL, NULL, NULL, &PacketLength);
+ pContext->Statistics.BandwidthUsed += PacketLength;
+ }
+ /* handle the case if your delaylist is becoming quite large. Then drop the packets.*/
+ if (pContext->Statistics.CountDelayQueuePkts > MAX_DELAY_QUEUE_LIMIT )
+ {
+ /* if queue size becomes long then drop packets from tail of Delay queue and call completPacket for
+ * dropped packets.
+ */
+ pEntry = (tSendEntry *)RemoveTailList(&pContext->DelayQueue);
+ CompletePacket(pContext, pEntry->packet);
+ pContext->Statistics.CountDelayQueuePkts–;
+ }
+ }
+ else
+ {
+ /* Maybe there is a condition where previous bandwidth usage was overshooting but now its controlled.
+ * So XMitBytesPerSecond will be in control. But due to previous overshoot, there may be some packets
+ * in Delayqueue. So, add them to send queue so that they get their chance of xmit.
+ *. Currently I am totally emptying the delay queue in one instance, but this may be wrong as it may overflow
+ * send queue. So this needs to be handled.
+ */
+ while (!IsListEmpty(&pContext->DelayQueue))
+ {
+ pEntry = (tSendEntry *)RemoveHeadList(&pContext->DelayQueue);
+ InsertTailList(&pContext->SendQueue, &pEntry->list);
+ pContext->Statistics.CountDelayQueuePkts–;
+ pContext->Statistics.CountSendQueuePkts++;
+ }
+ if (pContext->Statistics.CountDelayQueuePkts)
+ {
+ DPrintf(0, (“ERROR: Should Not Happen….\n”)); /* erraneious condition. Should not happen*/
+ }
+ }
+ NdisReleaseSpinLock(&pContext->SendLock);
ParaNdis_ProcessTx(pContext, FALSE, FALSE);
}

Tags:


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.