VirtualBox

Changeset 82826 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Jan 22, 2020 1:20:42 PM (5 years ago)
Author:
vboxsync
Message:

Network/DevVirtioNet_1_0.cpp: Have gone through the functions and basically ported them to VirtIO 1.0, but haven't tried compiling, so still a lot of work to get it to build and run. See BugRef(8651)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Network/DevVirtioNet_1_0.cpp

    r82780 r82826  
    6060#include "VBoxDD.h"
    6161
    62 /*
    63  * GSO = Generic Segmentation Offload
    64  * TSO = TCP Segmentation Offload */
    6562
    6663#define VIRTIONET_SAVED_STATE_VERSION          UINT32_C(1)
     
    7067#define VIRTIONET_MAC_FILTER_LEN               32
    7168#define VIRTIONET_MAX_VLAN_ID                  (1 << 12)
    72 #define VIRTIONET_PREALLOCATE_RX_SEG_COUNT    32
     69#define VIRTIONET_PREALLOCATE_RX_SEG_COUNT     32
    7370
    7471#define QUEUE_NAME(a_pVirtio, a_idxQueue)   ((a_pVirtio)->virtqState[(a_idxQueue)].szVirtqName)
     
    7774#define FEATURE_ENABLED(feature) (pThis->fNegotiatedFeatures & VIRTIONET_F_##feature)
    7875#define FEATURE_DISABLED(feature) (!FEATURE_ENABLED(feature))
     76#define FEATURE_OFFERED(feature) (VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_##feature)
     77#define INSTANCE(pState) pState->szInstanceName;
     78
     79#define SET_LINK_UP(pState) \
     80            pState->virtioNetConfig.uStatus |= VIRTIONET_F_LINK_UP; \
     81            virtioCoreNotifyConfigChanged(&pThis->Virtio)
     82
     83#define SET_LINK_DOWN(pState) \
     84            pState->virtioNetConfig.uStatus &= !VIRTIONET_F_LINK_UP; \
     85            virtioCoreNotifyConfigChanged(&pThis->Virtio)
     86
     87#define IS_LINK_UP(pState)  (pState->virtioNetConfig.uStatus & VIRTIONET_F_LINK_UP)
     88#define IS_LINK_DOWN(pState) !IS_LINK_UP(pState)
    7989
    8090/* Macros to calculate queue specific index number VirtIO 1.0, 5.1.2 */
     
    161171 * MMIO accesses to device-specific configuration parameters.
    162172 */
     173
     174#pragma pack(1)
    163175typedef struct virtio_net_config
    164176{
    165     uint8_t  uMacAddress[6];                                    /**< mac                                            */
    166 #if VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_STATUS
     177    RTMAC  uMacAddress;                                         /**< mac                                            */
     178#if FEATURE_OFFERED(STATUS)
    167179    uint16_t uStatus;                                           /**< status                                         */
    168180#endif
    169 #if VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_MQ
     181#if FEATURE_OFFERED(MQ)
    170182    uint16_t uMaxVirtqPairs;                                    /**< max_virtq_pairs                                */
    171183#endif
    172184} VIRTIONET_CONFIG_T, PVIRTIONET_CONFIG_T;
     185#pragma pack()
    173186
    174187#define VIRTIONET_F_LINK_UP                RT_BIT_16(1)         /**< config status: Link is up                      */
     
    881894static void virtioNetR3QuiesceDevice(PPDMDEVINS pDevIns, VIRTIOVMSTATECHANGED enmQuiescingFor)
    882895{
    883     PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    884     PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
     896    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
     897    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
    885898
    886899    RT_NOREF(pThis);
     
    902915static DECLCALLBACK(void) virtioNetR3Reset(PPDMDEVINS pDevIns)
    903916{
     917    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    904918    LogFunc(("\n"));
    905     PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    906919    pThis->fResetting = true;
    907920    virtioNetR3QuiesceDevice(pDevIns, kvirtIoVmStateChangedReset);
     
    915928    LogFunc(("\n"));
    916929
    917     PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    918     PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
     930    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
     931    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
    919932
    920933    RT_NOREF2(pThis, pThisCC);
     
    953966static DECLCALLBACK(void) virtioNetR3Resume(PPDMDEVINS pDevIns)
    954967{
    955     PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    956     PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
     968    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
     969    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
    957970    LogFunc(("\n"));
    958971
     
    978991}
    979992
    980 /* Device operation: Net header packet (VirtIO 1.0, 5.1.6) */
    981 #pragma pack(1)
    982 struct virtio_net_hdr {
    983     uint8_t  uFlags;                                           /**< flags                                           */
    984     uint8_t  uGsoType;                                         /**< gso_type                                        */
    985     uint16_t uHdrLen;                                          /**< hdr_len                                         */
    986     uint16_t uGsoSize;                                         /**< gso_size                                        */
    987     uint16_t uChksumStart;                                     /**< csum_start                                      */
    988     uint16_t uChksumOffset;                                    /**< csum_offset                                     */
    989     uint16_t uNumBuffers;                                      /**< num_buffers                                     */
    990 };
    991 #pragma pack()
    992 typedef virtio_net_hdr VIRTIONET_PKT_HDR_T, *PVIRTIONET_PKT_HDR_T;
    993 AssertCompileSize(VIRTIONET_PKT_HDR_T, 12);
    994 
     993/**
     994 * @callback_method_impl{FNIOMIOPORTNEWIN}
     995 */
     996static DECLCALLBACK(VBOXSTRICTRC) vnetIOPortIn(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
     997{
     998    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
     999    RT_NOREF(pvUser);
     1000    return vpciIOPortIn(pDevIns, &pThis->VPCI, offPort, pu32, cb, &g_IOCallbacks);
     1001}
     1002
     1003
     1004/**
     1005 * @callback_method_impl{FNIOMIOPORTNEWOUT}
     1006 */
     1007static DECLCALLBACK(VBOXSTRICTRC) vnetIOPortOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
     1008{
     1009    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
     1010    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
     1011    RT_NOREF(pvUser);
     1012    return vpciIOPortOut(pDevIns, &pThis->VPCI, &pThisCC->VPCI, offPort, u32, cb, &g_IOCallbacks);
     1013}
     1014
     1015
     1016#ifdef IN_RING3
     1017/**
     1018 * @interface_method_impl{PDMIBASE,pfnQueryInterface,
     1019 * For VNETSTATECC::VPCI.IBase}
     1020 */
     1021static DECLCALLBACK(void *) virtioNetQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
     1022{
     1023    PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, PVIRTIONETCC, VPCI.IBase);
     1024
     1025    PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
     1026    PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
     1027
     1028    return vpciR3QueryInterface(&pThisCC->VPCI, pszIID);
     1029}
     1030
     1031/**
     1032 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
     1033 */
     1034static DECLCALLBACK(int) virtioNetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
     1035{
     1036    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
     1037    PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, PVIRTIONETCC, INetworkDown);
     1038    PPDMDEVINS   pDevIns = pThisCC->pDevIns;
     1039
     1040    LogFlowFunc(("%s: cMillies=%u\n", INSTANCE(pThis), cMillies));
     1041
     1042    int rc = virtioNetR3CanReceive(pDevIns, pThis, pThisCC);
     1043    if (RT_SUCCESS(rc))
     1044        return VINF_SUCCESS;
     1045
     1046    if (cMillies == 0)
     1047        return VERR_NET_NO_BUFFER_SPACE;
     1048
     1049    rc = VERR_INTERRUPTED;
     1050    ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
     1051    STAM_PROFILE_START(&pThis->StatRxOverflow, a);
     1052
     1053    VMSTATE enmVMState;
     1054    while (RT_LIKELY(  (enmVMState = PDMDevHlpVMState(pDevIns)) == VMSTATE_RUNNING
     1055                     || enmVMState == VMSTATE_RUNNING_LS))
     1056    {
     1057        int rc2 = virtioNetR3CanReceive(pDevIns, pThis, pThisCC);
     1058        if (RT_SUCCESS(rc2))
     1059        {
     1060            rc = VINF_SUCCESS;
     1061            break;
     1062        }
     1063        LogFunc(("%s: waiting cMillies=%u...\n", INSTANCE(pThis), cMillies));
     1064        rc2 = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventMoreRxDescAvail, cMillies);
     1065        if (RT_FAILURE(rc2) && rc2 != VERR_TIMEOUT && rc2 != VERR_INTERRUPTED)
     1066            RTThreadSleep(1);
     1067    }
     1068    STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
     1069    ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
     1070
     1071    LogFlowFunc(("%s: -> %d\n", INSTANCE(pThis), rc));
     1072    return rc;
     1073}
     1074/**
     1075 * Check if the device can receive data now.
     1076 * This must be called before the pfnRecieve() method is called.
     1077 *
     1078 * @remarks As a side effect this function enables queue notification
     1079 *          if it cannot receive because the queue is empty.
     1080 *          It disables notification if it can receive.
     1081 *
     1082 * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
     1083 * @thread  RX
     1084 */
     1085static int virtioNetR3CanReceive(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
     1086{
     1087    int rc = virtioNetCsRxEnter(pThis, VERR_SEM_BUSY);
     1088    AssertRCReturn(rc, rc);
     1089
     1090    LogFlowFunc(("%s:\n", INSTANCE(pThis)));
     1091
     1092    if (!pThis->fVirtioReady)
     1093        rc = VERR_NET_NO_BUFFER_SPACE;
     1094
     1095    else if (!virtioCoreIsQueueEnabled(&pThis->Virtio, RXQIDX(0)))
     1096        rc = VERR_NET_NO_BUFFER_SPACE;
     1097
     1098    else if (virtioCoreQueueIsEmpty(pDevIns, pThis->Virtio, RXQIDX(0)))
     1099    {
     1100        virtioCoreQueueSetNotify(&pThis->pVirtio, RXQIDX(0), true);
     1101        rc = VERR_NET_NO_BUFFER_SPACE;
     1102    }
     1103    else
     1104    {
     1105        virtioCoreQueueSetNotify(&pThis->pVirtio, RXQIDX(0), false);
     1106        rc = VINF_SUCCESS;
     1107    }
     1108
     1109    LogFlowFunc(("%s: -> %Rrc\n", INSTANCE(pThis), rc));
     1110    virtioNetCsRxLeave(pThis);
     1111    return rc;
     1112}
     1113/**
     1114 * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
     1115 */
     1116static DECLCALLBACK(void) virtioNetR3LinkUpTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
     1117{
     1118    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
     1119    PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
     1120    RT_NOREF(pTimer, pvUser);
     1121
     1122    int rc = virtioNetR3CsEnter(pDevIns, pThis, VERR_SEM_BUSY);
     1123    AssertRCReturnVoid(rc);
     1124
     1125    SET_LINK_UP(pThis);
     1126
     1127    virtioNetWakeupReceive(pDevIns);
     1128
     1129    virtioNetR3CsLeave(pDevIns, pThis);
     1130
     1131    LogFunc(("%s: Link is up\n", INSTANCE(pThis)));
     1132    if (pThisCC->pDrv)
     1133        pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, PDMNETWORKLINKSTATE_UP);
     1134}
     1135
     1136
     1137/**
     1138 * Takes down the link temporarily if it's current status is up.
     1139 *
     1140 * This is used during restore and when replumbing the network link.
     1141 *
     1142 * The temporary link outage is supposed to indicate to the OS that all network
     1143 * connections have been lost and that it for instance is appropriate to
     1144 * renegotiate any DHCP lease.
     1145 *
     1146 * @param   pDevIns     The device instance.
     1147 * @param   pThis       The virtio-net shared instance data.
     1148 * @param   pThisCC     The virtio-net ring-3 instance data.
     1149 */
     1150static void virtioNetR3TempLinkDown(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
     1151{
     1152    if (IS_LINK_UP(pThis))
     1153    {
     1154        SET_LINK_DOWN(pThis);
     1155
     1156        /* Restore the link back in 5 seconds. */
     1157        int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, pThis->cMsLinkUpDelay);
     1158        AssertRC(rc);
     1159
     1160        LogFunc(("%s: Link is down temporarily\n", INSTANCE(pThis)));
     1161    }
     1162}
     1163
     1164/**
     1165 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
     1166 */
     1167static DECLCALLBACK(PDMNETWORKLINKSTATE) virtioNetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
     1168{
     1169    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
     1170
     1171    return IS_LINK_UP(pThis) ? PDMNETWORKLINKSTATE_UP : PDMNETWORKLINKSTATE_DOWN;
     1172}
     1173
     1174/**
     1175 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
     1176 */
     1177static DECLCALLBACK(int) virtioNetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
     1178{
     1179    PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, PVIRTIONETCC, INetworkConfig);
     1180    PPDMDEVINS   pDevIns = pThisCC->pDevIns;
     1181    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
     1182
     1183    bool fOldUp = !!(pThis->virtioNetConfig.uStatus & VIRTIONET_F_LINK_UP);
     1184    bool fNewUp = enmState == PDMNETWORKLINKSTATE_UP;
     1185
     1186    Log(("%s virtioNetR3NetworkConfig_SetLinkState: enmState=%d\n", INSTANCE(pThis), enmState));
     1187    if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
     1188    {
     1189        if (fOldUp)
     1190        {
     1191            /*
     1192             * We bother to bring the link down only if it was up previously. The UP link state
     1193             * notification will be sent when the link actually goes up in virtioNetR3LinkUpTimer().
     1194             */
     1195            virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
     1196            if (pThisCC->pDrv)
     1197                pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
     1198        }
     1199    }
     1200    else if (fNewUp != fOldUp)
     1201    {
     1202        if (fNewUp)
     1203        {
     1204            Log(("%s Link is up\n", INSTANCE(pThis)));
     1205            pThis->fCableConnected = true;
     1206            pThis->virtioNetConfig.uStatus |= VIRTIONET_F_LINK_UP;
     1207            virtioCoreNotifyConfigChanged(&pThis->Virtio);
     1208        }
     1209        else
     1210        {
     1211            /* The link was brought down explicitly, make sure it won't come up by timer.  */
     1212            PDMDevHlpTimerStop(pDevIns, pThisCC->hLinkUpTimer);
     1213            Log(("%s Link is down\n", INSTANCE(pThis)));
     1214            pThis->fCableConnected = false;
     1215            pThis->virtioNetConfig.uStatus &= ~VIRTIONET_F_LINK_UP;
     1216            virtioCoreNotifyConfigChanged(&pThis->Virtio);
     1217        }
     1218        if (pThisCC->pDrv)
     1219            pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
     1220    }
     1221    return VINF_SUCCESS;
     1222}
     1223
     1224/**
     1225 * @callback_method_impl{FNVPCIQUEUECALLBACK, The RX queue}
     1226 */
     1227static DECLCALLBACK(void) virtioNetR3QueueReceive(PPDMDEVINS pDevIns, PVQUEUE pQueue)
     1228{
     1229    PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
     1230    RT_NOREF(pThis, pQueue);
     1231    LogFunc(("%s Receive buffers has been added, waking up receive thread.\n", INSTANCE(pThis)));
     1232    virtioNetWakeupReceive(pDevIns);
     1233}
     1234
     1235/**
     1236 * Sets up the GSO context according to the Virtio header.
     1237 *
     1238 * @param   pGso                The GSO context to setup.
     1239 * @param   pCtx                The context descriptor.
     1240 */
     1241DECLINLINE(PPDMNETWORKGSO) virtioNetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VNETHDR const *pHdr)
     1242{
     1243    pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
     1244
     1245    if (pHdr->u8GSOType & VNETHDR_GSO_ECN)
     1246    {
     1247        AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
     1248        return NULL;
     1249    }
     1250    switch (pHdr->u8GSOType & ~VNETHDR_GSO_ECN)
     1251    {
     1252        case VNETHDR_GSO_TCPV4:
     1253            pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
     1254            pGso->cbHdrsSeg = pHdr->u16HdrLen;
     1255            break;
     1256        case VNETHDR_GSO_TCPV6:
     1257            pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
     1258            pGso->cbHdrsSeg = pHdr->u16HdrLen;
     1259            break;
     1260        case VNETHDR_GSO_UDP:
     1261            pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
     1262            pGso->cbHdrsSeg = pHdr->u16CSumStart;
     1263            break;
     1264        default:
     1265            return NULL;
     1266    }
     1267    if (pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)
     1268        pGso->offHdr2  = pHdr->u16CSumStart;
     1269    else
     1270    {
     1271        AssertMsgFailed(("GSO without checksum offloading!\n"));
     1272        return NULL;
     1273    }
     1274    pGso->offHdr1     = sizeof(RTNETETHERHDR);
     1275    pGso->cbHdrsTotal = pHdr->u16HdrLen;
     1276    pGso->cbMaxSeg    = pHdr->u16GSOSize;
     1277    return pGso;
     1278}
     1279
     1280
     1281/**
     1282 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
     1283 */
     1284static DECLCALLBACK(int) virtioNetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
     1285{
     1286    return virtioNetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
     1287}
     1288
     1289/**
     1290 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
     1291 */
     1292static DECLCALLBACK(int) virtioNetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
     1293{
     1294    PVNETSTATECC    pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkConfig);
     1295    PVIRTIONET      pThis   = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVNETSTATE);
     1296    memcpy(pMac, pThis->virtioNetConfig.uMacAddress.au8, sizeof(RTMAC));
     1297    return VINF_SUCCESS;
     1298}
     1299#endif
     1300
     1301/**
     1302 * Returns true if it is a broadcast packet.
     1303 *
     1304 * @returns true if destination address indicates broadcast.
     1305 * @param   pvBuf           The ethernet packet.
     1306 */
     1307DECLINLINE(bool) virtioNetR3IsBroadcast(const void *pvBuf)
     1308{
     1309    static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
     1310    return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
     1311}
     1312
     1313/**
     1314 * Returns true if it is a multicast packet.
     1315 *
     1316 * @remarks returns true for broadcast packets as well.
     1317 * @returns true if destination address indicates multicast.
     1318 * @param   pvBuf           The ethernet packet.
     1319 */
     1320DECLINLINE(bool) virtioNetR3IsMulticast(const void *pvBuf)
     1321{
     1322    return (*(char*)pvBuf) & 1;
     1323}
    9951324/**
    9961325 * Determines if the packet is to be delivered to upper layer.
     
    9981327 * @returns true if packet is intended for this node.
    9991328 * @param   pThis          Pointer to the state structure.
    1000  * @param   pvBuf           The ethernet packet.
    1001  * @param   cb              Number of bytes available in the packet.
    1002  */
    1003 static bool vnetR3AddressFilter(PVNETSTATE pThis, const void *pvBuf, size_t cb)
     1329 * @param   pvBuf          The ethernet packet.
     1330 * @param   cb             Number of bytes available in the packet.
     1331 */
     1332static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
    10041333{
    10051334    if (pThis->fPromiscuous)
     
    10071336
    10081337    /* Ignore everything outside of our VLANs */
    1009     uint16_t *uPtr = (uint16_t*)pvBuf;
     1338    uint16_t *uPtr = (uint16_t *)pvBuf;
     1339
    10101340    /* Compare TPID with VLAN Ether Type */
    10111341    if (   uPtr[6] == RT_H2BE_u(0x8100)
    10121342        && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_u(uPtr[7]) & 0xFFF))
    10131343    {
    1014         Log4(("%s vnetR3AddressFilter: not our VLAN, returning false\n", INSTANCE(pThis)));
     1344        Log4func(("%s: not our VLAN, returning false\n", INSTANCE(pThis))));
    10151345        return false;
    10161346    }
    10171347
    1018     if (vnetR3IsBroadcast(pvBuf))
     1348    if (virtioNetR3IsBroadcast(pvBuf))
    10191349        return true;
    10201350
    1021     if (pThis->fAllMulti && vnetR3IsMulticast(pvBuf))
     1351    if (pThis->fAllMulticast && virtioNetR3IsMulticast(pvBuf))
    10221352        return true;
    10231353
    1024     if (!memcmp(pThis->config.mac.au, pvBuf, sizeof(RTMAC)))
     1354    if (!memcmp(pThis->virtioNetConfig.uMacAddress.au8, pvBuf, sizeof(RTMAC)))
    10251355        return true;
    1026     Log4(("%s vnetR3AddressFilter: %RTmac (conf) != %RTmac (dest)\n", INSTANCE(pThis), pThis->config.mac.au, pvBuf));
    1027 
    1028     for (unsigned i = 0; i < pThis->cMacFilterEntries; i++)
    1029         if (!memcmp(&pThis->aMacFilter[i], pvBuf, sizeof(RTMAC)))
     1356
     1357    Log4func(("%s : %RTmac (conf) != %RTmac (dest)\n",
     1358        INSTANCE(pThis), pThis->virtioNetConfig.uMacAddress.au8, pvBuf));
     1359
     1360    for (uint16_t i = 0; i < pThis->cMulticastFilterMacs; i++)
     1361        if (!memcmp(&pThis->aMacMulticastFilter[i], pvBuf, sizeof(RTMAC)))
    10301362            return true;
    10311363
    1032     Log2(("%s vnetR3AddressFilter: failed all tests, returning false, packet dump follows:\n", INSTANCE(pThis)));
    1033     vnetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
     1364    /* @todo Original combined unicast & multicast into one table. Should we distinguish? */
     1365
     1366    for (uint16_t i = 0; i < pThis->cUnicastFilterMacs; i++)
     1367        if (!memcmp(&pThis->aMacUnicastFilter[i], pvBuf, sizeof(RTMAC)))
     1368            return true;
     1369
     1370    Log2Func(("%s: failed all tests, returning false, packet dump follows:\n",
     1371        INSTANCE(pThis))));
     1372
     1373    virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
    10341374
    10351375    return false;
    10361376}
     1377
     1378
     1379
    10371380
    10381381/**
     
    10571400    {
    10581401        Log2Func(("%s gso type=%x cbPktHdrsTotal=%u cbPktHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
    1059               pThis->szInstanceName, pGso->uType, pGso->cbHdrsTotal,
     1402              INSTANCE(pThis), pGso->uType, pGso->cbHdrsTotal,
    10601403              pGso->cbHdrsSeg, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
    10611404
     
    10961439    RTSgBufInit(pSegsBuf, paSegs, cSegs);
    10971440
    1098     uint16_t *pPhysPktHdrNumBufs, cDescs = 0;
    1099 
    1100     uint8_t fFirstIteration = true;
    1101     for (uint32_t uOffset = 0; uOffset < cb; fFirstIteration = false)
     1441    RTGCPHYS physPktHdrNumBufs, cDescs = 0;
     1442
     1443    uint8_t fAddPktHdr = true;
     1444    for (uint32_t uOffset = 0; uOffset < cb; )
    11021445    {
    11031446        PVIRTIO_DESC_CHAIN_T pDescChain;
     
    11081451        /** @todo  Find a better way to deal with this */
    11091452
    1110         AssertMsgReturn(rc == VINF_SUCCESS && pDescChain->cbPhysSend,
     1453        AssertMsgReturn(rc == VINF_SUCCESS && pDescChain->cbPhysReturn,
    11111454                        ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
    11121455                        VERR_INTERNAL_ERROR);
    11131456
    1114         AssertMsgReturn(pDescChain->cbPhysReturn >= sizeof(VIRTIONET_PKT_HDR_T),
     1457        /* It's almost inconceivable that the first segment of the guest Rx s/g buf is smaller
     1458         * than 12 bytes (i.e. size of virtio_net_hdr). Smaller hasn't been seen in practice.
     1459         * To simplify the code, that constraint is implemented here. If a guest OS ever violates
     1460         * this assumption, the algorithm can be adapted handle the more complex corner case. */
     1461
     1462        AssertMsgReturn(pDescChain->pSgPhysReturn->paSegs[0].cbSeg >= sizeof(VIRTIONET_PKT_HDR_T),
    11151463                        ("Desc chain's phys segs have insufficient space for pkt header!\n"),
    11161464                        VERR_INTERNAL_ERROR);
     
    11191467
    11201468        uint16_t cSegs = 0;
    1121         if (fFirstIteration)
     1469        if (fAddPktHdr)
    11221470        {
    11231471            /* Lead with packet header */
     
    11251473            paSegs[cSegs].pvSeg = RTMemAlloc(paSegs[0].cb);
    11261474            AssertReturn(paSegs[0].pvSeg, VERR_NO_MEMORY);
    1127             cbDescChainLeft -= paReqSegs[0].cb;
    1128             *pPhysPktHdrNumBufs = ((uint8_t *)paSegs[cSegs].pvSeg)
    1129                                      + RT_UOFFSETOF(VIRTIONET_PKT_HDR_T, uNumBuffers);
     1475
     1476            gcPhysPktHdrNumBuffers = pDescChain->pSgPhysReturn->paSegs[0]
     1477                + RT_UOFFSETOF(VIRTIONET_PKT_HDR_T, uNumBuffers);
     1478
    11301479            cSegs++;
     1480            fAddPktHdr = false;
     1481            cbDescChainLeft -= sizeof(VIRTIONET_PKT_HDR_T);
    11311482        }
    11321483
     
    11461497    /* Fix up pkt hdr (already in guest phys. memory) with number of descriptors to send */
    11471498
    1148     int rc = PDMDevHlpPCIPhysWrite(pDevIns, pPhysPktHdrNumBuffers, cDescs, sizeof(cDescs));
     1499    int rc = PDMDevHlpPCIPhysWrite(pDevIns, gcPhysPktHdrNumBuffers, cDescs, sizeof(cDescs));
    11491500    AssertMsgRCReturn(rc, "Failure updating descriptor count in pkt hdr in guest physical memory\n");
    11501501
     
    11531504    for (int i = 0; i < 2; i++)
    11541505        RTMemFree(paSegs[i].pvSeg);
     1506
    11551507    RTMemFree(paSegs);
    11561508    RTMemFree(pSegsBuf);
     
    11581510    if (uOffset < cb)
    11591511    {
    1160         Log(("%s vnetR3HandleRxPacket: Packet did not fit into RX queue (packet size=%u)!\n", INSTANCE(pThis), cb));
     1512        Log(("%s virtioNetR3HandleRxPacket: Packet did not fit into RX queue (packet size=%u)!\n", INSTANCE(pThis), cb));
    11611513        return VERR_TOO_MUCH_DATA;
    11621514    }
     
    11971549        if (!uFeatures)
    11981550        {
    1199             Log2Func((GSO type (0x%x) not supported\n", pThis->szInstanceName, pGso->uType));
     1551            Log2Func((GSO type (0x%x) not supported\n", INSTANCE(pThis), pGso->uType));
    12001552            return VERR_NOT_SUPPORTED;
    12011553        }
    12021554    }
    12031555
    1204     Log2Func(("pvBuf=%p cb=%u pGso=%p\n", pThis->szInstanceName, pvBuf, cb, pGso));
     1556    Log2Func(("pvBuf=%p cb=%u pGso=%p\n", INSTANCE(pThis), pvBuf, cb, pGso));
    12051557
    12061558    int rc = virtioR3CanReceive(pDevIns, pThis, pThisCC);
     
    12541606    AssertReturnVoid(uStart < cbSize);
    12551607    AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cbSize);
    1256     *(uint16_t *)(pBuf + uStart + uOffset) = vnetR3Chksum16(pBuf + uStart, cbSize - uStart);
     1608    *(uint16_t *)(pBuf + uStart + uOffset) = virtioNetR3Chksum16(pBuf + uStart, cbSize - uStart);
    12571609}
    12581610
     
    12701622    }
    12711623}
    1272 static bool virtioNetR3ReadHeader(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONET_PKT_HDR_T pPktHdr, uint32_t cbMax)
    1273 {
    1274     int rc = PDMDevHlpPCIPhysRead(pDevIns, GCPhys, pPktHdr, sizeof(*pPktHdr));
    1275     if (RT_FAILURE(rc))
    1276         return false;
    1277 
    1278     Log4(("virtio-net: header flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x cb=%x\n",
    1279           pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen, pPktHdr->uGSOSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbMax));
    1280 
    1281     if (pPktHdr->uGsoType)
    1282     {
    1283         uint32_t uMinHdrSize;
    1284 
    1285         /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
    1286         if (  RT_UNLIKELY(!(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM))
    1287             | RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)))
    1288                 return false;
    1289 
    1290         switch (pPktHdr->uGsoType)
    1291         {
    1292             case VIRTIONET_HDR_GSO_TCPV4:
    1293             case VIRTIONET_HDR_GSO_TCPV6:
    1294                 uMinHdrSize = sizeof(RTNETTCP);
    1295                 break;
    1296             case VIRTIONET_HDR_GSO_UDP:
    1297                 uMinHdrSize = 0;
    1298                 break;
    1299             default:
    1300                 return false;
    1301         }
    1302         /* Header + MSS must not exceed the packet size. */
    1303         if (RT_UNLIKELY(uMinHdrSize + pPktHdr->uChksumStart + pPktHdr->uGSOSize > cbMax))
    1304             return false;
    1305     }
    1306     /* Checksum must fit into the frame (validating both checksum fields). */
    1307     if ((   pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
    1308          && sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset > cbMax)
    1309                return false;
    1310     Log4func(("returning true\n"));
    1311     return true;
    1312 }
     1624
    13131625
    13141626static uint8_t virtioNetR3CtrlRx(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
     
    14251737
    14261738#ifdef LOG_ENABLED
    1427             LogFunc(("%s: unicast MACs:\n", pThis->szInstanceName)));
     1739            LogFunc(("%s: unicast MACs:\n", INSTANCE(pThis))));
    14281740            for(unsigned i = 0; i < nMacs; i++)
    14291741                LogFunc(("         %RTmac\n", &pThis->aMacUnicastFilter[i]));
    14301742
    1431             LogFunc(("%s: multicast MACs:\n", pThis->szInstanceName)));
     1743            LogFunc(("%s: multicast MACs:\n", INSTANCE(pThis))));
    14321744            for(unsigned i = 0; i < nMacs; i++)
    14331745                LogFunc(("         %RTmac\n", &pThis->aMacUnicastFilter[i]));
     
    14481760    virtioNetR3PullChain(pDescChain, &uVlanId, sizeof(uVlanId));
    14491761    AssertMsgReturn(uVlanId > VIRTIONET_MAX_VLAN_ID,
    1450         ("%s VLAN ID out of range (VLAN ID=%u)\n", pThis->szInstanceName, uVlanId), VIRTIONET_ERROR);
    1451     LogFunc(("%s: uCommand=%u VLAN ID=%u\n", pThis->szInstanceName, pCtrlPktHdr->uCmd, uVlanId));
     1762        ("%s VLAN ID out of range (VLAN ID=%u)\n", INSTANCE(pThis), uVlanId), VIRTIONET_ERROR);
     1763    LogFunc(("%s: uCommand=%u VLAN ID=%u\n", INSTANCE(pThis), pCtrlPktHdr->uCmd, uVlanId));
    14521764    switch (pCtrlPktHdr->uCmd)
    14531765    {
     
    15071819    }
    15081820
    1509     PRTSGSEG paReqSegs = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * 2);
    1510     AssertReturn(paReqSegs, VERR_NO_MEMORY);
     1821    /* Return CTRL packet Ack byte (result code) to guest driver */
     1822    PRTSGSEG paSegs = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * 2);
     1823    AssertReturn(paSegs, VERR_NO_MEMORY);
    15111824
    15121825    RTSGSEG aSegs[] = { { &uAck, sizeof(uAck) } };
    1513     memcpy(paReqSegs, aSegs, sizeof(aSegs));
    1514 
    1515     PRTSGBUF pReqSegBuf = (PRTSGBUF)RTMemAllocZ(sizeof(RTSGBUF));
    1516     AssertReturn(pReqSegBuf, VERR_NO_MEMORY);
     1826    memcpy(paSegs, aSegs, sizeof(aSegs));
     1827
     1828    PRTSGBUF pSegBuf = (PRTSGBUF)RTMemAllocZ(sizeof(RTSGBUF));
     1829    AssertReturn(pSegBuf, VERR_NO_MEMORY);
    15171830
    15181831    /* Copy segment data to malloc'd memory to avoid stack out-of-scope errors sanitizer doesn't detect */
    15191832    for (int i = 0; i < cSegs; i++)
    15201833    {
    1521         void *pv = paReqSegs[i].pvSeg;
    1522         paReqSegs[i].pvSeg = RTMemAlloc(paReqSegs[i].cbSeg);
    1523         AssertReturn(paReqSegs[i].pvSeg, VERR_NO_MEMORY);
    1524         memcpy(paReqSegs[i].pvSeg, pv, paReqSegs[i].cbSeg);
    1525     }
    1526 
    1527     RTSgBufInit(pReqSegBuf, paReqSegs, cSegs);
    1528 
    1529     virtioCoreR3QueuePut(pDevIns, &pThis->Virtio, qIdx, pReqSegBuf, pDescChain, true);
     1834        void *pv = paSegs[i].pvSeg;
     1835        paSegs[i].pvSeg = RTMemAlloc(paSegs[i].cbSeg);
     1836        AssertReturn(paSegs[i].pvSeg, VERR_NO_MEMORY);
     1837        memcpy(paSegs[i].pvSeg, pv, paSegs[i].cbSeg);
     1838    }
     1839
     1840    RTSgBufInit(pSegBuf, paSegs, cSegs);
     1841
     1842    virtioCoreR3QueuePut(pDevIns, &pThis->Virtio, qIdx, pSegBuf, pDescChain, true);
    15301843    virtioCoreQueueSync(pDevIns, &pThis->Virtio, qIdx);
    15311844
    15321845    for (int i = 0; i < cSegs; i++)
    1533         RTMemFree(paReqSegs[i].pvSeg);
    1534 
    1535     RTMemFree(paReqSegs);
    1536     RTMemFree(pReqSegBuf);
     1846        RTMemFree(paSegs[i].pvSeg);
     1847
     1848    RTMemFree(paSegs);
     1849    RTMemFree(pSegBuf);
    15371850
    15381851    LogFunc(("Processed ctrl message class/cmd/subcmd = %u/%u/%u. Ack=%u.\n",
    1539     pCtrlPktHdr.uClass, pCtrlPktHdr.uCmd, pCtrlPktHdr.uCmdSpecific, uAck);
    1540 
    1541 }
    1542 
    1543 
     1852              pCtrlPktHdr.uClass, pCtrlPktHdr.uCmd, pCtrlPktHdr.uCmdSpecific, uAck);
     1853
     1854}
     1855
     1856static bool virtioNetR3ReadHeader(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONET_PKT_HDR_T pPktHdr, uint32_t cbMax)
     1857{
     1858    int rc = PDMDevHlpPCIPhysRead(pDevIns, GCPhys, pPktHdr, sizeof(*pPktHdr));
     1859    if (RT_FAILURE(rc))
     1860        return false;
     1861
     1862    Log4(("virtio-net: header flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x cb=%x\n",
     1863          pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen, pPktHdr->uGSOSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbMax));
     1864
     1865    if (pPktHdr->uGsoType)
     1866    {
     1867        uint32_t uMinHdrSize;
     1868
     1869        /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
     1870        if (  RT_UNLIKELY(!(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM))
     1871            | RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)))
     1872                return false;
     1873
     1874        switch (pPktHdr->uGsoType)
     1875        {
     1876            case VIRTIONET_HDR_GSO_TCPV4:
     1877            case VIRTIONET_HDR_GSO_TCPV6:
     1878                uMinHdrSize = sizeof(RTNETTCP);
     1879                break;
     1880            case VIRTIONET_HDR_GSO_UDP:
     1881                uMinHdrSize = 0;
     1882                break;
     1883            default:
     1884                return false;
     1885        }
     1886        /* Header + MSS must not exceed the packet size. */
     1887        if (RT_UNLIKELY(uMinHdrSize + pPktHdr->uChksumStart + pPktHdr->uGSOSize > cbMax))
     1888            return false;
     1889    }
     1890    /* Checksum must fit into the frame (validating both checksum fields). */
     1891    if ((   pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
     1892         && sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset > cbMax)
     1893               return false;
     1894    Log4func(("returning true\n"));
     1895    return true;
     1896}
     1897
     1898static int virtioNetR3TransmitFrame(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMSCATTERGATHER pSgBuf,
     1899                               PPDMNETWORKGSO pGso, PVNETHDR pHdr)
     1900{
     1901    virtioNetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
     1902    if (pGso)
     1903    {
     1904        /* Some guests (RHEL) may report HdrLen excluding transport layer header! */
     1905        /*
     1906         * We cannot use cdHdrs provided by the guest because of different ways
     1907         * it gets filled out by different versions of kernels.
     1908         */
     1909        //if (pGso->cbHdrs < pHdr->uCSumStart + pHdr->uCSumOffset + 2)
     1910        {
     1911            Log4Func(("%s: HdrLen before adjustment %d.\n",
     1912                  INSTANCE(pThis), pGso->cbHdrsTotal));
     1913            switch (pGso->u8Type)
     1914            {
     1915                case PDMNETWORKGSOTYPE_IPV4_TCP:
     1916                case PDMNETWORKGSOTYPE_IPV6_TCP:
     1917                    pGso->cbHdrsTotal = pHdr->uChkumStart +
     1918                        ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pHdr->uChkumStart))->th_off * 4;
     1919                    pGso->cbHdrsSeg   = pGso->cbHdrsTotal;
     1920                    break;
     1921                case PDMNETWORKGSOTYPE_IPV4_UDP:
     1922                    pGso->cbHdrsTotal = (uint8_t)(pHdr->uCSumStart + sizeof(RTNETUDP));
     1923                    pGso->cbHdrsSeg   = pHdr->u16CSumStart;
     1924                    break;
     1925            }
     1926            /* Update GSO structure embedded into the frame */
     1927            ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
     1928            ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg   = pGso->cbHdrsSeg;
     1929            Log4(("%s vnetR3TransmitPendingPackets: adjusted HdrLen to %d.\n",
     1930                  INSTANCE(pThis), pGso->cbHdrsTotal));
     1931        }
     1932        Log2Func(("%s: gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
     1933                  INSTANCE(pThis), pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
     1934                  pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
     1935        STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
     1936    }
     1937    else if (pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)
     1938    {
     1939        STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
     1940        /*
     1941         * This is not GSO frame but checksum offloading is requested.
     1942         */
     1943        virtioNetR3CompleteChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
     1944                             pHdr->uChkumStart, pHdr->uCSumOffset);
     1945    }
     1946
     1947    return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, false);
     1948}
     1949
     1950static void virtioNetR3TransmitPendingPackets(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC,
     1951                                         uint16_t qIdx, bool fOnWorkerThread)
     1952{
     1953    PVIRTIOCORE pVirtio = pThis->pVirtio;
     1954
     1955    /*
     1956     * Only one thread is allowed to transmit at a time, others should skip
     1957     * transmission as the packets will be picked up by the transmitting
     1958     * thread.
     1959     */
     1960    if (!ASMAtomicCmpXchgU32(&pThis->uIsTransmitting, 1, 0))
     1961        return;
     1962
     1963    if (!fVirtioReady)
     1964    {
     1965        LogFunc(("%s Ignoring transmit requests. VirtIO not ready (status=0x%x).\n",
     1966                INSTANCE(pThis), pThis->virtioNetConfig.uStatus));
     1967        return;
     1968    }
     1969
     1970    if (!pThis->fCableConnected)
     1971    {
     1972        Log(("%s Ignoring transmit requests while cable is disconnected.\n", INSTANCE(pThis)));
     1973        return;
     1974    }
     1975
     1976    PPDMINETWORKUP pDrv = pThisCC->pDrv;
     1977    if (pDrv)
     1978    {
     1979        int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
     1980        Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
     1981        if (rc == VERR_TRY_AGAIN)
     1982        {
     1983            ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
     1984            return;
     1985        }
     1986    }
     1987
     1988    unsigned int cbPktHdr = sizeof(VIRTIONET_PKT_HDR_T);
     1989
     1990    Log3func(("%s: About to transmit %d pending packets\n",
     1991              INSTANCE(pThis), virtioCoreR3QueuePendingCount(pVirtio->pDevIndx, pVirtio, TXQIDX(0));
     1992
     1993    virtioNetR3SetWriteLed(&pThisCC, true);
     1994
     1995    PVIRTIO_DESC_CHAIN_T pDescChain;
     1996    while (virtioCoreR3QueuePeek(pVirtio->pDevIns, pVirtio, TXQIDX(0), &pDescChain) {
     1997    {
     1998        unsigned uOffset = 0;
     1999        uint32_t cVirtioSegs = pDescChain->pSgPhysSend->cSegs;
     2000        PVIRTIOSGSEG paVirtioSegs = pDescChain->pSgPhysSend->paSegs;
     2001
     2002        if (cVirtioSegs < 2 || paVirtioSegs[0].cbSeg != cbPktHdr)
     2003        {
     2004            /* This check could be made more complex, because in theory (but not likely nor
     2005             * seen in practice) the first segment could contain header and data. */
     2006            LogFunc(("%s: The first segment is not the header! (%u < 2 || %u != %u).\n",
     2007                 INSTANCE(pThis), cVirtioSegs, paVirtioSegs[0].cbSeg, cbPktHdr));
     2008            break;
     2009        }
     2010
     2011        VIRTIONET_PKT_HDR_T PktHdr;
     2012        uint32_t uSize = 0;
     2013
     2014        /* Compute total frame size. */
     2015        for (unsigned i = 1; i < cVirtioSegs && uSize < VIRTIONET_MAX_FRAME_SIZE; i++)
     2016            uSize +=  paVirtioSegs[i].cbSeg;
     2017
     2018        Log5func(("%s: complete frame is %u bytes.\n", INSTANCE(pThis), uSize));
     2019        Assert(uSize <= VIRTIONET_MAX_FRAME_SIZE);
     2020
     2021        /* Truncate oversized frames. */
     2022        if (uSize > VIRTIONET_MAX_FRAME_SIZE)
     2023            uSize = VIRTIONET_MAX_FRAME_SIZE;
     2024
     2025        if (pThisCC->pDrv && virtioNetR3ReadHeader(pDevIns, paVirtioSegs[0].gcPhys, &PktHdr, uSize))
     2026        {
     2027            PDMNETWORKGSO Gso, *pGso = virtioNetR3SetupGsoCtx(&Gso, &PktHdr);
     2028
     2029            /** @todo Optimize away the extra copying! (lazy bird) */
     2030            PPDMSCATTERGATHER pPdmSgBuf;
     2031            int rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uSize, pGso, &pPdmSgBuf);
     2032            if (RT_SUCCESS(rc))
     2033            {
     2034                pPdmSgBuf->cbUsed = uSize;
     2035
     2036                /* Assemble a complete frame. */
     2037                for (unsigned i = 1; i < cVirtioSegs && uSize > 0; i++)
     2038                {
     2039                    unsigned uOffset;
     2040                    unsigned cbSeg = RT_MIN(uSize, paVirtioSegs[i].cbSeg);
     2041
     2042                    PDMDevHlpPCIPhysRead(pDevIns, paVirtioSegs[i].gcPhys;
     2043                                         ((uint8_t *)pPdmSgBuf->aSegs[0].pvSeg) + uOffset,
     2044                                         cbSeg);
     2045                    uOffset += cbSeg;
     2046                    uSize -= cbSeg;
     2047                }
     2048                rc = virtioNetR3TransmitFrame(pThis, pThisCC, pPdmSgBuf, pGso, &PktHdr);
     2049            }
     2050            else
     2051            {
     2052                Log4func(("Failed to allocate SG buffer: size=%u rc=%Rrc\n", uSize, rc));
     2053                /* Stop trying to fetch TX descriptors until we get more bandwidth. */
     2054                break;
     2055            }
     2056        }
     2057
     2058        /* Remove this descriptor chain from the available ring */
     2059        virtioCoreR3QueueSkip(pVirtio, TXQIDX(0));
     2060
     2061        /* No data to return to guest, but call is needed put elem (e.g. desc chain) on used ring */
     2062        virtioCoreR3QueuePut(pVirtio->pDevIns, pVirtio, TXQIDX(0), NULL, pDescChain, false);
     2063
     2064        virtioCoreQueueSync(pVirtio->pDevIns, pVirtio, TXQIDX(0));
     2065    }
     2066    virtioNetR3SetWriteLed(pThisCC, false);
     2067
     2068    if (pDrv)
     2069        pDrv->pfnEndXmit(pDrv);
     2070    ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
     2071}
     2072
     2073/**
     2074 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
     2075 */
     2076static DECLCALLBACK(void) vnetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
     2077{
     2078    PVIRTIONETCC    pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
     2079    PPDMDEVINS      pDevIns = pThisCC->pDevIns;
     2080    PVIRTIONET      pThis   = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
     2081    vnetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, TXQIDX(0), false /*fOnWorkerThread*/);
     2082}
     2083
     2084# ifdef VNET_TX_DELAY
     2085
     2086/**
     2087 * @callback_method_impl{FNVPCIQUEUECALLBACK, The TX queue}
     2088 */
     2089static DECLCALLBACK(void) virtioNetR3QueueTransmit(PPDMDEVINS pDevIns, PVQUEUE pQueue)
     2090{
     2091    PVIRTIONET      pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
     2092    PVIRTIONETCC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
     2093
     2094    if (PDMDevHlpTimerIsActive(pDevIns, pThis->hTxTimer))
     2095    {
     2096        PDMDevHlpTimerStop(pDevIns, pThis->hTxTimer);
     2097        Log3Func(("%: Got kicked with notification disabled, re-enable notification and flush TX queue\n", INSTANCE(pThis)));
     2098        virtioNetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, TXQIDX(0), false /*fOnWorkerThread*/);
     2099        if (RT_FAILURE(virtioNettR3CsEnter(pDevIns, pThis, VERR_SEM_BUSY)))
     2100            LogRelFunc(("Failed to enter critical section!/n"));
     2101        else
     2102        {
     2103            virtioCoreQueueSetNotify(&pThis->Virtio,  TXQIDX(0), true);
     2104            virtioNetR3CsLeave(pDevIns, pThis);
     2105        }
     2106    }
     2107    else
     2108    {
     2109        if (RT_FAILURE(virtioNetR3CsEnter(pDevIns, pThis, VERR_SEM_BUSY)))
     2110            LogRelFunc(("Failed to enter critical section!/n"));
     2111        else
     2112        {
     2113            virtioCoreQueueSetNotify(&pThis->Virtio,  TXQIDX(0), false);
     2114            PDMDevHlpTimerSetMicro(pDevIns, pThis->hTxTimer, VNET_TX_DELAY);
     2115            pThis->u64NanoTS = RTTimeNanoTS();
     2116            virtioNetR3CsLeave(pDevIns, pThis);
     2117        }
     2118    }
     2119}
     2120
     2121/**
     2122 * @callback_method_impl{FNTMTIMERDEV, Transmit Delay Timer handler.}
     2123 */
     2124static DECLCALLBACK(void) virtioNetR3TxTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
     2125{
     2126    PVIRTIONET      pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
     2127    PVIRTIONETCC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
     2128    RT_NOREF(pTimer, pvUser);
     2129
     2130    uint32_t u32MicroDiff = (uint32_t)((RTTimeNanoTS() - pThis->u64NanoTS) / 1000);
     2131    if (u32MicroDiff < pThis->u32MinDiff)
     2132        pThis->u32MinDiff = u32MicroDiff;
     2133    if (u32MicroDiff > pThis->u32MaxDiff)
     2134        pThis->u32MaxDiff = u32MicroDiff;
     2135    pThis->u32AvgDiff = (pThis->u32AvgDiff * pThis->u32i + u32MicroDiff) / (pThis->u32i + 1);
     2136    pThis->u32i++;
     2137    Log3Func(("Expired, diff %9d usec, avg %9d usec, min %9d usec, max %9d usec\n",
     2138          u32MicroDiff, pThis->u32AvgDiff, pThis->u32MinDiff, pThis->u32MaxDiff));
     2139
     2140//    Log3Func(("%s Expired\n", INSTANCE(pThis)));
     2141    virtioNetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, TXQIDX(0), false /*fOnWorkerThread*/);
     2142    if (RT_FAILURE(virtioNettR3CsEnter(pDevIns, pThis, VERR_SEM_BUSY)))
     2143    {
     2144        LogRelFunc(("Failed to enter critical section!/n"));
     2145        return;
     2146    }
     2147    virtioCoreQueueSetNotify(&pThis->Virtio,  TXQIDX(0), true);
     2148    virtioNetR3CsLeave(pDevIns, pThis);
     2149}
     2150
     2151DECLINLINE(int) virtioNetR3CreateTxThreadAndEvent(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
     2152{
     2153    RT_NOREF(pDevIns, pThis, pThisCC);
     2154    return VINF_SUCCESS;
     2155}
     2156
     2157DECLINLINE(void) virtioNetR3DestroyTxThreadAndEvent(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
     2158{
     2159    RT_NOREF(pDevIns, pThis, pThisCC);
     2160}
     2161
     2162# else /* !VNET_TX_DELAY */
     2163
     2164/**
     2165 * @callback_method_impl{FNPDMTHREADDEV}
     2166 */
     2167static DECLCALLBACK(int) vnetR3TxThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
     2168{
     2169    PVNETSTATE      pThis   = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
     2170    PVNETSTATECC    pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
     2171
     2172    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
     2173        return VINF_SUCCESS;
     2174
     2175    int rc = VINF_SUCCESS;
     2176    while (pThread->enmState == PDMTHREADSTATE_RUNNING)
     2177    {
     2178        rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hTxEvent, RT_INDEFINITE_WAIT);
     2179        if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
     2180            break;
     2181        while (true)
     2182        {
     2183            virtioNetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, TXQIDX(0), false /*fOnWorkerThread*/); /// @todo shouldn't it be true instead?
     2184            LogFunc(("Enable kicking and get to sleep\n"));
     2185            virtioCoreQueueSetNotify(&pThis->Virtio,  TXQIDX(0), true);
     2186            if (virtioCoreQueueIsEmpty(pDevIns, &pThis->Virtio, TXQIDX(0)))
     2187                break;
     2188            virtioCoreQueueSetNotify(&pThis->Virtio, TXQIDX(0), false);
     2189        }
     2190    }
     2191
     2192    return rc;
     2193}
     2194
     2195/**
     2196 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
     2197 */
     2198static DECLCALLBACK(int) virtioNetR3TxThreadWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
     2199{
     2200    PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
     2201    RT_NOREF(pThread);
     2202    return PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTxEvent);
     2203}
     2204
     2205static int vnetR3CreateTxThreadAndEvent(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
     2206{
     2207    int rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hTxEvent);
     2208    if (RT_SUCCESS(rc))
     2209    {
     2210        rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->pTxThread, NULL, vnetR3TxThread,
     2211                                   vnetR3TxThreadWakeUp, 0, RTTHREADTYPE_IO, INSTANCE(pThis));
     2212        if (RT_FAILURE(rc))
     2213            PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("VNET: Failed to create worker thread %s"), INSTANCE(pThis));
     2214    }
     2215    else
     2216        PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("VNET: Failed to create SUP event semaphore"));
     2217    return rc;
     2218}
     2219
     2220static void vnetR3DestroyTxThreadAndEvent(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
     2221{
     2222    if (pThisCC->pTxThread)
     2223    {
     2224        /* Destroy the thread. */
     2225        int rcThread;
     2226        int rc = PDMDevHlpThreadDestroy(pDevIns, pThisCC->pTxThread, &rcThread);
     2227        if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
     2228            AssertMsgFailed(("%s Failed to destroy async IO thread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
     2229        pThisCC->pTxThread = NULL;
     2230    }
     2231
     2232    if (pThis->hTxEvent != NIL_SUPSEMEVENT)
     2233    {
     2234        PDMDevHlpSUPSemEventClose(pDevIns, pThis->hTxEvent);
     2235        pThis->hTxEvent = NIL_SUPSEMEVENT;
     2236    }
     2237}
     2238
     2239/**
     2240 * @callback_method_impl{FNVPCIQUEUECALLBACK, The TX queue}
     2241 */
     2242static DECLCALLBACK(void) virtioNetR3QueueTransmit(PPDMDEVINS pDevIns, uint16_t qIdx)
     2243{
     2244    PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
     2245
     2246    LogFunc(("Disable kicking and wake up TX thread\n"));
     2247    virtioCoreQueueSetNotify(&pThis->Virtio, qIdx, false);
     2248    int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTxEvent);
     2249    AssertRC(rc);
     2250}
     2251
     2252# endif /* !VNET_TX_DELAY */
    15442253static void virtioNetR3Transmit(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC, uint16_t qIdx, PVIRTIO_DESC_CHAIN_T pDescChain)
    15452254{
     
    16712380
    16722381        pThis->virtioNetConfig.status = pThis->fCableConnected ? VIRTIONET_S_LINK_UP : 0;
    1673         LogFunc(("%s Link is %s\n", pThis->szInstanceName, pThis->fCableConnected ? "up" : "down"));
     2382        LogFunc(("%s Link is %s\n", INSTANCE(pThis), pThis->fCableConnected ? "up" : "down"));
    16742383
    16752384        pThis->fPromiscuous  = true;
     
    18522561     */
    18532562    LogFunc(("PDM device instance: %d\n", iInstance));
    1854     RTStrPrintf(pThis->szInstanceName, sizeof(pThis->szInstanceName), "VIRTIONET%d", iInstance);
     2563    RTStrPrintf(INSTANCE(pThis), sizeof(INSTANCE(pThis)), "VIRTIONET%d", iInstance);
    18552564    pThisCC->pDevIns     = pDevIns;
    18562565
     
    18862595    if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
    18872596        LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
    1888                 pThis->szInstanceName, pThis->cMsLinkUpDelay / 1000));
    1889 
    1890     Log(("%s Link up delay is set to %u seconds\n", pThis->szInstanceName, pThis->cMsLinkUpDelay / 1000));
     2597                INSTANCE(pThis), pThis->cMsLinkUpDelay / 1000));
     2598
     2599    Log(("%s Link up delay is set to %u seconds\n", INSTANCE(pThis), pThis->cMsLinkUpDelay / 1000));
    18912600
    18922601    /* Copy the MAC address configured for the VM to the MMIO accessible Virtio dev-specific config area */
     
    19182627    VirtioPciParams.uInterruptPin           = 0x01;
    19192628
    1920     rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, pThis->szInstanceName,
     2629    rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, INSTANCE(pThis),
    19212630                          VIRTIONET_HOST_FEATURES_OFFERED,
    19222631                          &pThis->virtioNetConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioNetConfig));
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette