VirtualBox

Changeset 85415 in vbox for trunk/src/VBox/Devices/Network


Ignore:
Timestamp:
Jul 22, 2020 2:44:19 PM (5 years ago)
Author:
vboxsync
Message:

Network/DevVirtioNet_1_0.cpp: Fixed pause/resume/poweroff, added more support for multiqueue (MQ) handling (see bugref:8651, Comment 91), More little cleanup, comment fixes

File:
1 edited

Legend:

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

    r85291 r85415  
    6060
    6161#define LUN0                            0
    62 
    63 #define VIRTIONET_SAVED_STATE_VERSION   UINT32_C(1)
    64 #define VIRTIONET_MAX_QPAIRS            1
    65 #define VIRTIONET_MAX_VIRTQS            (VIRTIONET_MAX_QPAIRS * 2 + 1)
    66 #define VIRTIONET_MAX_FRAME_SIZE        65535 + 18  /**< Max IP pkt size + Eth. header w/VLAN tag  */
    67 #define VIRTIONET_MAC_FILTER_LEN        32
    68 #define VIRTIONET_MAX_VLAN_ID           (1 << 12)
    69 #define VIRTIONET_RX_SEG_COUNT          32
    70 
    71 #define VIRTQNAME(uVirtqNbr)            (pThis->aVirtqs[uVirtqNbr]->szName)
    72 #define CBVIRTQNAME(uVirtqNbr)          RTStrNLen(VIRTQNAME(uVirtqNbr), sizeof(VIRTQNAME(uVirtqNbr)))
    73 #define FEATURE_ENABLED(feature)        RT_BOOL(pThis->fNegotiatedFeatures & VIRTIONET_F_##feature)
    74 #define FEATURE_DISABLED(feature)       (!FEATURE_ENABLED(feature))
    75 #define FEATURE_OFFERED(feature)        VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_##feature
    76 
    77 /* Macros to calculate queue specific index number VirtIO 1.0, 5.1.2 */
    78 #define IS_TX_VIRTQ(n)                  ((n) != CTRLQIDX && ((n) & 1))
    79 #define IS_RX_VIRTQ(n)                  ((n) != CTRLQIDX && !IS_TX_VIRTQ(n))
    80 #define IS_CTRL_VIRTQ(n)                ((n) == CTRLQIDX)
    81 #define RXQIDX(qPairIdx)                (qPairIdx * 2)
    82 #define TXQIDX(qPairIdx)                (qPairIdx * 2 + 1)
    83 #define CTRLQIDX                        (FEATURE_ENABLED(MQ) ? ((VIRTIONET_MAX_QPAIRS - 1) * 2 + 2) : 2)
    84 
    85 #define IS_LINK_UP(pState)              (pState->virtioNetConfig.uStatus & VIRTIONET_F_LINK_UP)
    86 #define IS_LINK_DOWN(pState)            !IS_LINK_UP(pState)
    87 
    88 \
    89 #define SET_LINK_UP(pState) \
    90             LogFunc(("SET_LINK_UP\n")); \
    91             pState->virtioNetConfig.uStatus |= VIRTIONET_F_LINK_UP; \
    92             virtioCoreNotifyConfigChanged(&pThis->Virtio)
    93 
    94 #define SET_LINK_DOWN(pState) \
    95             LogFunc(("SET_LINK_DOWN\n")); \
    96             pState->virtioNetConfig.uStatus &= ~VIRTIONET_F_LINK_UP; \
    97             virtioCoreNotifyConfigChanged(&pThis->Virtio)
    98 
    99 #define IS_VIRTQ_EMPTY(pDevIns, pVirtio, uVirtqNbr) \
    100             (virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr) == 0)
    101 
    102 
    103 #ifdef USING_CRITICAL_SECTION
    104 #  define ENTER_CRITICAL_SECTION \
    105         do { \
    106             int rc = virtioNetR3CsEnter(pDevIns, pThis, VERR_SEM_BUSY); \
    107             AssertRCReturnVoid(rc); \
    108             RT_NOREF(rc);
    109         } while(0)
    110 #  define LEAVE_CRITICAL_SECTION \
    111         do { \
    112             virtioNetR3CsLeave(pDevIns, pThis); \
    113         } while(0)
    114 #else
    115 #   define ENTER_CRITICAL_SECTION do { } while(0)
    116 #   define LEAVE_CRITICAL_SECTION do { } while(0)
    117 #endif
    118 
    119 /*
    120  * Glossary of networking acronyms used in the following bit definitions:
    121  *
    122  * GSO = Generic Segmentation Offload
    123  * TSO = TCP Segmentation Offload
    124  * UFO = UDP Fragmentation Offload
    125  * ECN = Explicit Congestion Notification
    126  */
    12762
    12863/** @name VirtIO 1.0 NET Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
     
    175110    | VIRTIONET_F_MRG_RXBUF
    176111
     112/*
     113 * Glossary of networking acronyms used in the previous bit definitions:
     114 *
     115 * GSO = Generic Segmentation Offload
     116 * TSO = TCP Segmentation Offload
     117 * UFO = UDP Fragmentation Offload
     118 * ECN = Explicit Congestion Notification
     119 */
     120
     121#define FEATURE_ENABLED(feature)        RT_BOOL(pThis->fNegotiatedFeatures & VIRTIONET_F_##feature)
     122#define FEATURE_DISABLED(feature)       (!FEATURE_ENABLED(feature))
     123#define FEATURE_OFFERED(feature)        VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_##feature
     124
     125#define VIRTIONET_SAVED_STATE_VERSION   UINT32_C(1)
     126
     127#if FEATURE_OFFERED(MQ)
     128#   define VIRTIONET_MAX_QPAIRS         1000
     129#else
     130#   define VIRTIONET_MAX_QPAIRS         VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN
     131#endif
     132
     133#define VIRTIONET_MAX_VIRTQS            (VIRTIONET_MAX_QPAIRS * 2 + 1)
     134#define VIRTIONET_MAX_FRAME_SIZE        65535 + 18  /**< Max IP pkt size + Eth. header w/VLAN tag  */
     135#define VIRTIONET_MAC_FILTER_LEN        32
     136#define VIRTIONET_MAX_VLAN_ID           (1 << 12)
     137#define VIRTIONET_RX_SEG_COUNT          32
     138
     139#define VIRTQNAME(uVirtqNbr)            (pThis->aVirtqs[uVirtqNbr]->szName)
     140#define CBVIRTQNAME(uVirtqNbr)          RTStrNLen(VIRTQNAME(uVirtqNbr), sizeof(VIRTQNAME(uVirtqNbr)))
     141
     142/* Macros to calculate queue specific index number VirtIO 1.0, 5.1.2 */
     143#define IS_TX_VIRTQ(n)                  ((n) != CTRLQIDX && ((n) & 1))
     144#define IS_RX_VIRTQ(n)                  ((n) != CTRLQIDX && !IS_TX_VIRTQ(n))
     145#define IS_CTRL_VIRTQ(n)                ((n) == CTRLQIDX)
     146#define RXQIDX(qPairIdx)                (qPairIdx * 2)
     147#define TXQIDX(qPairIdx)                (RXQIDX(qPairIdx) + 1)
     148#define CTRLQIDX                        (FEATURE_ENABLED(MQ) ? ((VIRTIONET_MAX_QPAIRS - 1) * 2 + 2) : 2)
     149
     150#define IS_LINK_UP(pState)              (pState->virtioNetConfig.uStatus & VIRTIONET_F_LINK_UP)
     151#define IS_LINK_DOWN(pState)            !IS_LINK_UP(pState)
     152
     153#define SET_LINK_UP(pState) \
     154            LogFunc(("SET_LINK_UP\n")); \
     155            pState->virtioNetConfig.uStatus |= VIRTIONET_F_LINK_UP; \
     156            virtioCoreNotifyConfigChanged(&pThis->Virtio)
     157
     158#define SET_LINK_DOWN(pState) \
     159            LogFunc(("SET_LINK_DOWN\n")); \
     160            pState->virtioNetConfig.uStatus &= ~VIRTIONET_F_LINK_UP; \
     161            virtioCoreNotifyConfigChanged(&pThis->Virtio)
     162
     163#define IS_VIRTQ_EMPTY(pDevIns, pVirtio, uVirtqNbr) \
     164            (virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr) == 0)
     165
    177166#define PCI_DEVICE_ID_VIRTIONET_HOST               0x1041      /**< Informs guest driver of type of VirtIO device   */
    178167#define PCI_CLASS_BASE_NETWORK_CONTROLLER          0x02        /**< PCI Network device class                        */
     
    303292/** @name Control virtq: Setting Offloads State (VirtIO 1.0, 5.1.6.5.6.1)
    304293 * @{  */
    305 #define VIRTIO_NET_CTRL_GUEST_OFFLOADS             5            /**< Control class: Offloads state configuration    */
    306 #define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET         0            /** Apply new offloads configuration                */
     294#define VIRTIONET_CTRL_GUEST_OFFLOADS             5            /**< Control class: Offloads state configuration    */
     295#define VIRTIONET_CTRL_GUEST_OFFLOADS_SET         0            /** Apply new offloads configuration                */
    307296/** @} */
    308297
     
    317306    struct VIRTIONETWORKER         *pWorker;                    /**< Pointer to R0 worker struct                    */
    318307    struct VIRTIONETWORKERR3       *pWorkerR3;                  /**< Pointer to R3 worker struct                    */
    319     uint16_t                       idx;                         /**< Index of this queue                            */
     308    uint16_t                       uIdx;                        /**< Index of this queue                            */
    320309    uint16_t                       align;
    321310    char                           szName[VIRTIO_MAX_VIRTQ_NAME_SIZE]; /**< Virtq name                              */
     
    333322    SUPSEMEVENT                     hEvtProcess;                /**< handle of associated sleep/wake-up semaphore   */
    334323    PVIRTIONETVIRTQ                 pVirtq;                     /**< pointer to queue                               */
    335     uint16_t                        idx;                        /**< Index of this worker                           */
     324    uint16_t                        uIdx;                       /**< Index of this worker                           */
    336325    bool volatile                   fSleeping;                  /**< Flags whether worker thread is sleeping or not */
    337326    bool volatile                   fNotified;                  /**< Flags whether worker thread notified           */
     
    349338    R3PTRTYPE(PPDMTHREAD)           pThread;                    /**< pointer to worker thread's handle              */
    350339    PVIRTIONETVIRTQ                 pVirtq;                     /**< pointer to queue                               */
    351     uint16_t                        idx;                        /**< Index of this worker                           */
     340    uint16_t                        uIdx;                       /**< Index of this worker                           */
    352341    uint16_t                        pad;
    353342} VIRTIONETWORKERR3;
     
    383372    uint16_t                cVirtqPairs;
    384373
     374    /** Number of Rx/Tx queue pairs that have already been initialized */
     375    uint16_t                cInitializedVirtqPairs;
     376
    385377    /** Number of virtqueues total (which includes each queue of each pair plus one control queue */
    386378    uint16_t                cVirtVirtqs;
     
    433425    /** Resetting flag */
    434426    uint8_t                 fResetting;
    435 
    436     /** Quiescing I/O activity flag */
    437     uint8_t                 fQuiescing;
    438427
    439428    /** Promiscuous mode -- RX filter accepts all packets. */
     
    525514    TMTIMERHANDLE                   hLinkUpTimer;
    526515
    527     /** True if in the process of quiescing I/O */
    528     uint32_t                        fQuiescing;
    529 
    530     /** For which purpose we're quiescing. */
    531     VIRTIOVMSTATECHANGED            enmQuiescingFor;
    532 
    533516} VIRTIONETR3;
    534517
     
    571554#ifdef IN_RING3
    572555static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread);
     556static int virtioNetR3CreateWorkerThreads(PPDMDEVINS, PVIRTIONET, PVIRTIONETCC);
    573557
    574558DECLINLINE(const char *) virtioNetThreadStateName(PPDMTHREAD pThread)
     
    693677{
    694678    RTStrCopy(pThis->aVirtqs[CTRLQIDX].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "controlq");
    695     for (uint16_t qPairIdx = 0; qPairIdx < pThis->cVirtqPairs; qPairIdx++)
     679    for (uint16_t qPairIdx = pThis->cInitializedVirtqPairs; qPairIdx < pThis->cVirtqPairs; qPairIdx++)
    696680    {
    697681        RTStrPrintf(pThis->aVirtqs[RXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "receiveq<%d>",  qPairIdx);
     
    854838            if (pVirtq->fHasWorker)
    855839            {
    856                 PVIRTIONETWORKER pWorker = pVirtq->pWorker;
     840                PVIRTIONETWORKER   pWorker  = pVirtq->pWorker;
    857841                PVIRTIONETWORKERR3 pWorkerR3 = pVirtq->pWorkerR3;
    858842
     
    910894
    911895        pHlp->pfnPrintf(pHlp, "    Transmitting: ............. %s\n", fTransmitting ? "true" : "false");
    912         pHlp->pfnPrintf(pHlp, "    Quiescing: ................ %s %s%s%s\n",
    913                 pThis->fQuiescing ? "true" : "false",
    914                 pThis->fQuiescing ? "(" : "",
    915                 pThis->fQuiescing ? virtioCoreGetStateChangeText(pThisCC->enmQuiescingFor) : "",
    916                 pThis->fQuiescing ? ")" : "");
    917         pHlp->pfnPrintf(pHlp, "    Resetting: ................ %s\n", pThis->fResetting ? "true" : "false");
    918896        pHlp->pfnPrintf(pHlp, "\n");
    919897        pHlp->pfnPrintf(pHlp, "Misc state\n");
     
    11531131     * Nudge queue workers
    11541132     */
    1155     for (int idxWorker = 0; idxWorker < pThis->cWorkers; idxWorker++)
    1156     {
    1157         PVIRTIONETWORKER pWorker = &pThis->aWorkers[idxWorker];
     1133    for (int uIdxWorker = 0; uIdxWorker < pThis->cWorkers; uIdxWorker++)
     1134    {
     1135        PVIRTIONETWORKER pWorker = &pThis->aWorkers[uIdxWorker];
    11581136        PVIRTIONETVIRTQ  pVirtq  = pWorker->pVirtq;
    11591137        if (pVirtq->fAttachedToVirtioCore)
     
    12241202*   Device interface.                                                                                                            *
    12251203*********************************************************************************************************************************/
    1226 /*xx*/
    1227 /**
    1228  * @callback_method_impl{FNPDMDEVASYNCNOTIFY}
    1229  */
    1230 static DECLCALLBACK(bool) virtioNetR3DeviceQuiesced(PPDMDEVINS pDevIns)
    1231 {
    1232     PVIRTIONET     pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    1233     PVIRTIONETCC   pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
    1234 
    1235     /** @todo create test to conclusively determine I/O has been quiesced and add it here: */
    1236 
    1237     Log7Func(("%s Device I/O activity quiesced: %s\n",
    1238         pThis->szInst, virtioCoreGetStateChangeText(pThisCC->enmQuiescingFor)));
    1239 
    1240     virtioCoreR3VmStateChanged(&pThis->Virtio, pThisCC->enmQuiescingFor);
    1241 
    1242     pThis->fResetting = false;
    1243     pThisCC->fQuiescing = false;
    1244 
    1245     return true;
    1246 }
    1247 
    1248 /**
    1249  * Worker for virtioNetR3Reset() and virtioNetR3SuspendOrPowerOff().
    1250  */
    1251 static void virtioNetR3QuiesceDevice(PPDMDEVINS pDevIns, VIRTIOVMSTATECHANGED enmQuiescingFor)
    1252 {
    1253     PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    1254     PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
    1255 
    1256     RT_NOREF(pThis);
    1257 
    1258     /* Prevent worker threads from removing/processing elements from virtq's */
    1259     pThisCC->fQuiescing = true;
    1260     pThisCC->enmQuiescingFor = enmQuiescingFor;
    1261 
    1262     /*
    1263      * Wake downstream network driver thread that's waiting for Rx buffers to be available
    1264      * to tell it that's going to happen...
    1265      */
    1266     virtioNetWakeupRxBufWaiter(pDevIns);
    1267 
    1268     PDMDevHlpSetAsyncNotification(pDevIns, virtioNetR3DeviceQuiesced);
    1269 
    1270     /* If already quiesced invoke async callback.  */
    1271     if (!ASMAtomicReadBool(&pThis->fLeafWantsEmptyRxBufs))
    1272         PDMDevHlpAsyncNotificationCompleted(pDevIns);
    1273 
    1274     /** @todo make sure Rx and Tx are really quiesced (how do we synchronize w/downstream driver?) */
    1275 }
    1276 
    1277 /**
    1278  * @interface_method_impl{PDMDEVREGR3,pfnReset}
    1279  */
    1280 static DECLCALLBACK(void) virtioNetR3Reset(PPDMDEVINS pDevIns)
    1281 {
    1282     PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    1283     Log7Func(("%s\n", pThis->szInst));
    1284     pThis->fResetting = true;
    1285     virtioNetR3QuiesceDevice(pDevIns, kvirtIoVmStateChangedReset);
    1286 }
    1287 
    1288 /**
    1289  * @interface_method_impl{PDMDEVREGR3,pfnPowerOff}
    1290  */
    1291 static DECLCALLBACK(void) virtioNetR3SuspendOrPowerOff(PPDMDEVINS pDevIns, VIRTIOVMSTATECHANGED enmType)
    1292 {
    1293 
    1294     PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    1295     PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
    1296     Log7Func(("%s\n", pThis->szInst));
    1297 
    1298     RT_NOREF2(pThis, pThisCC);
    1299 
    1300     virtioNetR3QuiesceDevice(pDevIns, enmType);
    1301     virtioNetWakeupRxBufWaiter(pDevIns);
    1302 }
    1303 
    1304 /**
    1305  * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
    1306  */
    1307 static DECLCALLBACK(void) virtioNetR3PowerOff(PPDMDEVINS pDevIns)
    1308 {
    1309     PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    1310     Log7Func(("%s\n", pThis->szInst));
    1311     RT_NOREF(pThis);
    1312     virtioNetR3SuspendOrPowerOff(pDevIns, kvirtIoVmStateChangedPowerOff);
    1313 }
    1314 
    1315 /**
    1316  * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
    1317  */
    1318 static DECLCALLBACK(void) virtioNetR3Suspend(PPDMDEVINS pDevIns)
    1319 {
    1320     PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    1321     Log7Func(("%s \n", pThis->szInst));
    1322     RT_NOREF(pThis);
    1323     virtioNetR3SuspendOrPowerOff(pDevIns, kvirtIoVmStateChangedSuspend);
    1324 }
    1325 
    1326 /**
    1327  * @interface_method_impl{PDMDEVREGR3,pfnResume}
    1328  *
    1329  * Just process the VM device-related state change itself.
    1330  * Unlike SCSI driver, there are no packets to redo. No I/O was halted or saved while
    1331  * quiescing for pfnSuspend(). Any packets in process were simply dropped by the upper
    1332  * layer driver, presumably to be retried or cause erring out at the upper layers
    1333  * of the network stack.
    1334  */
    1335 static DECLCALLBACK(void) virtioNetR3Resume(PPDMDEVINS pDevIns)
    1336 {
    1337     PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    1338     PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
    1339     Log7Func(("\n"));
    1340 
    1341     pThisCC->fQuiescing = false;
    1342 
    1343     /* Ensure guest is working the queues */
    1344     virtioCoreR3VmStateChanged(&pThis->Virtio, kvirtIoVmStateChangedResume);
    1345 }
    13461204
    13471205#ifdef IN_RING3
     
    14071265DECLINLINE(bool) virtioNetIsOperational(PVIRTIONET pThis, PPDMDEVINS pDevIns)
    14081266{
    1409     if (RT_LIKELY(pThis->fVirtioReady) && RT_LIKELY(!pThis->fQuiescing))
     1267    if (RT_LIKELY(pThis->fVirtioReady))
    14101268    {
    14111269        VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
     
    14341292        Log8Func(("%s No Rx bufs available. (VirtIO core not ready)\n", pThis->szInst));
    14351293
    1436     else if (!virtioCoreIsVirtqEnabled(&pThis->Virtio, pRxVirtq->idx))
     1294    else if (!virtioCoreIsVirtqEnabled(&pThis->Virtio, pRxVirtq->uIdx))
    14371295        Log8Func(("%s No Rx bufs available. (%s not enabled)\n",  pThis->szInst, pRxVirtq->szName));
    14381296
    1439     else if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pRxVirtq->idx))
     1297    else if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pRxVirtq->uIdx))
    14401298        Log8Func(("%s No Rx bufs available. (%s empty)\n",  pThis->szInst, pRxVirtq->szName));
    14411299
     
    14451303        rc = VINF_SUCCESS;
    14461304    }
    1447     virtioCoreVirtqEnableNotify(&pThis->Virtio, pRxVirtq->idx, rc == VERR_INVALID_STATE /* fEnable */);
     1305    virtioCoreVirtqEnableNotify(&pThis->Virtio, pRxVirtq->uIdx, rc == VERR_INVALID_STATE /* fEnable */);
    14481306    return rc;
    14491307}
     
    14731331    PVIRTIONET   pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
    14741332
     1333    if (!virtioNetIsOperational(pThis, pDevIns))
     1334        return VERR_INTERRUPTED;
     1335
    14751336    if (virtioNetR3RxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
    14761337    {
     
    14821343
    14831344    LogFunc(("%s %s\n", pThis->szInst, timeoutMs == RT_INDEFINITE_WAIT ? "<indefinite wait>" : ""));
     1345
    14841346
    14851347    ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, true);
     
    14901352        {
    14911353            Log10Func(("%s Rx bufs now available, releasing waiter...\n", pThis->szInst));
     1354            ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
    14921355            return VINF_SUCCESS;
    14931356        }
     
    14991362        {
    15001363            LogFunc(("Woken due to %s\n", rc == VERR_TIMEOUT ? "timeout" : "getting interrupted"));
     1364
     1365            if (!virtioNetIsOperational(pThis, pDevIns))
     1366                break;
     1367
    15011368            continue;
    15021369        }
     
    16971564        PVIRTQBUF pVirtqBuf = NULL;
    16981565
    1699         int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->idx, &pVirtqBuf, true);
     1566        int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, &pVirtqBuf, true);
    17001567        AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
    17011568
     
    17331600                /* Calculate & cache GCPhys addr of field to update after final value is known */
    17341601                GCPhysPktHdrNumBuffers = pVirtqBuf->pSgPhysReturn->paSegs[0].GCPhys
    1735                                          + RT_UOFFSETOF(VIRTIONETPKTHDR, uNumBuffers);
     1602                                       + RT_UOFFSETOF(VIRTIONETPKTHDR, uNumBuffers);
    17361603                fAddPktHdr = false;
    17371604                cSegs++;
     
    17581625            Log7Func(("Send Rx pkt to guest...\n"));
    17591626            STAM_PROFILE_START(&pThis->StatReceiveStore, a);
    1760             virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->idx,
     1627            virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx,
    17611628                                        pVirtSegBufToGuest, pVirtqBuf, true /* fFence */);
    17621629            STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
     
    17791646    AssertMsgRCReturn(rc, ("Failure updating descriptor count in pkt hdr in guest physical memory\n"), rc);
    17801647
    1781     virtioCoreVirtqSyncUsedRing(pDevIns, &pThis->Virtio, pRxVirtq->idx);
     1648    virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
    17821649
    17831650    return VINF_SUCCESS;
     
    17971664 * @param   cb              Number of bytes available in the buffer.
    17981665 * @param   pGso            Pointer to Global Segmentation Offload structure
    1799  * @param   idxRxVirtq      Rx queue to work with
     1666 * @param   pRxVirtq        Pointer to Rx virtqueue
    18001667 * @thread  RX
    18011668 */
     
    18761743    PPDMDEVINS      pDevIns = pThisCC->pDevIns;
    18771744    PVIRTIONET      pThis   = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
     1745
    18781746    if (!pThis->fVirtioReady)
    18791747    {
    18801748        LogRelFunc(("VirtIO not ready, aborting downstream receive\n"));
    1881         return VERR_INTERRUPTED;
    1882     }
    1883     if (pThis->fQuiescing)
    1884     {
    1885         LogRelFunc(("Quiescing I/O for suspend or power off, aborting downstream receive\n"));
    18861749        return VERR_INTERRUPTED;
    18871750    }
     
    21021965                LogFunc(("         %RTmac\n", &pThis->aMacMulticastFilter[i]));
    21031966#endif
     1967            break;
     1968        }
     1969        default:
     1970            LogRelFunc(("Unrecognized MAC subcommand in CTRL pkt from guest\n"));
     1971            return VIRTIONET_ERROR;
     1972    }
     1973    return VIRTIONET_OK;
     1974}
     1975
     1976static uint8_t virtioNetR3CtrlMultiQueue(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMDEVINS pDevIns, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
     1977{
     1978    LogFunc(("%s Processing CTRL MQ command\n", pThis->szInst));
     1979
     1980    uint16_t cVirtqPairs;
     1981    switch(pCtrlPktHdr->uCmd)
     1982    {
     1983        case VIRTIONET_CTRL_MQ_VQ_PAIRS_SET:
     1984        {
     1985            size_t cbRemaining = pVirtqBuf->cbPhysSend - sizeof(*pCtrlPktHdr);
     1986
     1987            AssertMsgReturn(cbRemaining > sizeof(cVirtqPairs),
     1988                ("DESC chain too small for VIRTIONET_CTRL_MQ cmd processing"), VIRTIONET_ERROR);
     1989
     1990            /* Fetch number of virtq pairs from guest buffer */
     1991            virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cVirtqPairs, sizeof(cVirtqPairs));
     1992
     1993            AssertMsgReturn(cVirtqPairs > VIRTIONET_MAX_QPAIRS,
     1994                ("%s Guest CTRL MQ virtq pair count out of range)\n", pThis->szInst, cVirtqPairs), VIRTIONET_ERROR);
     1995
     1996            LogFunc(("%s Guest specifies %d VQ pairs in use\n", pThis->szInst, cVirtqPairs));
     1997            pThis->cVirtqPairs = cVirtqPairs;
     1998            break;
     1999        }
     2000        default:
     2001            LogRelFunc(("Unrecognized multiqueue subcommand in CTRL pkt from guest\n"));
     2002            return VIRTIONET_ERROR;
     2003    }
     2004
     2005    /*
     2006     * The MQ control function is invoked by the guest in an RPC like manner to change
     2007     * the Rx/Tx queue pair count. If the new value exceeds the number of queues
     2008     * (and associated workers) already initialized initialize only the new queues and
     2009     * respective workers.
     2010     */
     2011    if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
     2012    {
     2013        virtioNetR3SetVirtqNames(pThis);
     2014        int rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
     2015        if (RT_FAILURE(rc))
     2016        {
     2017            LogRelFunc(("Failed to create worker threads\n"));
     2018            return VIRTIONET_ERROR;
    21042019        }
    21052020    }
     
    21152030
    21162031    AssertMsgReturn(cbRemaining > sizeof(uVlanId),
    2117         ("DESC chain too small for VIRTIO_NET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
     2032        ("DESC chain too small for VIRTIONET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
    21182033
    21192034    /* Fetch VLAN ID from guest buffer */
     
    21342049            break;
    21352050        default:
     2051            LogRelFunc(("Unrecognized VLAN subcommand in CTRL pkt from guest\n"));
    21362052            return VIRTIONET_ERROR;
    21372053    }
     
    21812097        case VIRTIONET_CTRL_VLAN:
    21822098            uAck = virtioNetR3CtrlVlan(pThis, pCtrlPktHdr, pVirtqBuf);
     2099            break;
     2100        case VIRTIONET_CTRL_MQ:
     2101            uAck = virtioNetR3CtrlMultiQueue(pThis, pThisCC, pDevIns, pCtrlPktHdr, pVirtqBuf);
    21832102            break;
    21842103        case VIRTIONET_CTRL_ANNOUNCE:
     
    21982117            Log7Func(("%s Clearing VIRTIONET_F_ANNOUNCE in config status\n", pThis->szInst));
    21992118            break;
    2200 
    22012119        default:
    22022120            LogRelFunc(("Unrecognized CTRL pkt hdr class (%d)\n", pCtrlPktHdr->uClass));
     
    22302148
    22312149    virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, CTRLQIDX, pReturnSegBuf, pVirtqBuf, true /* fFence */);
    2232     virtioCoreVirtqSyncUsedRing(pDevIns, &pThis->Virtio, CTRLQIDX);
     2150    virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, CTRLQIDX);
    22332151
    22342152    for (int i = 0; i < cSegs; i++)
     
    23832301    }
    23842302
    2385     int cPkts = virtioCoreVirtqAvailBufCount(pVirtio->pDevInsR3, pVirtio, pTxVirtq->idx);
     2303    int cPkts = virtioCoreVirtqAvailBufCount(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
    23862304    if (!cPkts)
    23872305    {
     
    24002318    int rc;
    24012319    PVIRTQBUF pVirtqBuf = NULL;
    2402     while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->idx, &pVirtqBuf)) == VINF_SUCCESS)
     2320    while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, &pVirtqBuf)) == VINF_SUCCESS)
    24032321    {
    24042322        Log10Func(("%s fetched descriptor chain from %s\n", pThis->szInst, pTxVirtq->szName));
     
    24852403            }
    24862404
    2487             /* Point to next descriptor in avail ring of virtq */
    2488             virtioCoreR3VirtqAvailBufNext(pVirtio, pTxVirtq->idx);
     2405            /* Point to next descriptor chain in avail ring of virtq */
     2406            virtioCoreR3VirtqAvailBufNext(pVirtio, pTxVirtq->uIdx);
    24892407
    24902408            /* No data to return to guest, but necessary to put elem (e.g. desc chain head idx) on used ring */
    2491             virtioCoreR3VirtqUsedBufPut(pVirtio->pDevInsR3, pVirtio, pTxVirtq->idx, NULL, pVirtqBuf, true /* fFence */);
     2409            virtioCoreR3VirtqUsedBufPut(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, NULL, pVirtqBuf, true /* fFence */);
    24922410
    24932411            /* Update used ring idx and notify guest that we've transmitted the data it sent */
    2494             virtioCoreVirtqSyncUsedRing(pVirtio->pDevInsR3, pVirtio, pTxVirtq->idx);
     2412            virtioCoreVirtqUsedRingSync(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
    24952413        }
    24962414
     
    25182436    STAM_COUNTER_INC(&pThis->StatTransmitByNetwork);
    25192437
    2520     /** @todo If we ever start using more than one Rx/Tx queue pair, is a random queue
    2521           selection algorithm feasible or even necessary */
    25222438    virtioNetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pTxVirtq, true /*fOnWorkerThread*/);
    25232439}
    2524 
    2525 
    2526 #ifdef USING_CRITICAL_SECTION
    2527 DECLINLINE(int) virtioNetR3CsEnter(PPDMDEVINS pDevIns, PVIRTIONET pThis, int rcBusy)
    2528 {
    2529     RT_NOREF(pDevIns, pThis, rcBusy);
    2530     /* Original DevVirtioNet uses CS in attach/detach/link-up timer/tx timer/transmit */
    2531     LogFunc(("%s CS unimplemented. What does the critical section protect in orig driver??", pThis->szInst));
    2532     return VINF_SUCCESS;
    2533 }
    2534 
    2535 DECLINLINE(void) virtioNetR3CsLeave(PPDMDEVINS pDevIns, PVIRTIONET pThis)
    2536 {
    2537     RT_NOREF(pDevIns, pThis);
    2538     LogFunc(("%s CS unimplemented. What does the critical section protect in orig driver??", pThis->szInst));
    2539 }
    2540 #endif /* USING_CRITICAL_SECTION */
    25412440
    25422441/**
     
    25492448    RT_NOREF(pTimer, pvUser);
    25502449
    2551     ENTER_CRITICAL_SECTION;
    2552 
    25532450    SET_LINK_UP(pThis);
    25542451
    25552452    virtioNetWakeupRxBufWaiter(pDevIns);
    2556 
    2557     LEAVE_CRITICAL_SECTION;
    25582453
    25592454    LogFunc(("%s Link is up\n", pThis->szInst));
     
    26722567    Log10Func(("%s\n", pThis->szInst));
    26732568    int rc = VINF_SUCCESS;
    2674     for (unsigned idxWorker = 0; idxWorker < pThis->cWorkers; idxWorker++)
    2675     {
    2676         PVIRTIONETWORKER   pWorker   = &pThis->aWorkers[idxWorker];
    2677         PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[idxWorker];
     2569    for (unsigned uIdxWorker = 0; uIdxWorker < pThis->cWorkers; uIdxWorker++)
     2570    {
     2571        PVIRTIONETWORKER   pWorker   = &pThis->aWorkers[uIdxWorker];
     2572        PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uIdxWorker];
    26782573
    26792574        if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
     
    26942589}
    26952590
    2696 static int virtioNetR3CreateOneWorkerThread(PPDMDEVINS pDevIns, PVIRTIONET pThis, uint16_t idxWorker,
     2591static int virtioNetR3CreateOneWorkerThread(PPDMDEVINS pDevIns, PVIRTIONET pThis, uint16_t uIdxWorker,
    26972592                                            PVIRTIONETWORKER pWorker, PVIRTIONETWORKERR3 pWorkerR3,
    26982593                                            PVIRTIONETVIRTQ pVirtq)
     
    27142609    if (RT_FAILURE(rc))
    27152610        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
    2716                                    N_("Error creating thread for Virtual Virtq %s\n"), pVirtq->idx);
     2611                                   N_("Error creating thread for Virtual Virtq %s\n"), pVirtq->uIdx);
    27172612
    27182613    pWorker->pVirtq    = pWorkerR3->pVirtq   = pVirtq;
    2719     pWorker->idx       = pWorkerR3->idx      = idxWorker;
     2614    pWorker->uIdx      = pWorkerR3->uIdx     = uIdxWorker;
    27202615    pVirtq->pWorker    = pWorker;
    27212616    pVirtq->pWorkerR3  = pWorkerR3;
     
    27352630
    27362631    PVIRTIONETVIRTQ pCtlVirtq = &pThis->aVirtqs[CTRLQIDX];
    2737     int rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, CTRLQIDX /* idxWorker */,
     2632    int rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, CTRLQIDX /* uIdxWorker */,
    27382633                                              &pThis->aWorkers[CTRLWIDX], &pThisCC->aWorkers[CTRLWIDX], pCtlVirtq);
    27392634    AssertRCReturn(rc, rc);
     
    27412636    pCtlVirtq->fHasWorker = true;
    27422637
    2743     uint16_t idxWorker = CTRLWIDX + 1;
    2744     for (uint16_t uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++, idxWorker++)
     2638    uint16_t uIdxWorker = CTRLWIDX + 1;
     2639    for (uint16_t uVirtqPair = pThis->cInitializedVirtqPairs; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++, uIdxWorker++)
    27452640    {
    27462641        PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(uVirtqPair)];
    27472642        PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
    27482643
    2749         rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, idxWorker, &pThis->aWorkers[idxWorker],
    2750                                               &pThisCC->aWorkers[idxWorker], pTxVirtq);
     2644        rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, uIdxWorker, &pThis->aWorkers[uIdxWorker],
     2645                                              &pThisCC->aWorkers[uIdxWorker], pTxVirtq);
    27512646        AssertRCReturn(rc, rc);
    27522647
     
    27542649        pRxVirtq->fHasWorker = false;
    27552650    }
     2651
     2652    if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
     2653        pThis->cInitializedVirtqPairs = pThis->cVirtqPairs;
     2654
    27562655    pThis->cWorkers = pThis->cVirtqPairs + 1 /* One control virtq */;
    27572656    return rc;
     
    27752674    /** @todo Race w/guest enabling/disabling guest notifications cyclically.
    27762675              See BugRef #8651, Comment #82 */
    2777     virtioCoreVirtqEnableNotify(&pThis->Virtio, pVirtq->idx, true /* fEnable */);
    2778 
    2779     while (pThread->enmState == PDMTHREADSTATE_RUNNING)
    2780     {
    2781         if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pVirtq->idx))
     2676    virtioCoreVirtqEnableNotify(&pThis->Virtio, pVirtq->uIdx, true /* fEnable */);
     2677
     2678    while (   pThread->enmState != PDMTHREADSTATE_TERMINATING
     2679           && pThread->enmState != PDMTHREADSTATE_TERMINATED)
     2680    {
     2681        if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pVirtq->uIdx))
    27822682        {
    27832683            /* Atomic interlocks avoid missing alarm while going to sleep & notifier waking the awoken */
     
    28022702        /* Dispatch to the handler for the queue this worker is set up to drive */
    28032703
    2804         if (!pThisCC->fQuiescing)
    2805         {
    2806              if (pVirtq->fCtlVirtq)
     2704         if (pVirtq->fCtlVirtq)
     2705         {
     2706             Log10Func(("%s %s worker woken. Fetching desc chain\n", pThis->szInst, pVirtq->szName));
     2707             PVIRTQBUF pVirtqBuf = NULL;
     2708             int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, &pVirtqBuf, true);
     2709             if (rc == VERR_NOT_AVAILABLE)
    28072710             {
    2808                  Log10Func(("%s %s worker woken. Fetching desc chain\n", pThis->szInst, pVirtq->szName));
    2809                  PVIRTQBUF pVirtqBuf = NULL;
    2810                  int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->idx, &pVirtqBuf, true);
    2811                  if (rc == VERR_NOT_AVAILABLE)
    2812                  {
    2813                     Log10Func(("%s %s worker woken. Nothing found in queue/n", pThis->szInst, pVirtq->szName));
    2814                     continue;
    2815                  }
    2816                  virtioNetR3Ctrl(pDevIns, pThis, pThisCC, pVirtqBuf);
    2817                  virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
     2711                Log10Func(("%s %s worker woken. Nothing found in queue/n", pThis->szInst, pVirtq->szName));
     2712                continue;
    28182713             }
    2819              else /* Must be Tx queue */
    2820              {
    2821                  Log10Func(("%s %s worker woken. Virtq has data to transmit\n",  pThis->szInst, pVirtq->szName));
    2822                  virtioNetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pVirtq, false /* fOnWorkerThread */);
    2823              }
    2824 
    2825              /* Rx queues aren't handled by our worker threads. Instead, the PDM network
    2826               * leaf driver invokes PDMINETWORKDOWN.pfnWaitReceiveAvail() callback,
    2827               * which waits until notified directly by virtioNetVirtqNotified()
    2828               * that guest IN buffers have been added to receive virt queue.
    2829               */
    2830         }
     2714             virtioNetR3Ctrl(pDevIns, pThis, pThisCC, pVirtqBuf);
     2715             virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
     2716         }
     2717         else /* Must be Tx queue */
     2718         {
     2719             Log10Func(("%s %s worker woken. Virtq has data to transmit\n",  pThis->szInst, pVirtq->szName));
     2720             virtioNetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pVirtq, false /* fOnWorkerThread */);
     2721         }
     2722
     2723         /* Rx queues aren't handled by our worker threads. Instead, the PDM network
     2724          * leaf driver invokes PDMINETWORKDOWN.pfnWaitReceiveAvail() callback,
     2725          * which waits until woken by virtioNetVirtqNotified()
     2726          * indicating that guest IN buffers have been added to Rx virt queue.
     2727          */
    28312728    }
    28322729    Log10(("%s %s worker thread exiting\n", pThis->szInst, pVirtq->szName));
     
    28592756
    28602757        pThis->fResetting = false;
    2861         pThisCC->fQuiescing = false;
    28622758
    28632759        for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtVirtqs; uVirtqNbr++)
    28642760        {
    28652761            PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
    2866             pVirtq->idx = uVirtqNbr;
    2867             (void) virtioCoreR3VirtqAttach(&pThis->Virtio, pVirtq->idx, pVirtq->szName);
     2762            pVirtq->uIdx = uVirtqNbr;
     2763            (void) virtioCoreR3VirtqAttach(&pThis->Virtio, pVirtq->uIdx, pVirtq->szName);
    28682764            pVirtq->fAttachedToVirtioCore = true;
    2869             if (IS_VIRTQ_EMPTY(pThisCC->pDevIns, &pThis->Virtio, pVirtq->idx))
    2870                 virtioCoreVirtqEnableNotify(&pThis->Virtio, pVirtq->idx, true /* fEnable */);
     2765            if (IS_VIRTQ_EMPTY(pThisCC->pDevIns, &pThis->Virtio, pVirtq->uIdx))
     2766                virtioCoreVirtqEnableNotify(&pThis->Virtio, pVirtq->uIdx, true /* fEnable */);
    28712767        }
    28722768    }
     
    29162812    AssertLogRelReturnVoid(iLUN == 0);
    29172813
    2918     ENTER_CRITICAL_SECTION;
    2919 
    29202814    RT_NOREF(pThis);
    29212815
     
    29252819    pThisCC->pDrvBase = NULL;
    29262820    pThisCC->pDrv     = NULL;
    2927 
    2928     LEAVE_CRITICAL_SECTION;
    29292821}
    29302822
     
    29452837    AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
    29462838
    2947     ENTER_CRITICAL_SECTION;
    2948 
    29492839    RT_NOREF(pThis);
    29502840
     
    29602850                    Log(("%s No attached driver!\n", pThis->szInst));
    29612851
    2962     LEAVE_CRITICAL_SECTION;
    29632852    return rc;
    29642853}
     
    30982987#   endif
    30992988
    3100 #   if FEATURE_OFFERED(MQ)
    3101         pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_MAX_QPAIRS;
    3102 #   endif
     2989    pThis->virtioNetConfig.uMaxVirtqPairs   = VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX;
    31032990
    31042991    pThisCC->Virtio.pfnVirtqNotified        = virtioNetVirtqNotified;
     
    31213008        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create event semaphore"));
    31223009
    3123     /* Initialize VirtIO core. (pfnStatusChanged callback when VirtIO and guest are ready) */
     3010    /* Initialize VirtIO core. (pfnStatusChanged callback when both host VirtIO core & guest driver are ready) */
    31243011    rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, pThis->szInst,
    31253012                          VIRTIONET_HOST_FEATURES_OFFERED,
     
    31323019        return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: Required features not successfully negotiated."));
    31333020
    3134     pThis->cVirtqPairs =  (pThis->fNegotiatedFeatures & VIRTIONET_F_MQ)
    3135                          ? pThis->virtioNetConfig.uMaxVirtqPairs  :  1;
     3021    pThis->cVirtqPairs = 1;  /* default, VirtIO 1.0, 5.1.6.5.5 */
    31363022
    31373023    pThis->cVirtVirtqs += pThis->cVirtqPairs * 2 + 1;
     
    31523038    rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
    31533039    if (RT_FAILURE(rc))
    3154         return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to worker threads"));
     3040        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create worker threads"));
    31553041
    31563042    /*
     
    32653151    /* .pfnMemSetup = */            NULL,
    32663152    /* .pfnPowerOn = */             NULL,
    3267     /* .pfnReset = */               virtioNetR3Reset,
    3268     /* .pfnSuspend = */             virtioNetR3Suspend,
    3269     /* .pfnResume = */              virtioNetR3Resume,
     3153    /* .pfnReset = */               NULL,
     3154    /* .pfnSuspend = */             virtioNetWakeupRxBufWaiter,
     3155    /* .pfnResume = */              NULL,
    32703156    /* .pfnAttach = */              virtioNetR3Attach,
    32713157    /* .pfnDetach = */              virtioNetR3Detach,
    32723158    /* .pfnQueryInterface = */      NULL,
    32733159    /* .pfnInitComplete = */        NULL,
    3274     /* .pfnPowerOff = */            virtioNetR3PowerOff,
     3160    /* .pfnPowerOff = */            virtioNetWakeupRxBufWaiter,
    32753161    /* .pfnSoftReset = */           NULL,
    32763162    /* .pfnReserved0 = */           NULL,
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