669
714
* @param cb Number of bytes available in the buffer.
672
static int vnetHandleRxPacket(PVNETSTATE pState, const void *pvBuf, size_t cb)
717
static int vnetHandleRxPacket(PVNETSTATE pState, const void *pvBuf, size_t cb,
718
PCPDMNETWORKGSO pGso)
677
hdr.u8GSOType = VNETHDR_GSO_NONE;
723
RTGCPHYS addrHdrMrx = 0;
727
Log2(("%s vnetHandleRxPacket: gso type=%x cbHdr=%u mss=%u"
728
" off1=0x%x off2=0x%x\n", INSTANCE(pState), pGso->u8Type,
729
pGso->cbHdrs, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
730
Hdr.Hdr.u8Flags = VNETHDR_F_NEEDS_CSUM;
731
switch (pGso->u8Type)
733
case PDMNETWORKGSOTYPE_IPV4_TCP:
734
Hdr.Hdr.u8GSOType = VNETHDR_GSO_TCPV4;
735
Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
737
case PDMNETWORKGSOTYPE_IPV6_TCP:
738
Hdr.Hdr.u8GSOType = VNETHDR_GSO_TCPV6;
739
Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
741
case PDMNETWORKGSOTYPE_IPV4_UDP:
742
Hdr.Hdr.u8GSOType = VNETHDR_GSO_UDP;
743
Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
746
return VERR_INVALID_PARAMETER;
748
Hdr.Hdr.u16HdrLen = pGso->cbHdrs;
749
Hdr.Hdr.u16GSOSize = pGso->cbMaxSeg;
750
Hdr.Hdr.u16CSumStart = pGso->offHdr2;
751
STAM_REL_COUNTER_INC(&pState->StatReceiveGSO);
756
Hdr.Hdr.u8GSOType = VNETHDR_GSO_NONE;
759
if (vnetMergeableRxBuffers(pState))
760
uHdrLen = sizeof(VNETHDRMRX);
762
uHdrLen = sizeof(VNETHDR);
679
764
vnetPacketDump(pState, (const uint8_t*)pvBuf, cb, "<-- Incoming");
681
766
unsigned int uOffset = 0;
682
for (unsigned int nElem = 0; uOffset < cb; nElem++)
768
for (nElem = 0; uOffset < cb; nElem++)
685
unsigned int nSeg = 0, uElemSize = 0;
771
unsigned int nSeg = 0, uElemSize = 0, cbReserved = 0;
687
773
if (!vqueueGet(&pState->VPCI, pState->pRxQueue, &elem))
776
* @todo: It is possible to run out of RX buffers if only a few
777
* were added and we received a big packet.
689
779
Log(("%s vnetHandleRxPacket: Suddenly there is no space in receive queue!\n", INSTANCE(pState)));
690
780
return VERR_INTERNAL_ERROR;
701
/* The very first segment of the very first element gets the header. */
702
if (elem.aSegsIn[nSeg].cb != sizeof(VNETHDR))
704
Log(("%s vnetHandleRxPacket: The first descriptor does match the header size!\n", INSTANCE(pState)));
705
return VERR_INTERNAL_ERROR;
708
elem.aSegsIn[nSeg++].pv = &hdr;
709
uElemSize += sizeof(VNETHDR);
791
if (vnetMergeableRxBuffers(pState))
793
addrHdrMrx = elem.aSegsIn[nSeg].addr;
794
cbReserved = uHdrLen;
798
/* The very first segment of the very first element gets the header. */
799
if (elem.aSegsIn[nSeg].cb != sizeof(VNETHDR))
801
Log(("%s vnetHandleRxPacket: The first descriptor does match the header size!\n", INSTANCE(pState)));
802
return VERR_INTERNAL_ERROR;
804
elem.aSegsIn[nSeg++].pv = &Hdr;
806
uElemSize += uHdrLen;
712
808
while (nSeg < elem.nIn && uOffset < cb)
714
unsigned int uSize = RT_MIN(elem.aSegsIn[nSeg].cb, cb - uOffset);
810
unsigned int uSize = (unsigned int)RT_MIN(elem.aSegsIn[nSeg].cb - (nSeg?0:cbReserved),
715
812
elem.aSegsIn[nSeg++].pv = (uint8_t*)pvBuf + uOffset;
716
813
uOffset += uSize;
717
814
uElemSize += uSize;
719
816
STAM_PROFILE_START(&pState->StatReceiveStore, a);
720
vqueuePut(&pState->VPCI, pState->pRxQueue, &elem, uElemSize);
817
vqueuePut(&pState->VPCI, pState->pRxQueue, &elem, uElemSize, cbReserved);
721
818
STAM_PROFILE_STOP(&pState->StatReceiveStore, a);
819
if (!vnetMergeableRxBuffers(pState))
823
if (vnetMergeableRxBuffers(pState))
825
Hdr.u16NumBufs = nElem;
826
int rc = PDMDevHlpPhysWrite(pState->VPCI.CTX_SUFF(pDevIns), addrHdrMrx,
830
Log(("%s vnetHandleRxPacket: Failed to write merged RX buf header: %Rrc\n",
831
INSTANCE(pState), rc));
723
835
vqueueSync(&pState->VPCI, pState->pRxQueue);
838
Log(("%s vnetHandleRxPacket: Packet did not fit into RX queue (packet size=%u)!\n",
839
INSTANCE(pState), cb));
840
return VERR_TOO_MUCH_DATA;
725
843
return VINF_SUCCESS;
729
* @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
847
* @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
731
static DECLCALLBACK(int) vnetNetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
849
static DECLCALLBACK(int) vnetNetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface,
850
const void *pvBuf, size_t cb,
851
PCPDMNETWORKGSO pGso)
733
853
VNETSTATE *pState = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkDown);
735
Log2(("%s vnetNetworkDown_Receive: pvBuf=%p cb=%u\n", INSTANCE(pState), pvBuf, cb));
855
Log2(("%s vnetNetworkDown_ReceiveGso: pvBuf=%p cb=%u pGso=%p\n",
856
INSTANCE(pState), pvBuf, cb, pGso));
736
857
int rc = vnetCanReceive(pState);
737
858
if (RT_FAILURE(rc))
832
961
vnetWakeupReceive(pState->VPCI.CTX_SUFF(pDevIns));
965
* Sets up the GSO context according to the Virtio header.
967
* @param pGso The GSO context to setup.
968
* @param pCtx The context descriptor.
970
DECLINLINE(PPDMNETWORKGSO) vnetSetupGsoCtx(PPDMNETWORKGSO pGso, VNETHDR const *pHdr)
972
pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
974
if (pHdr->u8GSOType & VNETHDR_GSO_ECN)
976
AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
979
switch (pHdr->u8GSOType & ~VNETHDR_GSO_ECN)
981
case VNETHDR_GSO_TCPV4:
982
pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
984
case VNETHDR_GSO_TCPV6:
985
pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
987
case VNETHDR_GSO_UDP:
988
pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
993
if (pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)
994
pGso->offHdr2 = pHdr->u16CSumStart;
997
AssertMsgFailed(("GSO without checksum offloading!\n"));
1000
pGso->offHdr1 = sizeof(RTNETETHERHDR);
1001
pGso->cbHdrs = pHdr->u16HdrLen;
1002
pGso->cbMaxSeg = pHdr->u16GSOSize;
1006
DECLINLINE(uint16_t) vnetCSum16(const void *pvBuf, size_t cb)
1009
uint16_t *pu16 = (uint16_t *)pvBuf;
1017
csum += *(uint8_t*)pu16;
1019
csum = (csum >> 16) + (csum & 0xFFFF);
1023
DECLINLINE(void) vnetCompleteChecksum(uint8_t *pBuf, unsigned cbSize, uint16_t uStart, uint16_t uOffset)
1025
*(uint16_t*)(pBuf + uStart + uOffset) = vnetCSum16(pBuf + uStart, cbSize - uStart);
835
1028
static void vnetTransmitPendingPackets(PVNETSTATE pState, PVQUEUE pQueue, bool fOnWorkerThread)
870
1069
while (vqueueGet(&pState->VPCI, pQueue, &elem))
872
1071
unsigned int uOffset = 0;
873
if (elem.nOut < 2 || elem.aSegsOut[0].cb != sizeof(VNETHDR))
1072
if (elem.nOut < 2 || elem.aSegsOut[0].cb != uHdrLen)
875
1074
Log(("%s vnetQueueTransmit: The first segment is not the header! (%u < 2 || %u != %u).\n",
876
INSTANCE(pState), elem.nOut, elem.aSegsOut[0].cb, sizeof(VNETHDR)));
1075
INSTANCE(pState), elem.nOut, elem.aSegsOut[0].cb, uHdrLen));
877
1076
break; /* For now we simply ignore the header, but it must be there anyway! */
1080
unsigned int uSize = 0;
881
1081
STAM_PROFILE_ADV_START(&pState->StatTransmit, a);
882
/* Assemble a complete frame. */
883
for (unsigned int i = 1; i < elem.nOut && uOffset < VNET_MAX_FRAME_SIZE; i++)
885
unsigned int uSize = elem.aSegsOut[i].cb;
886
if (uSize > VNET_MAX_FRAME_SIZE - uOffset)
888
Log(("%s vnetQueueTransmit: Packet is too big (>64k), truncating...\n", INSTANCE(pState)));
889
uSize = VNET_MAX_FRAME_SIZE - uOffset;
891
PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[i].addr,
892
pState->pTxBuf + uOffset, uSize);
1082
/* Compute total frame size. */
1083
for (unsigned int i = 1; i < elem.nOut; i++)
1084
uSize += elem.aSegsOut[i].cb;
1085
Assert(uSize <= VNET_MAX_FRAME_SIZE);
895
1086
if (pState->pDrv)
897
vnetPacketDump(pState, pState->pTxBuf, uOffset, "--> Outgoing");
1089
PDMNETWORKGSO Gso, *pGso;
1091
PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[0].addr,
1094
STAM_REL_COUNTER_INC(&pState->StatTransmitPackets);
899
1096
STAM_PROFILE_START(&pState->StatTransmitSend, a);
1098
pGso = vnetSetupGsoCtx(&Gso, &Hdr);
901
1099
/** @todo Optimize away the extra copying! (lazy bird) */
902
1100
PPDMSCATTERGATHER pSgBuf;
903
int rc = pState->pDrv->pfnAllocBuf(pState->pDrv, uOffset, NULL /*pGso*/, &pSgBuf);
1101
int rc = pState->pDrv->pfnAllocBuf(pState->pDrv, uSize, pGso, &pSgBuf);
904
1102
if (RT_SUCCESS(rc))
906
1104
Assert(pSgBuf->cSegs == 1);
907
memcpy(pSgBuf->aSegs[0].pvSeg, pState->pTxBuf, uOffset);
908
pSgBuf->cbUsed = uOffset;
1105
/* Assemble a complete frame. */
1106
for (unsigned int i = 1; i < elem.nOut; i++)
1108
PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[i].addr,
1109
((uint8_t*)pSgBuf->aSegs[0].pvSeg) + uOffset,
1110
elem.aSegsOut[i].cb);
1111
uOffset += elem.aSegsOut[i].cb;
1113
pSgBuf->cbUsed = uSize;
1114
vnetPacketDump(pState, (uint8_t*)pSgBuf->aSegs[0].pvSeg, uSize, "--> Outgoing");
1117
/* Some guests (RHEL) may report HdrLen excluding transport layer header! */
1119
* We cannot use cdHdrs provided by the guest because of different ways
1120
* it gets filled out by different versions of kernels.
1122
//if (pGso->cbHdrs < Hdr.u16CSumStart + Hdr.u16CSumOffset + 2)
1124
Log4(("%s vnetTransmitPendingPackets: HdrLen before adjustment %d.\n",
1125
INSTANCE(pState), pGso->cbHdrs));
1126
switch (pGso->u8Type)
1128
case PDMNETWORKGSOTYPE_IPV4_TCP:
1129
case PDMNETWORKGSOTYPE_IPV6_TCP:
1130
pGso->cbHdrs = Hdr.u16CSumStart +
1131
((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + Hdr.u16CSumStart))->th_off * 4;
1133
case PDMNETWORKGSOTYPE_IPV4_UDP:
1134
pGso->cbHdrs = Hdr.u16CSumStart + sizeof(RTNETUDP);
1137
/* Update GSO structure embedded into the frame */
1138
((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrs = pGso->cbHdrs;
1139
Log4(("%s vnetTransmitPendingPackets: adjusted HdrLen to %d.\n",
1140
INSTANCE(pState), pGso->cbHdrs));
1142
Log2(("%s vnetTransmitPendingPackets: gso type=%x cbHdr=%u mss=%u"
1143
" off1=0x%x off2=0x%x\n", INSTANCE(pState), pGso->u8Type,
1144
pGso->cbHdrs, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
1145
STAM_REL_COUNTER_INC(&pState->StatTransmitGSO);
1147
else if (Hdr.u8Flags & VNETHDR_F_NEEDS_CSUM)
1149
STAM_REL_COUNTER_INC(&pState->StatTransmitCSum);
1151
* This is not GSO frame but checksum offloading is requested.
1153
vnetCompleteChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, uSize,
1154
Hdr.u16CSumStart, Hdr.u16CSumOffset);
909
1157
rc = pState->pDrv->pfnSendBuf(pState->pDrv, pSgBuf, false);
1160
LogRel(("virtio-net: failed to allocate SG buffer: size=%u rc=%Rrc\n", uSize, rc));
912
1162
STAM_PROFILE_STOP(&pState->StatTransmitSend, a);
913
1163
STAM_REL_COUNTER_ADD(&pState->StatTransmitBytes, uOffset);
1714
1956
/* Interfaces */
1715
1957
pState->INetworkDown.pfnWaitReceiveAvail = vnetNetworkDown_WaitReceiveAvail;
1716
1958
pState->INetworkDown.pfnReceive = vnetNetworkDown_Receive;
1959
pState->INetworkDown.pfnReceiveGso = vnetNetworkDown_ReceiveGso;
1717
1960
pState->INetworkDown.pfnXmitPending = vnetNetworkDown_XmitPending;
1719
1962
pState->INetworkConfig.pfnGetMac = vnetGetMac;
1720
1963
pState->INetworkConfig.pfnGetLinkState = vnetGetLinkState;
1721
1964
pState->INetworkConfig.pfnSetLinkState = vnetSetLinkState;
1723
pState->pTxBuf = (uint8_t *)RTMemAllocZ(VNET_MAX_FRAME_SIZE);
1724
AssertMsgReturn(pState->pTxBuf,
1725
("Cannot allocate TX buffer for virtio-net device\n"), VERR_NO_MEMORY);
1727
1966
/* Initialize critical section. */
1728
1967
// char szTmp[sizeof(pState->VPCI.szInstance) + 2];
1729
1968
// RTStrPrintf(szTmp, sizeof(szTmp), "%sRX", pState->VPCI.szInstance);
1804
2043
rc = vnetReset(pState);
1807
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/VNet%d/ReceiveBytes", iInstance);
1808
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/VNet%d/TransmitBytes", iInstance);
2046
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/VNet%d/Bytes/Receive", iInstance);
2047
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/VNet%d/Bytes/Transmit", iInstance);
2048
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveGSO, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of received GSO packets", "/Devices/VNet%d/Packets/ReceiveGSO", iInstance);
2049
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitPackets, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of sent packets", "/Devices/VNet%d/Packets/Transmit", iInstance);
2050
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitGSO, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of sent GSO packets", "/Devices/VNet%d/Packets/Transmit-Gso", iInstance);
2051
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitCSum, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of completed TX checksums", "/Devices/VNet%d/Packets/Transmit-Csum", iInstance);
1809
2052
#if defined(VBOX_WITH_STATISTICS)
1810
2053
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/VNet%d/Receive/Total", iInstance);
1811
2054
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveStore, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive storing", "/Devices/VNet%d/Receive/Store", iInstance);