Index: /trunk/src/VBox/Devices/Network/DevE1000.cpp
===================================================================
--- /trunk/src/VBox/Devices/Network/DevE1000.cpp	(revision 40991)
+++ /trunk/src/VBox/Devices/Network/DevE1000.cpp	(revision 40992)
@@ -31,19 +31,71 @@
 #define E1kLogRel(a)
 
-/* Options */
+/* Options *******************************************************************/
+/*
+ * E1K_INIT_RA0 forces E1000 to set the first entry in Receive Address filter
+ * table to MAC address obtained from CFGM. Most guests read MAC address from
+ * EEPROM and write it to RA[0] explicitly, but Mac OS X seems to depend on it
+ * being already set (see #4657).
+ */
 #define E1K_INIT_RA0
+/*
+ * E1K_LSC_ON_SLU causes E1000 to generate Link Status Change interrupt when
+ * the guest driver brings up the link via STATUS.LU bit. Again the only guest
+ * that requires it is Mac OS X (see #4657).
+ */
 #define E1K_LSC_ON_SLU
+/*
+ * E1K_ITR_ENABLED reduces the number of interrupts generated by E1000 if a
+ * guest driver requested it by writing non-zero value to the Interrupt
+ * Throttling Register (see section 13.4.18 in "8254x Family of Gigabit
+ * Ethernet Controllers Software Developer’s Manual").
+ */
 #define E1K_ITR_ENABLED
-//#define E1K_GLOBAL_MUTEX
+/*
+ * E1K_USE_TX_TIMERS aims to reduce the number of generated TX interrupts if a
+ * guest driver set the delays via the Transmit Interrupt Delay Value (TIDV)
+ * register. Enabling it showed no positive effects on existing guests so it
+ * stays disabled. See sections 3.2.7.1 and 3.4.3.1 in "8254x Family of Gigabit
+ * Ethernet Controllers Software Developer’s Manual" for more detailed
+ * explanation.
+ */
 //#define E1K_USE_TX_TIMERS
+/*
+ * E1K_NO_TAD disables one of two timers enabled by E1K_USE_TX_TIMERS, the
+ * Transmit Absolute Delay time. This timer sets the maximum time interval
+ * during which TX interrupts can be postponed (delayed). It has no effect
+ * if E1K_USE_TX_TIMERS is not defined.
+ */
 //#define E1K_NO_TAD
+/*
+ * E1K_REL_DEBUG enables debug logging of l1, l2, l3 in release build.
+ */
 //#define E1K_REL_DEBUG
+/*
+ * E1K_INT_STATS enables collection of internal statistics used for
+ * debugging of delayed interrupts, etc.
+ */
 //#define E1K_INT_STATS
-//#define E1K_REL_STATS
-//#define E1K_USE_SUPLIB_SEMEVENT
+/*
+ * E1K_WITH_MSI enables rudimentary MSI support. Not implemented.
+ */
 //#define E1K_WITH_MSI
+/*
+ * E1K_WITH_TXD_CACHE causes E1000 to fetch multiple TX descriptors in a
+ * single physical memory read (or two if it wraps around the end of TX
+ * descriptor ring). It is required for proper functioning of bandwidth
+ * resource control as it allows to compute exact sizes of packets prior
+ * to allocating their buffers (see #5582).
+ */
 #define E1K_WITH_TXD_CACHE 1
+/* End of Options ************************************************************/
 
 #ifdef E1K_WITH_TXD_CACHE
+/*
+ * E1K_TXD_CACHE_SIZE specifies the maximum number of TX descriptors stored
+ * in the state structure. It limits the amount of descriptors loaded in one
+ * batch read. For example, Windows XP guest uses about 5 descriptors per
+ * TSE packet.
+ */
 #define E1K_TXD_CACHE_SIZE 16u
 #endif /* E1K_WITH_TXD_CACHE */
@@ -75,12 +127,4 @@
 
 #ifndef DEBUG
-# ifdef E1K_REL_STATS
-#  undef STAM_COUNTER_INC
-#  undef STAM_PROFILE_ADV_START
-#  undef STAM_PROFILE_ADV_STOP
-#  define STAM_COUNTER_INC       STAM_REL_COUNTER_INC
-#  define STAM_PROFILE_ADV_START STAM_REL_PROFILE_ADV_START
-#  define STAM_PROFILE_ADV_STOP  STAM_REL_PROFILE_ADV_STOP
-# endif
 # ifdef E1K_REL_DEBUG
 #  define DEBUG
@@ -962,8 +1006,6 @@
 #endif
     PDMCRITSECT cs;                  /**< Critical section - what is it protecting? */
-#ifndef E1K_GLOBAL_MUTEX
     PDMCRITSECT csRx;                                     /**< RX Critical section. */
 //    PDMCRITSECT csTx;                                     /**< TX Critical section. */
-#endif
     /** Base address of memory-mapped registers. */
     RTGCPHYS    addrMMReg;
@@ -1081,5 +1123,5 @@
     STAMCOUNTER                         StatReceiveBytes;
     STAMCOUNTER                         StatTransmitBytes;
-#if defined(VBOX_WITH_STATISTICS) || defined(E1K_REL_STATS)
+#if defined(VBOX_WITH_STATISTICS)
     STAMPROFILEADV                      StatMMIOReadRZ;
     STAMPROFILEADV                      StatMMIOReadR3;
@@ -1116,5 +1158,5 @@
     STAMCOUNTER                         StatPHYAccesses;
 
-#endif /* VBOX_WITH_STATISTICS || E1K_REL_STATS */
+#endif /* VBOX_WITH_STATISTICS */
 
 #ifdef E1K_INT_STATS
@@ -1437,93 +1479,15 @@
 }
 
-#ifdef E1K_GLOBAL_MUTEX
-
-DECLINLINE(int) e1kCsEnter(E1KSTATE *pState, int iBusyRc)
-{
-    return VINF_SUCCESS;
-}
-
-DECLINLINE(void) e1kCsLeave(E1KSTATE *pState)
-{
-}
-
-# define e1kCsRxEnter(ps, rc) VINF_SUCCESS
-# define e1kCsRxLeave(ps) do { } while (0)
-
-# define e1kCsTxEnter(ps, rc) VINF_SUCCESS
-# define e1kCsTxLeave(ps) do { } while (0)
-
-
-DECLINLINE(int) e1kMutexAcquire(E1KSTATE *pState, int iBusyRc, RT_SRC_POS_DECL)
-{
-    int rc = PDMCritSectEnter(&pState->cs, iBusyRc);
-    if (RT_UNLIKELY(rc != VINF_SUCCESS))
-    {
-        E1kLog2(("%s ==> FAILED to enter critical section at %s:%d:%s with rc=\n",
-                INSTANCE(pState), RT_SRC_POS_ARGS, rc));
-        PDMDevHlpDBGFStop(pState->CTX_SUFF(pDevIns), RT_SRC_POS_ARGS,
-                          "%s Failed to enter critical section, rc=%Rrc\n",
-                          INSTANCE(pState), rc);
-    }
-    else
-    {
-        //E1kLog2(("%s ==> Mutex acquired at %s:%d:%s\n", INSTANCE(pState), RT_SRC_POS_ARGS));
-    }
-    return rc;
-}
-
-DECLINLINE(void) e1kMutexRelease(E1KSTATE *pState)
-{
-    //E1kLog2(("%s <== Releasing mutex...\n", INSTANCE(pState)));
-    PDMCritSectLeave(&pState->cs);
-}
-
-#else /* !E1K_GLOBAL_MUTEX */
-# define e1kCsEnter(ps, rc) PDMCritSectEnter(&ps->cs, rc)
-# define e1kCsLeave(ps) PDMCritSectLeave(&ps->cs)
-
-# define e1kCsRxEnter(ps, rc) PDMCritSectEnter(&ps->csRx, rc)
-# define e1kCsRxLeave(ps) PDMCritSectLeave(&ps->csRx)
-
-# define e1kCsTxEnter(ps, rc) VINF_SUCCESS
-# define e1kCsTxLeave(ps) do { } while (0)
+#define e1kCsEnter(ps, rc) PDMCritSectEnter(&ps->cs, rc)
+#define e1kCsLeave(ps) PDMCritSectLeave(&ps->cs)
+
+#define e1kCsRxEnter(ps, rc) PDMCritSectEnter(&ps->csRx, rc)
+#define e1kCsRxLeave(ps) PDMCritSectLeave(&ps->csRx)
+
+#define e1kCsTxEnter(ps, rc) VINF_SUCCESS
+#define e1kCsTxLeave(ps) do { } while (0)
 //# define e1kCsTxEnter(ps, rc) PDMCritSectEnter(&ps->csTx, rc)
 //# define e1kCsTxLeave(ps) PDMCritSectLeave(&ps->csTx)
 
-# if 0
-DECLINLINE(int) e1kCsEnter(E1KSTATE *pState, PPDMCRITSECT pCs, int iBusyRc, RT_SRC_POS_DECL)
-{
-    int rc = PDMCritSectEnter(pCs, iBusyRc);
-    if (RT_FAILURE(rc))
-    {
-        E1kLog2(("%s ==> FAILED to enter critical section at %s:%d:%s with rc=%Rrc\n",
-                INSTANCE(pState), RT_SRC_POS_ARGS, rc));
-        PDMDeviceDBGFStop(pState->CTX_SUFF(pDevIns), RT_SRC_POS_ARGS,
-                          "%s Failed to enter critical section, rc=%Rrc\n",
-                          INSTANCE(pState), rc);
-    }
-    else
-    {
-        //E1kLog2(("%s ==> Entered critical section at %s:%d:%s\n", INSTANCE(pState), RT_SRC_POS_ARGS));
-    }
-    return RT_SUCCESS(rc);
-}
-
-DECLINLINE(void) e1kCsLeave(E1KSTATE *pState, PPDMCRITSECT pCs)
-{
-    //E1kLog2(("%s <== Leaving critical section\n", INSTANCE(pState)));
-    PDMCritSectLeave(&pState->cs);
-}
-# endif
-DECLINLINE(int) e1kMutexAcquire(E1KSTATE *pState, int iBusyRc, RT_SRC_POS_DECL)
-{
-    return VINF_SUCCESS;
-}
-
-DECLINLINE(void) e1kMutexRelease(E1KSTATE *pState)
-{
-}
-
-#endif /* !E1K_GLOBAL_MUTEX */
 #ifdef IN_RING3
 
@@ -1821,8 +1785,6 @@
                 pState->fIntRaised = true;
                 /* Raise(1) INTA(0) */
-                //e1kMutexRelease(pState);
                 E1kLogRel(("E1000: irq RAISED icr&mask=0x%x, icr=0x%x\n", ICR & IMS, ICR));
                 PDMDevHlpPCISetIrq(pState->CTX_SUFF(pDevIns), 0, 1);
-                //e1kMutexAcquire(pState, RT_SRC_POS);
                 E1kLog(("%s e1kRaiseInterrupt: Raised. ICR&IMS=%08x\n",
                         INSTANCE(pState), ICR & IMS));
@@ -2034,9 +1996,7 @@
     uint8_t  *ptr = rxPacket;
 
-#ifndef E1K_GLOBAL_MUTEX
     int rc = e1kCsRxEnter(pState, VERR_SEM_BUSY);
     if (RT_UNLIKELY(rc != VINF_SUCCESS))
         return rc;
-#endif
 
     if (cb > 70) /* unqualified guess */
@@ -2507,7 +2467,5 @@
                 pState->fIntRaised = false;
                 /* Lower(0) INTA(0) */
-                //e1kMutexRelease(pState);
                 PDMDevHlpPCISetIrq(pState->CTX_SUFF(pDevIns), 0, 0);
-                //e1kMutexAcquire(pState, RT_SRC_POS);
 
                 pState->u64AckedAt = TMTimerGet(pState->CTX_SUFF(pIntTimer));
@@ -2779,14 +2737,10 @@
     E1KSTATE *pState = (E1KSTATE *)pvUser;
 
-    if (RT_LIKELY(e1kMutexAcquire(pState, VERR_SEM_BUSY, RT_SRC_POS) == VINF_SUCCESS))
-    {
-        E1K_INC_ISTAT_CNT(pState->uStatTID);
-        /* Cancel absolute delay timer as we have already got attention */
+    E1K_INC_ISTAT_CNT(pState->uStatTID);
+    /* Cancel absolute delay timer as we have already got attention */
 #ifndef E1K_NO_TAD
-        e1kCancelTimer(pState, pState->CTX_SUFF(pTADTimer));
+    e1kCancelTimer(pState, pState->CTX_SUFF(pTADTimer));
 #endif /* E1K_NO_TAD */
-        e1kRaiseInterrupt(pState, ICR_TXDW);
-        e1kMutexRelease(pState);
-    }
+    e1kRaiseInterrupt(pState, ICR_TXDW);
 }
 
@@ -2805,12 +2759,8 @@
     E1KSTATE *pState = (E1KSTATE *)pvUser;
 
-    if (RT_LIKELY(e1kMutexAcquire(pState, VERR_SEM_BUSY, RT_SRC_POS) == VINF_SUCCESS))
-    {
-        E1K_INC_ISTAT_CNT(pState->uStatTAD);
-        /* Cancel interrupt delay timer as we have already got attention */
-        e1kCancelTimer(pState, pState->CTX_SUFF(pTIDTimer));
-        e1kRaiseInterrupt(pState, ICR_TXDW);
-        e1kMutexRelease(pState);
-    }
+    E1K_INC_ISTAT_CNT(pState->uStatTAD);
+    /* Cancel interrupt delay timer as we have already got attention */
+    e1kCancelTimer(pState, pState->CTX_SUFF(pTIDTimer));
+    e1kRaiseInterrupt(pState, ICR_TXDW);
 }
 
@@ -2832,12 +2782,8 @@
     E1KSTATE *pState = (E1KSTATE *)pvUser;
 
-    if (RT_LIKELY(e1kMutexAcquire(pState, VERR_SEM_BUSY, RT_SRC_POS) == VINF_SUCCESS))
-    {
-        E1K_INC_ISTAT_CNT(pState->uStatRID);
-        /* Cancel absolute delay timer as we have already got attention */
-        e1kCancelTimer(pState, pState->CTX_SUFF(pRADTimer));
-        e1kRaiseInterrupt(pState, ICR_RXT0);
-        e1kMutexRelease(pState);
-    }
+    E1K_INC_ISTAT_CNT(pState->uStatRID);
+    /* Cancel absolute delay timer as we have already got attention */
+    e1kCancelTimer(pState, pState->CTX_SUFF(pRADTimer));
+    e1kRaiseInterrupt(pState, ICR_RXT0);
 }
 
@@ -2856,12 +2802,8 @@
     E1KSTATE *pState = (E1KSTATE *)pvUser;
 
-    if (RT_LIKELY(e1kMutexAcquire(pState, VERR_SEM_BUSY, RT_SRC_POS) == VINF_SUCCESS))
-    {
-        E1K_INC_ISTAT_CNT(pState->uStatRAD);
-        /* Cancel interrupt delay timer as we have already got attention */
-        e1kCancelTimer(pState, pState->CTX_SUFF(pRIDTimer));
-        e1kRaiseInterrupt(pState, ICR_RXT0);
-        e1kMutexRelease(pState);
-    }
+    E1K_INC_ISTAT_CNT(pState->uStatRAD);
+    /* Cancel interrupt delay timer as we have already got attention */
+    e1kCancelTimer(pState, pState->CTX_SUFF(pRIDTimer));
+    e1kRaiseInterrupt(pState, ICR_RXT0);
 }
 
@@ -2881,15 +2823,11 @@
 
     STAM_PROFILE_ADV_START(&pState->StatLateIntTimer, a);
-    if (RT_LIKELY(e1kMutexAcquire(pState, VERR_SEM_BUSY, RT_SRC_POS) == VINF_SUCCESS))
-    {
-        STAM_COUNTER_INC(&pState->StatLateInts);
-        E1K_INC_ISTAT_CNT(pState->uStatIntLate);
+    STAM_COUNTER_INC(&pState->StatLateInts);
+    E1K_INC_ISTAT_CNT(pState->uStatIntLate);
 #if 0
-        if (pState->iStatIntLost > -100)
-            pState->iStatIntLost--;
+    if (pState->iStatIntLost > -100)
+        pState->iStatIntLost--;
 #endif
-        e1kRaiseInterrupt(pState, VERR_SEM_BUSY, 0);
-        e1kMutexRelease(pState);
-    }
+    e1kRaiseInterrupt(pState, VERR_SEM_BUSY, 0);
     STAM_PROFILE_ADV_STOP(&pState->StatLateIntTimer, a);
 }
@@ -2915,11 +2853,7 @@
         return;
 
-    if (RT_LIKELY(e1kMutexAcquire(pState, VERR_SEM_BUSY, RT_SRC_POS) == VINF_SUCCESS))
-    {
-        STATUS |= STATUS_LU;
-        Phy::setLinkStatus(&pState->phy, true);
-        e1kRaiseInterrupt(pState, VERR_SEM_BUSY, ICR_LSC);
-        e1kMutexRelease(pState);
-    }
+    STATUS |= STATUS_LU;
+    Phy::setLinkStatus(&pState->phy, true);
+    e1kRaiseInterrupt(pState, VERR_SEM_BUSY, ICR_LSC);
 }
 
@@ -3420,9 +3354,7 @@
             /* Release critical section to avoid deadlock in CanReceive */
             //e1kCsLeave(pState);
-            e1kMutexRelease(pState);
             STAM_PROFILE_START(&pState->CTX_SUFF_Z(StatTransmitSend), a);
             rc = pDrv->pfnSendBuf(pDrv, pSg, fOnWorkerThread);
             STAM_PROFILE_STOP(&pState->CTX_SUFF_Z(StatTransmitSend), a);
-            e1kMutexAcquire(pState, VERR_SEM_BUSY, RT_SRC_POS);
             //e1kCsEnter(pState, RT_SRC_POS);
         }
@@ -4581,43 +4513,38 @@
             return rc;
     }
-    rc = e1kMutexAcquire(pState, VERR_TRY_AGAIN, RT_SRC_POS);
-    if (RT_SUCCESS(rc))
-    {
-        /*
-         * Process all pending descriptors.
-         * Note! Do not process descriptors in locked state
-         */
-        while (TDH != TDT && !pState->fLocked)
-        {
-            E1KTXDESC desc;
-            E1kLog3(("%s About to process new TX descriptor at %08x%08x, TDLEN=%08x, TDH=%08x, TDT=%08x\n",
-                     INSTANCE(pState), TDBAH, TDBAL + TDH * sizeof(desc), TDLEN, TDH, TDT));
-
-            e1kLoadDesc(pState, &desc, ((uint64_t)TDBAH << 32) + TDBAL + TDH * sizeof(desc));
-            rc = e1kXmitDesc(pState, &desc, ((uint64_t)TDBAH << 32) + TDBAL + TDH * sizeof(desc), fOnWorkerThread);
-            /* If we failed to transmit descriptor we will try it again later */
-            if (RT_FAILURE(rc))
-                break;
-            if (++TDH * sizeof(desc) >= TDLEN)
-                TDH = 0;
-
-            if (e1kGetTxLen(pState) <= GET_BITS(TXDCTL, LWTHRESH)*8)
-            {
-                E1kLog2(("%s Low on transmit descriptors, raise ICR.TXD_LOW, len=%x thresh=%x\n",
-                         INSTANCE(pState), e1kGetTxLen(pState), GET_BITS(TXDCTL, LWTHRESH)*8));
-                e1kRaiseInterrupt(pState, VERR_SEM_BUSY, ICR_TXD_LOW);
-            }
-
-            STAM_PROFILE_ADV_STOP(&pState->CTX_SUFF_Z(StatTransmit), a);
-        }
-
-        /// @todo: uncomment: pState->uStatIntTXQE++;
-        /// @todo: uncomment: e1kRaiseInterrupt(pState, ICR_TXQE);
-
-        /*
-         * Release the locks.
-         */
-        e1kMutexRelease(pState);
-    }
+    /*
+     * Process all pending descriptors.
+     * Note! Do not process descriptors in locked state
+     */
+    while (TDH != TDT && !pState->fLocked)
+    {
+        E1KTXDESC desc;
+        E1kLog3(("%s About to process new TX descriptor at %08x%08x, TDLEN=%08x, TDH=%08x, TDT=%08x\n",
+                 INSTANCE(pState), TDBAH, TDBAL + TDH * sizeof(desc), TDLEN, TDH, TDT));
+
+        e1kLoadDesc(pState, &desc, ((uint64_t)TDBAH << 32) + TDBAL + TDH * sizeof(desc));
+        rc = e1kXmitDesc(pState, &desc, ((uint64_t)TDBAH << 32) + TDBAL + TDH * sizeof(desc), fOnWorkerThread);
+        /* If we failed to transmit descriptor we will try it again later */
+        if (RT_FAILURE(rc))
+            break;
+        if (++TDH * sizeof(desc) >= TDLEN)
+            TDH = 0;
+
+        if (e1kGetTxLen(pState) <= GET_BITS(TXDCTL, LWTHRESH)*8)
+        {
+            E1kLog2(("%s Low on transmit descriptors, raise ICR.TXD_LOW, len=%x thresh=%x\n",
+                     INSTANCE(pState), e1kGetTxLen(pState), GET_BITS(TXDCTL, LWTHRESH)*8));
+            e1kRaiseInterrupt(pState, VERR_SEM_BUSY, ICR_TXD_LOW);
+        }
+
+        STAM_PROFILE_ADV_STOP(&pState->CTX_SUFF_Z(StatTransmit), a);
+    }
+
+    /// @todo: uncomment: pState->uStatIntTXQE++;
+    /// @todo: uncomment: e1kRaiseInterrupt(pState, ICR_TXQE);
+
+    /*
+     * Release the lock.
+     */
     if (pDrv)
         pDrv->pfnEndXmit(pDrv);
@@ -4647,65 +4574,58 @@
             return rc;
     }
-    rc = e1kMutexAcquire(pState, VERR_TRY_AGAIN, RT_SRC_POS);
-    if (RT_SUCCESS(rc))
-    {
-        /*size_t cbPacket = 0;
-        int nDescInPacket = 0;
-        E1KTXDESC *pFirstDesc = pState->aTxDescriptors;*/
-        /*
-         * Process all pending descriptors.
-         * Note! Do not process descriptors in locked state
-         */
-        STAM_PROFILE_ADV_START(&pState->CTX_SUFF_Z(StatTransmit), a);
-        while (!pState->fLocked && e1kTxDLazyLoad(pState))
-        {
-            while (e1kLocateTxPacket(pState))
-            {
-                // 1) packet located -- allocate it!
-                rc = e1kXmitAllocBuf(pState, pState->fGSO);
-                /* If we're out of bandwidth we'll come back later. */
-                if (RT_FAILURE(rc))
-                    goto out;
-                /* Copy the packet to allocated buffer and send it. */
-                rc = e1kXmitPacket(pState, fOnWorkerThread);
-                /* If we're out of bandwidth we'll come back later. */
-                if (RT_FAILURE(rc))
-                    goto out;
-            }
-            uint8_t u8Remain = pState->nTxDFetched - pState->iTxDCurrent;
-            if (u8Remain > 0)
-            {
-                /*
-                 * A packet was partially fetched. Move incomplete packet to
-                 * the beginning of cache buffer, then load more descriptors.
-                 */
-                memmove(pState->aTxDescriptors,
-                        &pState->aTxDescriptors[pState->iTxDCurrent],
-                        u8Remain * sizeof(E1KTXDESC));
-                pState->nTxDFetched = u8Remain;
-                e1kTxDLoadMore(pState);
-            }
-            else
-                pState->nTxDFetched = 0;
-            pState->iTxDCurrent = 0;
-        }
-        if (!pState->fLocked && GET_BITS(TXDCTL, LWTHRESH) == 0)
-        {
-            E1kLog2(("%s Out of transmit descriptors, raise ICR.TXD_LOW\n",
-                     INSTANCE(pState)));
-            e1kRaiseInterrupt(pState, VERR_SEM_BUSY, ICR_TXD_LOW);
-        }
+
+    /*
+     * Process all pending descriptors.
+     * Note! Do not process descriptors in locked state
+     */
+    STAM_PROFILE_ADV_START(&pState->CTX_SUFF_Z(StatTransmit), a);
+    while (!pState->fLocked && e1kTxDLazyLoad(pState))
+    {
+        while (e1kLocateTxPacket(pState))
+        {
+            /* Found a complete packet, allocate it. */
+            rc = e1kXmitAllocBuf(pState, pState->fGSO);
+            /* If we're out of bandwidth we'll come back later. */
+            if (RT_FAILURE(rc))
+                goto out;
+            /* Copy the packet to allocated buffer and send it. */
+            rc = e1kXmitPacket(pState, fOnWorkerThread);
+            /* If we're out of bandwidth we'll come back later. */
+            if (RT_FAILURE(rc))
+                goto out;
+        }
+        uint8_t u8Remain = pState->nTxDFetched - pState->iTxDCurrent;
+        if (u8Remain > 0)
+        {
+            /*
+             * A packet was partially fetched. Move incomplete packet to
+             * the beginning of cache buffer, then load more descriptors.
+             */
+            memmove(pState->aTxDescriptors,
+                    &pState->aTxDescriptors[pState->iTxDCurrent],
+                    u8Remain * sizeof(E1KTXDESC));
+            pState->nTxDFetched = u8Remain;
+            e1kTxDLoadMore(pState);
+        }
+        else
+            pState->nTxDFetched = 0;
+        pState->iTxDCurrent = 0;
+    }
+    if (!pState->fLocked && GET_BITS(TXDCTL, LWTHRESH) == 0)
+    {
+        E1kLog2(("%s Out of transmit descriptors, raise ICR.TXD_LOW\n",
+                 INSTANCE(pState)));
+        e1kRaiseInterrupt(pState, VERR_SEM_BUSY, ICR_TXD_LOW);
+    }
 
 out:
-        STAM_PROFILE_ADV_STOP(&pState->CTX_SUFF_Z(StatTransmit), a);
-
-        /// @todo: uncomment: pState->uStatIntTXQE++;
-        /// @todo: uncomment: e1kRaiseInterrupt(pState, ICR_TXQE);
-
-        /*
-         * Release the locks.
-         */
-        e1kMutexRelease(pState);
-    }
+    STAM_PROFILE_ADV_STOP(&pState->CTX_SUFF_Z(StatTransmit), a);
+
+    /// @todo: uncomment: pState->uStatIntTXQE++;
+    /// @todo: uncomment: e1kRaiseInterrupt(pState, ICR_TXQE);
+
+    /*
+     * Release the lock.
+     */
     if (pDrv)
         pDrv->pfnEndXmit(pDrv);
@@ -5122,9 +5042,5 @@
              * Mask out irrelevant bits.
              */
-#ifdef E1K_GLOBAL_MUTEX
-            rc = e1kMutexAcquire(pState, VINF_IOM_R3_MMIO_READ, RT_SRC_POS);
-#else
             //rc = e1kCsEnter(pState, VERR_SEM_BUSY, RT_SRC_POS);
-#endif
             if (RT_UNLIKELY(rc != VINF_SUCCESS))
                 return rc;
@@ -5135,5 +5051,4 @@
             u32 &= mask;
             //e1kCsLeave(pState);
-            e1kMutexRelease(pState);
             E1kLog2(("%s At %08X read  %s          from %s (%s)\n",
                     szInst, uOffset, e1kU32toHex(u32, mask, buf), s_e1kRegMap[index].abbrev, s_e1kRegMap[index].name));
@@ -5205,9 +5120,5 @@
             E1kLog2(("%s At %08X write          %08X  to  %s (%s)\n",
                      INSTANCE(pState), uOffset, u32, s_e1kRegMap[index].abbrev, s_e1kRegMap[index].name));
-#ifdef E1K_GLOBAL_MUTEX
-            rc = e1kMutexAcquire(pState, VINF_IOM_R3_MMIO_WRITE, RT_SRC_POS);
-#else
             //rc = e1kCsEnter(pState, VERR_SEM_BUSY, RT_SRC_POS);
-#endif
             if (RT_UNLIKELY(rc != VINF_SUCCESS))
                 return rc;
@@ -5217,5 +5128,4 @@
             rc = s_e1kRegMap[index].pfnWrite(pState, uOffset, index, u32);
             //e1kCsLeave(pState);
-            e1kMutexRelease(pState);
         }
         else
@@ -5523,6 +5433,4 @@
     size_t cb;
 
-    if (RT_UNLIKELY(e1kMutexAcquire(pState, VERR_SEM_BUSY, RT_SRC_POS) != VINF_SUCCESS))
-        return VERR_NET_NO_BUFFER_SPACE;
     if (RT_UNLIKELY(e1kCsRxEnter(pState, VERR_SEM_BUSY) != VINF_SUCCESS))
         return VERR_NET_NO_BUFFER_SPACE;
@@ -5551,5 +5459,4 @@
 
     e1kCsRxLeave(pState);
-    e1kMutexRelease(pState);
     return cb > 0 ? VINF_SUCCESS : VERR_NET_NO_BUFFER_SPACE;
 }
@@ -5801,31 +5708,27 @@
 
     STAM_PROFILE_ADV_START(&pState->StatReceive, a);
-    rc = e1kMutexAcquire(pState, VERR_SEM_BUSY, RT_SRC_POS);
-    if (RT_LIKELY(rc == VINF_SUCCESS))
-    {
-        //if (!e1kCsEnter(pState, RT_SRC_POS))
-        //    return VERR_PERMISSION_DENIED;
-
-        e1kPacketDump(pState, (const uint8_t*)pvBuf, cb, "<-- Incoming");
-
-        /* Update stats */
-        if (RT_LIKELY(e1kCsEnter(pState, VERR_SEM_BUSY) == VINF_SUCCESS))
-        {
-            E1K_INC_CNT32(TPR);
-            E1K_ADD_CNT64(TORL, TORH, cb < 64? 64 : cb);
-            e1kCsLeave(pState);
-        }
-        STAM_PROFILE_ADV_START(&pState->StatReceiveFilter, a);
-        E1KRXDST status;
-        RT_ZERO(status);
-        bool fPassed = e1kAddressFilter(pState, pvBuf, cb, &status);
-        STAM_PROFILE_ADV_STOP(&pState->StatReceiveFilter, a);
-        if (fPassed)
-        {
-            rc = e1kHandleRxPacket(pState, pvBuf, cb, status);
-        }
-        //e1kCsLeave(pState);
-        e1kMutexRelease(pState);
-    }
+
+    //if (!e1kCsEnter(pState, RT_SRC_POS))
+    //    return VERR_PERMISSION_DENIED;
+
+    e1kPacketDump(pState, (const uint8_t*)pvBuf, cb, "<-- Incoming");
+
+    /* Update stats */
+    if (RT_LIKELY(e1kCsEnter(pState, VERR_SEM_BUSY) == VINF_SUCCESS))
+    {
+        E1K_INC_CNT32(TPR);
+        E1K_ADD_CNT64(TORL, TORH, cb < 64? 64 : cb);
+        e1kCsLeave(pState);
+    }
+    STAM_PROFILE_ADV_START(&pState->StatReceiveFilter, a);
+    E1KRXDST status;
+    RT_ZERO(status);
+    bool fPassed = e1kAddressFilter(pState, pvBuf, cb, &status);
+    STAM_PROFILE_ADV_STOP(&pState->StatReceiveFilter, a);
+    if (fPassed)
+    {
+        rc = e1kHandleRxPacket(pState, pvBuf, cb, status);
+    }
+    //e1kCsLeave(pState);
     STAM_PROFILE_ADV_STOP(&pState->StatReceive, a);
 
@@ -5988,7 +5891,4 @@
     return VINF_SUCCESS;
 #if 0
-    int rc = e1kMutexAcquire(pState, VERR_SEM_BUSY, RT_SRC_POS);
-    if (RT_UNLIKELY(rc != VINF_SUCCESS))
-        return rc;
     /* 1) Prevent all threads from modifying the state and memory */
     //pState->fLocked = true;
@@ -6007,5 +5907,4 @@
     /* 3) Did I forget anything? */
     E1kLog(("%s Locked\n", INSTANCE(pState)));
-    e1kMutexRelease(pState);
     return VINF_SUCCESS;
 #endif
@@ -6070,7 +5969,4 @@
     E1KSTATE* pState = PDMINS_2_DATA(pDevIns, E1KSTATE*);
 
-    int rc = e1kMutexAcquire(pState, VERR_SEM_BUSY, RT_SRC_POS);
-    if (RT_UNLIKELY(rc != VINF_SUCCESS))
-        return rc;
     /* If VM is being powered off unlocking will result in assertions in PGM */
     if (PDMDevHlpGetVM(pDevIns)->enmVMState == VMSTATE_RUNNING)
@@ -6079,5 +5975,4 @@
         E1kLog(("%s VM is not running -- remain locked\n", INSTANCE(pState)));
     E1kLog(("%s Unlocked\n", INSTANCE(pState)));
-    e1kMutexRelease(pState);
     return VINF_SUCCESS;
 }
@@ -6206,8 +6101,4 @@
     E1KSTATE* pState = PDMINS_2_DATA(pDevIns, E1KSTATE*);
 
-    int rc = e1kMutexAcquire(pState, VERR_SEM_BUSY, RT_SRC_POS);
-    if (RT_UNLIKELY(rc != VINF_SUCCESS))
-        return rc;
-
     /* Update promiscuous mode */
     if (pState->pDrvR3)
@@ -6230,5 +6121,4 @@
         e1kArmTimer(pState, pState->pLUTimerR3, 5000000);
     }
-    e1kMutexRelease(pState);
     return VINF_SUCCESS;
 }
@@ -6449,8 +6339,6 @@
             pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
         }
-#ifndef E1K_GLOBAL_MUTEX
         PDMR3CritSectDelete(&pState->csRx);
         //PDMR3CritSectDelete(&pState->csTx);
-#endif
         PDMR3CritSectDelete(&pState->cs);
     }
@@ -6794,9 +6682,7 @@
     if (RT_FAILURE(rc))
         return rc;
-#ifndef E1K_GLOBAL_MUTEX
     rc = PDMDevHlpCritSectInit(pDevIns, &pState->csRx, RT_SRC_POS, "%sRX", pState->szInstance);
     if (RT_FAILURE(rc))
         return rc;
-#endif
 
     /* Set PCI config registers */
@@ -6809,8 +6695,8 @@
 #ifdef E1K_WITH_MSI
     PDMMSIREG aMsiReg;
-    aMsiReg.cVectors = 1;
-    aMsiReg.iCapOffset = 0x80;
-    aMsiReg.iNextOffset = 0x0;
-    aMsiReg.iMsiFlags = 0;
+    aMsiReg.cMsiVectors = 1;
+    aMsiReg.iMsiCapOffset = 0x80;
+    aMsiReg.iMsiNextOffset = 0x0;
+    aMsiReg.fMsi64bit = false;
     rc = PDMDevHlpPCIRegisterMsi(pDevIns, &aMsiReg);
     AssertRC(rc);
@@ -6949,5 +6835,5 @@
     e1kHardReset(pState);
 
-#if defined(VBOX_WITH_STATISTICS) || defined(E1K_REL_STATS)
+#if defined(VBOX_WITH_STATISTICS)
     PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatMMIOReadRZ,         STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in RZ",         "/Devices/E1k%d/MMIO/ReadRZ", iInstance);
     PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatMMIOReadR3,         STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in R3",         "/Devices/E1k%d/MMIO/ReadR3", iInstance);
@@ -6970,12 +6856,12 @@
     PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflow,         STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows",        "/Devices/E1k%d/RxOverflow", iInstance);
     PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflowWakeup,   STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,     "Nr of RX overflow wakeups",          "/Devices/E1k%d/RxOverflowWakeup", iInstance);
-#endif /* VBOX_WITH_STATISTICS || E1K_REL_STATS */
+#endif /* VBOX_WITH_STATISTICS */
     PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveBytes,       STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,          "Amount of data received",            "/Devices/E1k%d/ReceiveBytes", iInstance);
-#if defined(VBOX_WITH_STATISTICS) || defined(E1K_REL_STATS)
+#if defined(VBOX_WITH_STATISTICS)
     PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitRZ,         STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in RZ",          "/Devices/E1k%d/Transmit/TotalRZ", iInstance);
     PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitR3,         STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in R3",          "/Devices/E1k%d/Transmit/TotalR3", iInstance);
-#endif /* VBOX_WITH_STATISTICS || E1K_REL_STATS */
+#endif /* VBOX_WITH_STATISTICS */
     PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitBytes,      STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,          "Amount of data transmitted",         "/Devices/E1k%d/TransmitBytes", iInstance);
-#if defined(VBOX_WITH_STATISTICS) || defined(E1K_REL_STATS)
+#if defined(VBOX_WITH_STATISTICS)
     PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitSendRZ,     STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in RZ",      "/Devices/E1k%d/Transmit/SendRZ", iInstance);
     PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitSendR3,     STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in R3",      "/Devices/E1k%d/Transmit/SendR3", iInstance);
@@ -6990,5 +6876,5 @@
     PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTxPathRegular,      STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,     "Regular descriptor path",            "/Devices/E1k%d/TxPath/Normal", iInstance);
     PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatPHYAccesses,        STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,     "Number of PHY accesses",             "/Devices/E1k%d/PHYAccesses", iInstance);
-#endif /* VBOX_WITH_STATISTICS || E1K_REL_STATS */
+#endif /* VBOX_WITH_STATISTICS */
 
     return VINF_SUCCESS;
