Index: trunk/src/VBox/Devices/Audio/DevHda.cpp
===================================================================
--- trunk/src/VBox/Devices/Audio/DevHda.cpp	(revision 89860)
+++ trunk/src/VBox/Devices/Audio/DevHda.cpp	(revision 89861)
@@ -1035,10 +1035,98 @@
 {
     RT_NOREF(pDevIns);
-
-    const uint8_t  uSD     = HDA_SD_NUM_FROM_REG(pThis, LPIB, iReg);
-    const uint32_t u32LPIB = HDA_STREAM_REG(pThis, LPIB, uSD);
-    *pu32Value = u32LPIB;
-    LogFlowFunc(("[SD%RU8] LPIB=%RU32, CBL=%RU32\n", uSD, u32LPIB, HDA_STREAM_REG(pThis, CBL, uSD)));
-
+    uint8_t const  uSD   = HDA_SD_NUM_FROM_REG(pThis, LPIB, iReg);
+    uint32_t const uLPIB = HDA_STREAM_REG(pThis, LPIB, uSD);
+
+#ifdef VBOX_HDA_WITH_ON_REG_ACCESS_DMA
+    /*
+     * Should we consider doing DMA work while we're here?  That would require
+     * the stream to have the DMA engine enabled and be an output stream.
+     */
+    if (   (HDA_STREAM_REG(pThis, CTL, uSD) & HDA_SDCTL_RUN)
+        && hdaGetDirFromSD(uSD) == PDMAUDIODIR_OUT
+        && uSD < RT_ELEMENTS(pThis->aStreams) /* paranoia */)
+    {
+        PHDASTREAM const pStreamShared = &pThis->aStreams[uSD];
+        Assert(pStreamShared->u8SD == uSD);
+        if (pStreamShared->State.fRunning /* should be same as HDA_SDCTL_RUN, but doesn't hurt to check twice */)
+        {
+            /*
+             * Calculate where the DMA engine should be according to the clock, if we can.
+             */
+            uint32_t const idxSched = pStreamShared->State.idxSchedule;
+            if (idxSched < RT_MIN(RT_ELEMENTS(pStreamShared->State.aSchedule), pStreamShared->State.cSchedule))
+            {
+                uint32_t const cbPeriod = pStreamShared->State.aSchedule[idxSched].cbPeriod;
+                uint32_t const cbFrame  = PDMAudioPropsFrameSize(&pStreamShared->State.Cfg.Props);
+                if (cbPeriod > cbFrame)
+                {
+                    AssertMsg(pStreamShared->State.cbDmaTotal < cbPeriod, ("%#x vs %#x\n", pStreamShared->State.cbDmaTotal, cbPeriod));
+                    uint64_t const tsTransferNext = pStreamShared->State.tsTransferNext;
+                    uint64_t const tsNow          = PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer);
+                    uint32_t       cbFuture;
+                    if (tsNow < tsTransferNext)
+                    {
+                        /** @todo ASSUMES nanosecond clock ticks, need to make this
+                         *        resolution independent. */
+                        cbFuture = PDMAudioPropsNanoToBytes(&pStreamShared->State.Cfg.Props, tsTransferNext - tsNow);
+                        cbFuture = RT_MIN(cbFuture, cbPeriod - cbFrame);
+                    }
+                    else
+                    {
+                        /* We've hit/overshot the timer deadline.  Return to ring-3 if we're
+                           not already there to increase the chance that we'll help expidite
+                           the timer.  If we're already in ring-3, do all but the last frame. */
+# ifndef IN_RING3
+                        LogFunc(("[SD%RU8] DMA period expired: tsNow=%RU64 >= tsTransferNext=%RU64 -> VINF_IOM_R3_MMIO_READ\n",
+                                 tsNow, tsTransferNext));
+                        return VINF_IOM_R3_MMIO_READ;
+# else
+                        cbFuture = cbPeriod - cbFrame;
+                        LogFunc(("[SD%RU8] DMA period expired: tsNow=%RU64 >= tsTransferNext=%RU64 -> cbFuture=%#x (cbPeriod=%#x - cbFrame=%#x)\n",
+                                 tsNow, tsTransferNext, cbFuture, cbPeriod, cbFrame));
+# endif
+                    }
+                    uint32_t const offNow = PDMAudioPropsFloorBytesToFrame(&pStreamShared->State.Cfg.Props, cbPeriod - cbFuture);
+
+                    /*
+                     * Should we transfer a little?  Minimum is 64 bytes (semi-random,
+                     * suspect real hardware might be doing some cache aligned stuff,
+                     * which might soon get complicated if you take unaligned buffers
+                     * into consideration and which cache line size (128 bytes is just
+                     * as likely as 64 or 32 bytes)).
+                     */
+                    uint32_t cbDmaTotal = pStreamShared->State.cbDmaTotal;
+                    if (cbDmaTotal + 64 <= offNow)
+                    {
+                        VBOXSTRICTRC rcStrict = hdaStreamDoOnAccessDmaOutput(pDevIns, pThis, pStreamShared, offNow - cbDmaTotal);
+
+                        /* LPIB is updated by hdaStreamDoOnAccessDmaOutput, so get the new value. */
+                        uint32_t const uNewLpib = HDA_STREAM_REG(pThis, LPIB, uSD);
+                        *pu32Value = uNewLpib;
+
+                        LogFlowFunc(("[SD%RU8] LPIB=%#RX32 (CBL=%#RX32 PrevLPIB=%#x offNow=%#x) rcStrict=%Rrc\n", uSD,
+                                     uNewLpib, HDA_STREAM_REG(pThis, CBL, uSD), uLPIB, offNow, VBOXSTRICTRC_VAL(rcStrict) ));
+                        return rcStrict;
+                    }
+
+                    /*
+                     * Do nothing, just return LPIB as it is.
+                     */
+                    LogFlowFunc(("[SD%RU8] Skipping DMA transfer: cbDmaTotal=%#x offNow=%#x\n", uSD, cbDmaTotal, offNow));
+                }
+                else
+                    LogFunc(("[SD%RU8] cbPeriod=%#x <= cbFrame=%#x!!\n", uSD, cbPeriod, cbFrame));
+            }
+            else
+                LogFunc(("[SD%RU8] idxSched=%u cSchedule=%u!!\n", uSD, idxSched, pStreamShared->State.cSchedule));
+        }
+        else
+            LogFunc(("[SD%RU8] fRunning=0 SDnCTL=%#x!!\n", uSD, HDA_STREAM_REG(pThis, CTL, uSD) ));
+    }
+#endif /* VBOX_HDA_WITH_ON_REG_ACCESS_DMA */
+
+    LogFlowFunc(("[SD%RU8] LPIB=%#RX32 (CBL=%#RX32 CTL=%#RX32)\n",
+                 uSD, uLPIB, HDA_STREAM_REG(pThis, CBL, uSD), HDA_STREAM_REG(pThis, CTL, uSD) ));
+    *pu32Value = uLPIB;
     return VINF_SUCCESS;
 }
@@ -3983,5 +4071,5 @@
                 rc = pHlp->pfnSSMGetMem(pSSM, pvBuf, cbBuf);
                 AssertRCReturn(rc, rc);
-                pStreamR3->State.offWrite = cbCircBufUsed;
+                pStreamShared->State.offWrite = cbCircBufUsed;
 
                 RTCircBufReleaseWriteBlock(pStreamR3->State.pCircBuf, cbBuf);
@@ -4965,8 +5053,6 @@
     for (size_t i = 0; i < HDA_MAX_STREAMS; i++)
     {
-        /* We need the first timer in ring-0 to calculate the wall clock (WALCLK) time. */
         rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, hdaR3Timer, (void *)(uintptr_t)i,
-                                  TMTIMER_FLAGS_NO_CRIT_SECT | (i == 0 ? TMTIMER_FLAGS_RING0 : TMTIMER_FLAGS_NO_RING0),
-                                  s_apszNames[i], &pThis->aStreams[i].hTimer);
+                                  TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, s_apszNames[i], &pThis->aStreams[i].hTimer);
         AssertRCReturn(rc, rc);
 
@@ -5062,6 +5148,10 @@
     PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIn,               STAMTYPE_PROFILE, "Input",             STAMUNIT_TICKS_PER_CALL, "Profiling input.");
     PDMDevHlpSTAMRegister(pDevIns, &pThis->StatOut,              STAMTYPE_PROFILE, "Output",            STAMUNIT_TICKS_PER_CALL, "Profiling output.");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesRead,        STAMTYPE_COUNTER, "BytesRead"   ,      STAMUNIT_BYTES,          "Bytes read from HDA emulation.");
-    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesWritten,     STAMTYPE_COUNTER, "BytesWritten",      STAMUNIT_BYTES,          "Bytes written to HDA emulation.");
+    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesRead,        STAMTYPE_COUNTER, "BytesRead"   ,      STAMUNIT_BYTES,          "Bytes read (DMA) from the guest.");
+    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesWritten,     STAMTYPE_COUNTER, "BytesWritten",      STAMUNIT_BYTES,          "Bytes written (DMA) to the guest.");
+#  ifdef VBOX_HDA_WITH_ON_REG_ACCESS_DMA
+    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatAccessDmaOutput,  STAMTYPE_COUNTER, "AccessDmaOutput",   STAMUNIT_COUNT,          "Number of on-register-access DMA sub-transfers we've made.");
+    PDMDevHlpSTAMRegister(pDevIns, &pThis->StatAccessDmaOutputToR3,STAMTYPE_COUNTER, "AccessDmaOutputToR3", STAMUNIT_COUNT,      "Number of time the on-register-access DMA forced a ring-3 return.");
+#  endif
 
     AssertCompile(RT_ELEMENTS(g_aHdaRegMap)              == HDA_NUM_REGS);
@@ -5110,7 +5200,7 @@
                                "DMA transfer period skipped because of BCIS pending.", "Stream%u/DMASkippedPendingBCIS", idxStream);
 
-        PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.offRead, STAMTYPE_U64, STAMVISIBILITY_USED, STAMUNIT_BYTES,
+        PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].State.offRead, STAMTYPE_U64, STAMVISIBILITY_USED, STAMUNIT_BYTES,
                                "Virtual internal buffer read position.",    "Stream%u/offRead", idxStream);
-        PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.offWrite, STAMTYPE_U64, STAMVISIBILITY_USED, STAMUNIT_BYTES,
+        PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].State.offWrite, STAMTYPE_U64, STAMVISIBILITY_USED, STAMUNIT_BYTES,
                                "Virtual internal buffer write position.",   "Stream%u/offWrite", idxStream);
         PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].State.cbTransferSize, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,
Index: trunk/src/VBox/Devices/Audio/DevHda.h
===================================================================
--- trunk/src/VBox/Devices/Audio/DevHda.h	(revision 89860)
+++ trunk/src/VBox/Devices/Audio/DevHda.h	(revision 89861)
@@ -146,4 +146,8 @@
     HDACODEC                Codec;
 
+#ifdef VBOX_HDA_WITH_ON_REG_ACCESS_DMA
+    STAMCOUNTER             StatAccessDmaOutput;
+    STAMCOUNTER             StatAccessDmaOutputToR3;
+#endif
 #ifdef VBOX_WITH_STATISTICS
     STAMPROFILE             StatIn;
Index: trunk/src/VBox/Devices/Audio/DevHdaStream.cpp
===================================================================
--- trunk/src/VBox/Devices/Audio/DevHdaStream.cpp	(revision 89860)
+++ trunk/src/VBox/Devices/Audio/DevHdaStream.cpp	(revision 89861)
@@ -40,15 +40,22 @@
 #endif
 
-#ifdef IN_RING3 /* whole file */
-
 
 /*********************************************************************************************************************************
 *   Internal Functions                                                                                                           *
 *********************************************************************************************************************************/
-static void hdaR3StreamSetPositionAbs(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t uLPIB);
-static void hdaR3StreamUpdateDma(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC,
-                                 PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3);
-
-
+static void     hdaStreamSetPositionAbs(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t uLPIB);
+#ifdef IN_RING3
+# ifdef VBOX_HDA_WITH_ON_REG_ACCESS_DMA
+static void     hdaR3StreamFlushDmaBounceBufferOutput(PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3);
+# endif
+static uint32_t hdaR3StreamHandleDmaBufferOverrun(PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, PAUDMIXSINK pSink,
+                                                  uint32_t cbNeeded, uint64_t nsNow,
+                                                  const char *pszCaller, uint32_t const cbStreamFree);
+static void     hdaR3StreamUpdateDma(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC,
+                                     PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3);
+#endif
+
+
+#ifdef IN_RING3
 
 /**
@@ -687,7 +694,6 @@
 
     /* Set I/O scheduling hint for the backends. */
-    /** @todo r=bird: This is in the 'Device' portion, yet it's used by the
-     *        audio driver.  You would think stuff in the 'Device' part is
-     *        private to the device. */
+    /** @todo r=bird: derive this from the schedule instead of using the
+     *        uTimerIoHz, as that's almost pure non-sense now. */
     pCfg->Device.cMsSchedulingHint = RT_MS_1SEC / pStreamShared->State.uTimerIoHz;
     LogRel2(("HDA: Stream #%RU8 set scheduling hint for the backends to %RU32ms\n", uSD, pCfg->Device.cMsSchedulingHint));
@@ -695,5 +701,9 @@
 
     /* Make sure to also update the stream's DMA counter (based on its current LPIB value). */
-    hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis, HDA_STREAM_REG(pThis, LPIB, uSD));
+    /** @todo r=bird: We use LPIB as-is here, so if it's not zero we have to
+     *        locate the right place in the schedule and whatnot... */
+    if (HDA_STREAM_REG(pThis, LPIB, uSD) != 0)
+        LogRel2(("HDA: Warning! Stream #%RU8 is set up with LPIB=%#RX32 instead of zero!\n", uSD, HDA_STREAM_REG(pThis, LPIB, uSD)));
+    hdaStreamSetPositionAbs(pStreamShared, pDevIns, pThis, HDA_STREAM_REG(pThis, LPIB, uSD));
 
 # ifdef LOG_ENABLED
@@ -714,6 +724,6 @@
         pStreamR3->State.StatDmaBufUsed = 0;
     }
-    pStreamR3->State.offWrite = 0;
-    pStreamR3->State.offRead  = 0;
+    pStreamShared->State.offWrite = 0;
+    pStreamShared->State.offRead  = 0;
 
     /*
@@ -878,6 +888,6 @@
     if (pStreamR3->State.pCircBuf)
         RTCircBufReset(pStreamR3->State.pCircBuf);
-    pStreamR3->State.offWrite = 0;
-    pStreamR3->State.offRead  = 0;
+    pStreamShared->State.offWrite = 0;
+    pStreamShared->State.offRead  = 0;
 
 # ifdef DEBUG
@@ -1023,11 +1033,5 @@
 }
 
-
-# if 0 /* Not used atm. */
-static uint32_t hdaR3StreamGetPosition(PHDASTATE pThis, PHDASTREAM pStreamShared)
-{
-    return HDA_STREAM_REG(pThis, LPIB, pStreamShared->u8SD);
-}
-# endif
+#endif /* IN_RING3 */
 
 /**
@@ -1040,5 +1044,5 @@
  * @param   uLPIB               Absolute position (in bytes) to set current read / write position to.
  */
-static void hdaR3StreamSetPositionAbs(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t uLPIB)
+static void hdaStreamSetPositionAbs(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t uLPIB)
 {
     AssertPtrReturnVoid(pStreamShared);
@@ -1053,4 +1057,17 @@
     if (pThis->fDMAPosition)
     {
+        /*
+         * Linux switched to using the position buffers some time during 2.6.x.
+         * 2.6.12 used LPIB, 2.6.17 defaulted to DMA position buffers, between
+         * the two version things were being changing quite a bit.
+         *
+         * Since 2.6.17, they will treat a zero DMA position value during the first
+         * period/IRQ as reason to fall back to LPIB mode (see azx_position_ok in
+         * 2.6.27+, and azx_pcm_pointer before that).  They later also added
+         * UINT32_MAX to the values causing same.
+         *
+         * Since 2.6.35 azx_position_ok will read the wall clock register before
+         * determining the position.
+         */
         int rc2 = PDMDevHlpPCIPhysWrite(pDevIns,
                                         pThis->u64DPBase + (pStreamShared->u8SD * 2 * sizeof(uint32_t)),
@@ -1071,5 +1088,5 @@
  * @param   cbToAdd             Position (in bytes) to add to the current read / write position.
  */
-static void hdaR3StreamSetPositionAdd(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t cbToAdd)
+static void hdaStreamSetPositionAdd(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t cbToAdd)
 {
     if (cbToAdd) /* No need to update anything if 0. */
@@ -1086,8 +1103,10 @@
                 uNewLpid %= uCBL;
 #endif
-            hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis, uNewLpid);
+            hdaStreamSetPositionAbs(pStreamShared, pDevIns, pThis, uNewLpid);
         }
     }
 }
+
+#ifdef IN_RING3
 
 /**
@@ -1121,4 +1140,6 @@
 }
 
+#endif /* IN_RING3 */
+
 /**
  * Get the current address and number of bytes left in the current BDLE.
@@ -1128,5 +1149,5 @@
  * @param   pcbLeft         The number of bytes left at the returned address.
  */
-DECLINLINE(RTGCPHYS) hdaR3StreamDmaBufGet(PHDASTREAM pStreamShared, uint32_t *pcbLeft)
+DECLINLINE(RTGCPHYS) hdaStreamDmaBufGet(PHDASTREAM pStreamShared, uint32_t *pcbLeft)
 {
     uint8_t        idxBdle    = pStreamShared->State.idxCurBdle;
@@ -1147,5 +1168,5 @@
  * @param   pStreamShared   The stream to check.
  */
-DECLINLINE(RTGCPHYS) hdaR3StreamDmaBufGetSize(PHDASTREAM pStreamShared)
+DECLINLINE(RTGCPHYS) hdaStreamDmaBufGetSize(PHDASTREAM pStreamShared)
 {
     uint8_t idxBdle = pStreamShared->State.idxCurBdle;
@@ -1161,5 +1182,5 @@
  * @param   pStreamShared   The stream to check.
  */
-DECLINLINE(bool) hdaR3StreamDmaBufIsComplete(PHDASTREAM pStreamShared)
+DECLINLINE(bool) hdaStreamDmaBufIsComplete(PHDASTREAM pStreamShared)
 {
     uint8_t const  idxBdle    = pStreamShared->State.idxCurBdle;
@@ -1179,5 +1200,5 @@
  * @param   pStreamShared   The stream to check.
  */
-DECLINLINE(bool) hdaR3StreamDmaBufNeedsIrq(PHDASTREAM pStreamShared)
+DECLINLINE(bool) hdaStreamDmaBufNeedsIrq(PHDASTREAM pStreamShared)
 {
     uint8_t const idxBdle = pStreamShared->State.idxCurBdle;
@@ -1191,5 +1212,5 @@
  * @param   pStreamShared   The stream which DMA engine is to be updated.
  */
-DECLINLINE(void) hdaR3StreamDmaBufAdvanceToNext(PHDASTREAM pStreamShared)
+DECLINLINE(void) hdaStreamDmaBufAdvanceToNext(PHDASTREAM pStreamShared)
 {
     uint8_t idxBdle = pStreamShared->State.idxCurBdle;
@@ -1203,4 +1224,6 @@
     pStreamShared->State.offCurBdle = 0;
 }
+
+#ifdef IN_RING3
 
 /**
@@ -1307,7 +1330,11 @@
 }
 
+#endif /* IN_RING3 */
+
 /**
  * Completes a BDLE at the end of a DMA loop iteration, if possible.
  *
+ * @retval  true if buffer completed and new loaded.
+ * @retval  false if buffer not completed.
  * @param   pDevIns         The device instance.
  * @param   pThis           The shared HDA device state.
@@ -1315,6 +1342,6 @@
  * @param   pszFunction     The function name (for logging).
  */
-DECLINLINE(void) hdaR3StreamDoDmaMaybeCompleteBuffer(PPDMDEVINS pDevIns, PHDASTATE pThis,
-                                                     PHDASTREAM pStreamShared, const char *pszFunction)
+DECLINLINE(bool) hdaStreamDoDmaMaybeCompleteBuffer(PPDMDEVINS pDevIns, PHDASTATE pThis,
+                                                   PHDASTREAM pStreamShared, const char *pszFunction)
 {
     RT_NOREF(pszFunction);
@@ -1323,5 +1350,5 @@
      * Is the buffer descriptor complete.
      */
-    if (hdaR3StreamDmaBufIsComplete(pStreamShared))
+    if (hdaStreamDmaBufIsComplete(pStreamShared))
     {
         Log3(("%s: [SD%RU8] Completed BDLE%u %#RX64 LB %#RX32 fFlags=%#x\n", pszFunction, pStreamShared->u8SD,
@@ -1330,4 +1357,5 @@
               pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].fFlags));
 
+#if 0 /* Moved to the transfer loops */
         /*
          * Update the stream's current position.
@@ -1339,8 +1367,9 @@
          *                      Not doing this at the right time will result in ugly sound crackles!
          */
-        hdaR3StreamSetPositionAdd(pStreamShared, pDevIns, pThis, hdaR3StreamDmaBufGetSize(pStreamShared));
+        hdaStreamSetPositionAdd(pStreamShared, pDevIns, pThis, hdaStreamDmaBufGetSize(pStreamShared));
+#endif
 
         /* Does the current BDLE require an interrupt to be sent? */
-        if (hdaR3StreamDmaBufNeedsIrq(pStreamShared))
+        if (hdaStreamDmaBufNeedsIrq(pStreamShared))
         {
             /* If the IOCE ("Interrupt On Completion Enable") bit of the SDCTL
@@ -1363,12 +1392,16 @@
          * Advance to the next BDLE.
          */
-        hdaR3StreamDmaBufAdvanceToNext(pStreamShared);
-    }
-    else
-        Log3(("%s: [SD%RU8] Not completed BDLE%u %#RX64 LB %#RX32 fFlags=%#x: off=%#RX32\n", pszFunction, pStreamShared->u8SD,
-              pStreamShared->State.idxCurBdle, pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].GCPhys,
-              pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].cb,
-              pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].fFlags, pStreamShared->State.offCurBdle));
-}
+        hdaStreamDmaBufAdvanceToNext(pStreamShared);
+        return true;
+    }
+
+    Log3(("%s: [SD%RU8] Incomplete BDLE%u %#RX64 LB %#RX32 fFlags=%#x: off=%#RX32\n", pszFunction, pStreamShared->u8SD,
+          pStreamShared->State.idxCurBdle, pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].GCPhys,
+          pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].cb,
+          pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].fFlags, pStreamShared->State.offCurBdle));
+    return false;
+}
+
+#ifdef IN_RING3
 
 /**
@@ -1396,5 +1429,5 @@
  */
 static void hdaR3StreamDoDmaInput(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared,
-                                  PHDASTREAMR3 pStreamR3, uint32_t cbToConsume, bool fWriteSilence, uint64_t tsNowNs)
+                                  PHDASTREAMR3 pStreamR3, uint32_t const cbToConsume, bool fWriteSilence, uint64_t tsNowNs)
 {
     uint8_t const uSD = pStreamShared->u8SD;
@@ -1429,7 +1462,8 @@
          */
         uint32_t cbChunk = 0;
-        RTGCPHYS GCPhys  = hdaR3StreamDmaBufGet(pStreamShared, &cbChunk);
+        RTGCPHYS GCPhys  = hdaStreamDmaBufGet(pStreamShared, &cbChunk);
 
         /* If we're writing silence.  */
+        uint32_t cbWritten = 0;
         if (!fWriteSilence)
         {
@@ -1462,14 +1496,14 @@
 
 # ifdef VBOX_WITH_DTRACE
-                VBOXDD_HDA_STREAM_DMA_IN((uint32_t)uSD, (uint32_t)cbBufSrc, pStreamR3->State.offRead);
+                VBOXDD_HDA_STREAM_DMA_IN((uint32_t)uSD, (uint32_t)cbBufSrc, pStreamShared->State.offRead);
 # endif
-                pStreamR3->State.offRead += cbBufSrc;
+                pStreamShared->State.offRead += cbBufSrc;
                 RTCircBufReleaseReadBlock(pCircBuf, cbBufSrc);
-                STAM_COUNTER_ADD(&pThis->StatBytesRead, cbBufSrc);
+                STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbBufSrc);
 
                 /* advance */
                 cbChunk                         -= (uint32_t)cbBufSrc;
+                cbWritten                       += (uint32_t)cbBufSrc;
                 GCPhys                          +=           cbBufSrc;
-                cbLeft                          -= (uint32_t)cbBufSrc;
                 pStreamShared->State.offCurBdle += (uint32_t)cbBufSrc;
             }
@@ -1508,5 +1542,4 @@
                 uint32_t cFrames = PDMAudioPropsBytesToFrames(pGuestProps, RT_MIN(cbGuest, sizeof(abBounce) - cbBounce));
 
-                size_t cbBufSrc = PDMAudioPropsFramesToBytes(&pStreamShared->State.Cfg.Props, cFrames);
                 cbGuest  = PDMAudioPropsFramesToBytes(pGuestProps, cFrames);
                 PDMAudioPropsClearBuffer(pGuestProps, &abBounce[cbBounce], cbGuest, cFrames);
@@ -1517,8 +1550,8 @@
                 int rc2 = PDMDevHlpPCIPhysWrite(pDevIns, GCPhys, abBounce, cbGuestActual);
                 AssertRC(rc2);
-                STAM_COUNTER_ADD(&pThis->StatBytesRead, cbGuestActual);
+                STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbGuestActual);
 
                 /* advance */
-                cbLeft                          -= (uint32_t)cbBufSrc;
+                cbWritten                       += cbGuestActual;
                 cbChunk                         -= cbGuestActual;
                 GCPhys                          += cbGuestActual;
@@ -1531,13 +1564,21 @@
                 Log5Func((" loop1: GCPhys=%RGp cbGuestActual=%#x cbBounce=%#x cFrames=%#x\n", GCPhys, cbGuestActual, cbBounce, cFrames));
             }
-            Log5Func(("loop0: GCPhys=%RGp cbBounce=%#x cbLeft=%#x\n", GCPhys, cbBounce, cbLeft));
+            Log5Func(("loop0: GCPhys=%RGp cbBounce=%#x cbLeft=%#x\n", GCPhys, cbBounce, cbLeft - cbWritten));
         }
 
+        cbLeft -= cbWritten;
         STAM_PROFILE_STOP(&pThis->StatIn, a);
 
         /*
          * Complete the buffer if necessary (common with the output DMA code).
+         *
+         * Must update the DMA position before we do this as the buffer IRQ may
+         * fire on another vCPU and run in parallel to us, although it is very
+         * unlikely it can make much progress as long as we're sitting on the
+         * lock, it could still read the DMA position (Linux won't, as it reads
+         * WALCLK and possibly SDnSTS before the DMA position).
          */
-        hdaR3StreamDoDmaMaybeCompleteBuffer(pDevIns, pThis, pStreamShared, "hdaR3StreamDoDmaInput");
+        hdaStreamSetPositionAdd(pStreamShared, pDevIns, pThis, cbWritten);
+        hdaStreamDoDmaMaybeCompleteBuffer(pDevIns, pThis, pStreamShared, "hdaR3StreamDoDmaInput");
     }
 
@@ -1554,5 +1595,5 @@
      */
     Log3Func(("LEAVE - [SD%RU8] %#RX32/%#RX32 @ %#RX64 - cTransferPendingInterrupts=%RU8\n",
-              uSD, cbToConsume, pStreamShared->State.cbTransferSize, pStreamR3->State.offRead - cbToConsume,
+              uSD, cbToConsume, pStreamShared->State.cbTransferSize, pStreamShared->State.offRead - cbToConsume,
               pStreamShared->State.cTransferPendingInterrupts));
 }
@@ -1563,21 +1604,22 @@
  * buffer.
  *
+ * @param   pStreamShared   HDA stream to update (shared).
  * @param   pStreamR3       HDA stream to update (ring-3 bits).
  * @param   pSink           The mixer sink to pull from.
  */
-static void hdaR3StreamPullFromMixer(PHDASTREAMR3 pStreamR3, PAUDMIXSINK pSink)
+static void hdaR3StreamPullFromMixer(PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, PAUDMIXSINK pSink)
 {
 # ifdef LOG_ENABLED
-    uint64_t const offWriteOld = pStreamR3->State.offWrite;
+    uint64_t const offWriteOld = pStreamShared->State.offWrite;
 # endif
-    pStreamR3->State.offWrite = AudioMixerSinkTransferToCircBuf(pSink,
-                                                                pStreamR3->State.pCircBuf,
-                                                                pStreamR3->State.offWrite,
-                                                                pStreamR3->u8SD,
-                                                                pStreamR3->Dbg.Runtime.fEnabled
-                                                                ? pStreamR3->Dbg.Runtime.pFileStream : NULL);
+    pStreamShared->State.offWrite = AudioMixerSinkTransferToCircBuf(pSink,
+                                                                    pStreamR3->State.pCircBuf,
+                                                                    pStreamShared->State.offWrite,
+                                                                    pStreamR3->u8SD,
+                                                                    pStreamR3->Dbg.Runtime.fEnabled
+                                                                    ? pStreamR3->Dbg.Runtime.pFileStream : NULL);
 
     Log3Func(("[SD%RU8] transferred=%#RX64 bytes -> @%#RX64\n", pStreamR3->u8SD,
-              pStreamR3->State.offWrite - offWriteOld, pStreamR3->State.offWrite));
+              pStreamShared->State.offWrite - offWriteOld, pStreamShared->State.offWrite));
 
     /* Update buffer stats. */
@@ -1605,5 +1647,5 @@
  */
 static void hdaR3StreamDoDmaOutput(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared,
-                                   PHDASTREAMR3 pStreamR3, uint32_t cbToProduce, uint64_t tsNowNs)
+                                   PHDASTREAMR3 pStreamR3, uint32_t const cbToProduce, uint64_t tsNowNs)
 {
     uint8_t const uSD = pStreamShared->u8SD;
@@ -1629,5 +1671,9 @@
     PRTCIRCBUF pCircBuf = pStreamR3->State.pCircBuf;
     uint32_t   cbLeft   = cbToProduce;
+# ifdef VBOX_HDA_WITH_ON_REG_ACCESS_DMA
+    Assert(cbLeft <= pStreamShared->State.cbTransferSize); /* a little pointless with the DMA'ing on LPIB read. */
+# else
     Assert(cbLeft == pStreamShared->State.cbTransferSize);
+# endif
     Assert(PDMAudioPropsIsSizeAligned(&pStreamShared->State.Cfg.Props, cbLeft));
 
@@ -1640,7 +1686,8 @@
          */
         uint32_t cbChunk = 0;
-        RTGCPHYS GCPhys  = hdaR3StreamDmaBufGet(pStreamShared, &cbChunk);
+        RTGCPHYS GCPhys  = hdaStreamDmaBufGet(pStreamShared, &cbChunk);
 
         /* Need to diverge if the BDLEs contain misaligned entries.  */
+        uint32_t cbRead  = 0;
 # if 0
         if (/** @todo pStreamShared->State.fFrameAlignedBuffers */)
@@ -1675,7 +1722,7 @@
 
 # ifdef VBOX_WITH_DTRACE
-                VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)uSD, (uint32_t)cbBufDst, pStreamR3->State.offWrite);
+                VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)uSD, (uint32_t)cbBufDst, pStreamShared->State.offWrite);
 # endif
-                pStreamR3->State.offWrite += cbBufDst;
+                pStreamShared->State.offWrite   += cbBufDst;
                 RTCircBufReleaseWriteBlock(pCircBuf, cbBufDst);
                 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbBufDst);
@@ -1683,6 +1730,6 @@
                 /* advance */
                 cbChunk                         -= (uint32_t)cbBufDst;
+                cbRead                          += (uint32_t)cbBufDst;
                 GCPhys                          +=           cbBufDst;
-                cbLeft                          -= (uint32_t)cbBufDst;
                 pStreamShared->State.offCurBdle += (uint32_t)cbBufDst;
             }
@@ -1779,10 +1826,18 @@
 # endif
 
+        cbLeft -= cbRead;
         STAM_PROFILE_STOP(&pThis->StatOut, a);
 
         /*
-         * Complete the buffer if necessary (common with the output DMA code).
+         * Complete the buffer if necessary (common with the input DMA code).
+         *
+         * Must update the DMA position before we do this as the buffer IRQ may
+         * fire on another vCPU and run in parallel to us, although it is very
+         * unlikely it can make much progress as long as we're sitting on the
+         * lock, it could still read the DMA position (Linux won't, as it reads
+         * WALCLK and possibly SDnSTS before the DMA position).
          */
-        hdaR3StreamDoDmaMaybeCompleteBuffer(pDevIns, pThis, pStreamShared, "hdaR3StreamDoDmaOutput");
+        hdaStreamSetPositionAdd(pStreamShared, pDevIns, pThis, cbRead);
+        hdaStreamDoDmaMaybeCompleteBuffer(pDevIns, pThis, pStreamShared, "hdaR3StreamDoDmaOutput");
     }
 
@@ -1801,8 +1856,147 @@
      */
     Log3Func(("LEAVE - [SD%RU8] %#RX32/%#RX32 @ %#RX64 - cTransferPendingInterrupts=%RU8\n",
-              uSD, cbToProduce, pStreamShared->State.cbTransferSize, pStreamR3->State.offWrite - cbToProduce,
+              uSD, cbToProduce, pStreamShared->State.cbTransferSize, pStreamShared->State.offWrite - cbToProduce,
               pStreamShared->State.cTransferPendingInterrupts));
 }
 
+#endif /* IN_RING3 */
+
+#ifdef VBOX_HDA_WITH_ON_REG_ACCESS_DMA
+/**
+ * Do DMA output transfer on LPIB register access.
+ *
+ * @returns VINF_SUCCESS or VINF_IOM_R3_MMIO_READ.
+ * @param   pDevIns         The device instance.
+ * @param   pThis           The shared instance data.
+ * @param   pStreamShared   The shared stream data.
+ * @param   cbToTransfer    How much to transfer.
+ */
+VBOXSTRICTRC hdaStreamDoOnAccessDmaOutput(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, uint32_t cbToTransfer)
+{
+    AssertReturn(cbToTransfer > 0, VINF_SUCCESS);
+    int rc = VINF_SUCCESS;
+
+    /*
+     * Check if we're exceeding the available buffer, go to ring-3 to
+     * handle that (we would perhaps always take this path when in ring-3).
+     */
+    uint32_t cbDma = pStreamShared->State.cbDma;
+    ASMCompilerBarrier();
+    if (   cbDma                >= sizeof(pStreamShared->State.abDma) /* paranoia */
+        || cbToTransfer         >= sizeof(pStreamShared->State.abDma) /* paranoia */
+        || cbDma + cbToTransfer >  sizeof(pStreamShared->State.abDma))
+    {
+# ifndef IN_RING3
+        STAM_REL_COUNTER_INC(&pThis->StatAccessDmaOutputToR3);
+        LogFlowFunc(("[SD%RU8] out of DMA buffer space (%#x, need %#x) -> VINF_IOM_R3_MMIO_READ\n",
+                     pStreamShared->u8SD, sizeof(pStreamShared->State.abDma) - pStreamShared->State.cbDma, cbToTransfer));
+        return VINF_IOM_R3_MMIO_READ;
+# else  /* IN_RING3 */
+        /*
+         * Flush the bounce buffer, then do direct transfers to the
+         * internal DMA buffer (updates LPIB).
+         */
+        PHDASTATER3 const       pThisCC       = PDMDEVINS_2_DATA_CC(pDevIns, PHDASTATER3);
+        uintptr_t const         idxStream     = pStreamShared->u8SD;
+        AssertReturn(idxStream < RT_ELEMENTS(pThisCC->aStreams), VERR_INTERNAL_ERROR_4);
+        PHDASTREAMR3 const      pStreamR3     = &pThisCC->aStreams[idxStream];
+
+        hdaR3StreamFlushDmaBounceBufferOutput(pStreamShared, pStreamR3);
+
+        uint32_t cbStreamFree = hdaR3StreamGetFree(pStreamR3);
+        if (cbStreamFree >= cbToTransfer)
+        { /* likely */ }
+        else
+        {
+            PAUDMIXSINK pSink = pStreamR3->pMixSink ? pStreamR3->pMixSink->pMixSink : NULL;
+            if (pSink)
+                cbStreamFree = hdaR3StreamHandleDmaBufferOverrun(pStreamShared, pStreamR3, pSink, cbToTransfer, RTTimeNanoTS(),
+                                                                 "hdaStreamDoOnAccessDmaOutput", cbStreamFree);
+            else
+            {
+                LogFunc(("[SD%RU8] No sink and insufficient internal DMA buffer space (%#x) - won't do anything\n",
+                         pStreamShared->u8SD, cbStreamFree));
+                return VINF_SUCCESS;
+            }
+            cbToTransfer = RT_MIN(cbToTransfer, cbStreamFree);
+            if (cbToTransfer < PDMAudioPropsFrameSize(&pStreamShared->State.Cfg.Props))
+            {
+                LogFunc(("[SD%RU8] No internal DMA buffer space (%#x) - won't do anything\n", pStreamShared->u8SD, cbStreamFree));
+                return VINF_SUCCESS;
+            }
+        }
+        hdaR3StreamDoDmaOutput(pDevIns, pThis, pStreamShared, pStreamR3, cbToTransfer, RTTimeNanoTS());
+        pStreamShared->State.cbDmaTotal += cbToTransfer;
+# endif /* IN_RING3 */
+    }
+    else
+    {
+        /*
+         * Transfer into the DMA bounce buffer.
+         */
+        LogFlowFunc(("[SD%RU8] Transfering %#x bytes to DMA bounce buffer (cbDma=%#x cbDmaTotal=%#x) (%p/%u)\n",
+                     pStreamShared->u8SD, cbToTransfer, cbDma, pStreamShared->State.cbDmaTotal, pStreamShared, pStreamShared->u8SD));
+        uint32_t cbLeft = cbToTransfer;
+        do
+        {
+            uint32_t cbChunk = 0;
+            RTGCPHYS GCPhys  = hdaStreamDmaBufGet(pStreamShared, &cbChunk);
+
+            bool fMustAdvanceBuffer;
+            if (cbLeft < cbChunk)
+            {
+                fMustAdvanceBuffer = false;
+                cbChunk            = cbLeft;
+            }
+            else
+                fMustAdvanceBuffer = true;
+
+            /* Read the guest data directly into the DMA bounce buffer. */
+            int rc2 = PDMDevHlpPCIPhysRead(pDevIns, GCPhys, &pStreamShared->State.abDma[cbDma], cbChunk);
+            AssertRC(rc2);
+
+            /* We update offWrite and StatBytesRead here even if we haven't moved the data
+               to the internal DMA buffer yet, because we want the dtrace even to fire here. */
+# ifdef VBOX_WITH_DTRACE
+            VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)pStreamShared->u8SD, cbChunk, pStreamShared->State.offWrite);
+# endif
+            pStreamShared->State.offWrite   += cbChunk;
+            STAM_COUNTER_ADD(&pThis->StatBytesRead, cbChunk);
+
+            /* advance */
+            pStreamShared->State.offCurBdle += cbChunk;
+            pStreamShared->State.cbDmaTotal += cbChunk;
+            cbDma                           += cbChunk;
+            pStreamShared->State.cbDma       = cbDma;
+            cbLeft                          -= cbChunk;
+            Log6Func(("cbLeft=%#x cbDma=%#x cbDmaTotal=%#x offCurBdle=%#x idxCurBdle=%#x (%p/%u)\n",
+                      cbLeft, cbDma, pStreamShared->State.cbDmaTotal, pStreamShared->State.offCurBdle,
+                      pStreamShared->State.idxCurBdle, pStreamShared, pStreamShared->u8SD));
+
+            /* Next buffer. */
+            bool fAdvanced = hdaStreamDoDmaMaybeCompleteBuffer(pDevIns, pThis, pStreamShared, "hdaStreamDoOnAccessDmaOutput");
+            AssertMsgStmt(fMustAdvanceBuffer == fAdvanced, ("%d %d\n", fMustAdvanceBuffer, fAdvanced), rc = VERR_INTERNAL_ERROR_3);
+        } while (cbLeft > 0);
+
+        /*
+         * Advance LPIB.
+         */
+        hdaStreamSetPositionAdd(pStreamShared, pDevIns, pThis, cbToTransfer - cbLeft);
+    }
+
+# ifdef VBOX_STRICT
+    uint32_t       idxSched = pStreamShared->State.idxSchedule;
+    AssertStmt(idxSched < RT_MIN(RT_ELEMENTS(pStreamShared->State.aSchedule), pStreamShared->State.cSchedule), idxSched = 0);
+    uint32_t const cbPeriod = pStreamShared->State.aSchedule[idxSched].cbPeriod;
+    AssertMsg(pStreamShared->State.cbDmaTotal < cbPeriod, ("%#x vs %#x\n", pStreamShared->State.cbDmaTotal, cbPeriod));
+# endif
+
+    STAM_REL_COUNTER_INC(&pThis->StatAccessDmaOutput);
+    return rc;
+}
+#endif /* VBOX_HDA_WITH_ON_REG_ACCESS_DMA */
+
+
+#ifdef IN_RING3
 
 /**
@@ -1817,16 +2011,16 @@
 {
 # ifdef LOG_ENABLED
-    uint64_t const offReadOld = pStreamR3->State.offRead;
+    uint64_t const offReadOld = pStreamShared->State.offRead;
 # endif
-    pStreamR3->State.offRead = AudioMixerSinkTransferFromCircBuf(pSink,
-                                                                 pStreamR3->State.pCircBuf,
-                                                                 pStreamR3->State.offRead,
-                                                                 pStreamR3->u8SD,
-                                                                 pStreamR3->Dbg.Runtime.fEnabled
-                                                                 ? pStreamR3->Dbg.Runtime.pFileStream : NULL);
+    pStreamShared->State.offRead = AudioMixerSinkTransferFromCircBuf(pSink,
+                                                                     pStreamR3->State.pCircBuf,
+                                                                     pStreamShared->State.offRead,
+                                                                     pStreamR3->u8SD,
+                                                                     pStreamR3->Dbg.Runtime.fEnabled
+                                                                     ? pStreamR3->Dbg.Runtime.pFileStream : NULL);
 
     Assert(nsNow >= pStreamShared->State.tsLastReadNs);
     Log3Func(("[SD%RU8] nsDeltaLastRead=%RI64 transferred=%#RX64 bytes -> @%#RX64\n", pStreamR3->u8SD,
-              nsNow - pStreamShared->State.tsLastReadNs, pStreamR3->State.offRead - offReadOld, pStreamR3->State.offRead));
+              nsNow - pStreamShared->State.tsLastReadNs, pStreamShared->State.offRead - offReadOld, pStreamShared->State.offRead));
     RT_NOREF(pStreamShared, nsNow);
 
@@ -1834,4 +2028,147 @@
     pStreamR3->State.StatDmaBufUsed = (uint32_t)RTCircBufUsed(pStreamR3->State.pCircBuf);
 }
+
+
+/**
+ * Deals with a DMA buffer overrun.
+ *
+ * Makes sure we return with @a cbNeeded bytes of free space in pCircBuf.
+ *
+ * @returns Number of bytes free in the internal DMA buffer.
+ * @param   pStreamShared   The shared data for the HDA stream.
+ * @param   pStreamR3       The ring-3 data for the HDA stream.
+ * @param   pSink           The mixer sink (valid).
+ * @param   cbNeeded        How much space we need (in bytes).
+ * @param   nsNow           Current RTNanoTimeTS() timestamp.
+ * @param   cbStreamFree    The current amount of free buffer space.
+ * @param   pszCaller       The caller (for logging).
+ */
+static uint32_t hdaR3StreamHandleDmaBufferOverrun(PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, PAUDMIXSINK pSink,
+                                                  uint32_t cbNeeded, uint64_t nsNow,
+                                                  const char *pszCaller, uint32_t const cbStreamFree)
+{
+    STAM_REL_COUNTER_INC(&pStreamR3->State.StatDmaFlowProblems);
+    Log(("%s: Warning! Stream #%u has insufficient space free: %#x bytes, need %#x.  Will try move data out of the buffer...\n",
+         pszCaller, pStreamShared->u8SD, cbStreamFree, cbNeeded));
+    RT_NOREF(pszCaller, cbStreamFree);
+
+    int rc = AudioMixerSinkTryLock(pSink);
+    if (RT_SUCCESS(rc))
+    {
+        hdaR3StreamPushToMixer(pStreamShared, pStreamR3, pSink, nsNow);
+        AudioMixerSinkUpdate(pSink, 0, 0);
+        AudioMixerSinkUnlock(pSink);
+    }
+    else
+        RTThreadYield();
+
+    uint32_t const cbRet = hdaR3StreamGetFree(pStreamR3);
+    Log(("%s: Gained %u bytes.\n", pszCaller, cbRet - cbStreamFree));
+    if (cbRet >= cbNeeded)
+        return cbRet;
+
+    /*
+     * Unable to make sufficient space.  Drop the whole buffer content.
+     *
+     * This is needed in order to keep the device emulation running at a
+     * constant rate, at the cost of losing valid (but too much) data.
+     */
+    STAM_REL_COUNTER_INC(&pStreamR3->State.StatDmaFlowErrors);
+    LogRel2(("HDA: Warning: Hit stream #%RU8 overflow, dropping %u bytes of audio data (%s)\n",
+             pStreamShared->u8SD, hdaR3StreamGetUsed(pStreamR3), pszCaller));
+# ifdef HDA_STRICT
+    AssertMsgFailed(("Hit stream #%RU8 overflow -- timing bug?\n", pStreamShared->u8SD));
+# endif
+/**
+ *
+ * @todo r=bird: I don't think RTCircBufReset is entirely safe w/o
+ * owning the AIO lock.  See the note in the documentation about it not being
+ * multi-threading aware (safe).   Wish I'd verified this code much earlier.
+ * Sigh^3!
+ *
+ */
+    RTCircBufReset(pStreamR3->State.pCircBuf);
+    pStreamShared->State.offWrite = 0;
+    pStreamShared->State.offRead  = 0;
+    return hdaR3StreamGetFree(pStreamR3);
+}
+
+
+# ifdef VBOX_HDA_WITH_ON_REG_ACCESS_DMA
+/**
+ * Flushes the DMA bounce buffer content to the internal DMA buffer.
+ *
+ * @param   pStreamShared   The shared data of the stream to have its DMA bounce
+ *                          buffer flushed.
+ * @param   pStreamR3       The ring-3 stream data for same.
+ */
+static void hdaR3StreamFlushDmaBounceBufferOutput(PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3)
+{
+    uint32_t cbDma = pStreamShared->State.cbDma;
+    LogFlowFunc(("cbDma=%#x\n", cbDma));
+    if (cbDma)
+    {
+        AssertReturnVoid(cbDma <= sizeof(pStreamShared->State.abDma));
+        PRTCIRCBUF const pCircBuf = pStreamR3->State.pCircBuf;
+        if (pCircBuf)
+        {
+            uint32_t offDma = 0;
+            while (offDma < cbDma)
+            {
+                uint32_t const cbSrcLeft = cbDma - offDma;
+
+                /*
+                 * Grab a chunk of the internal DMA buffer.
+                 */
+                void  *pvBufDst = NULL;
+                size_t cbBufDst = 0;
+                RTCircBufAcquireWriteBlock(pCircBuf, cbSrcLeft, &pvBufDst, &cbBufDst);
+                if (cbBufDst > 0)
+                { /* likely */ }
+                else
+                {
+                    /* We've got buffering trouble. */
+                    RTCircBufReleaseWriteBlock(pCircBuf, 0);
+
+                    PAUDMIXSINK pSink = pStreamR3->pMixSink ? pStreamR3->pMixSink->pMixSink : NULL;
+                    if (pSink)
+                        hdaR3StreamHandleDmaBufferOverrun(pStreamShared, pStreamR3, pSink, cbSrcLeft, RTTimeNanoTS(),
+                                                          "hdaR3StreamFlushDmaBounceBufferOutput", 0 /*cbStreamFree*/);
+                    else
+                    {
+                        LogFunc(("Stream #%u has no sink. Dropping the rest of the data\n", pStreamR3->u8SD));
+                        break;
+                    }
+
+                    RTCircBufAcquireWriteBlock(pCircBuf, cbSrcLeft, &pvBufDst, &cbBufDst);
+                    AssertBreakStmt(cbBufDst, RTCircBufReleaseWriteBlock(pCircBuf, 0));
+                }
+
+                /*
+                 * Copy the samples into it and write it to the debug file if open.
+                 *
+                 * We do not fire the dtrace probe here nor update offRead as that was
+                 * done already (not sure that was a good idea?).
+                 */
+                memcpy(pvBufDst, &pStreamShared->State.abDma[offDma], cbBufDst);
+
+                if (RT_LIKELY(!pStreamR3->Dbg.Runtime.fEnabled))
+                { /* likely */ }
+                else
+                    AudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMARaw, pvBufDst, cbBufDst, 0 /* fFlags */);
+
+                RTCircBufReleaseWriteBlock(pCircBuf, cbBufDst);
+
+                offDma += (uint32_t)cbBufDst;
+            }
+        }
+
+        /*
+         * Mark the buffer empty.
+         */
+        pStreamShared->State.cbDma = 0;
+    }
+}
+# endif /* VBOX_HDA_WITH_ON_REG_ACCESS_DMA */
 
 
@@ -1944,5 +2281,5 @@
     uint32_t       idxSched = pStreamShared->State.idxSchedule;
     AssertStmt(idxSched < RT_MIN(RT_ELEMENTS(pStreamShared->State.aSchedule), pStreamShared->State.cSchedule), idxSched = 0);
-    uint32_t const cbPeriod = pStreamShared->State.aSchedule[idxSched].cbPeriod;
+    uint32_t       cbPeriod = pStreamShared->State.aSchedule[idxSched].cbPeriod;
 
     /*
@@ -1951,4 +2288,18 @@
     if (hdaGetDirFromSD(pStreamShared->u8SD) == PDMAUDIODIR_OUT)
     {
+# ifdef VBOX_HDA_WITH_ON_REG_ACCESS_DMA
+        /* Subtract already transferred bytes and flush the DMA bounce buffer. */
+        uint32_t cbDmaTotal = pStreamShared->State.cbDmaTotal;
+        if (cbDmaTotal > 0)
+        {
+            AssertStmt(cbDmaTotal < cbPeriod, cbDmaTotal = cbPeriod);
+            cbPeriod -= cbDmaTotal;
+            pStreamShared->State.cbDmaTotal = 0;
+            hdaR3StreamFlushDmaBounceBufferOutput(pStreamShared, pStreamR3);
+        }
+        else
+            Assert(pStreamShared->State.cbDma == 0);
+# endif
+
         /*
          * Check how much room we have in our DMA buffer.  There should be at
@@ -1959,42 +2310,11 @@
         { /* likely */ }
         else
-        {
-            STAM_REL_COUNTER_INC(&pStreamR3->State.StatDmaFlowProblems);
-            Log(("hdaR3StreamUpdateDma: Warning! Stream #%u has insufficient space free: %u bytes, need %u.  Will try move data out of the buffer...\n",
-                 pStreamShared->u8SD, cbStreamFree, cbPeriod));
-            int rc = AudioMixerSinkTryLock(pSink);
-            if (RT_SUCCESS(rc))
-            {
-                hdaR3StreamPushToMixer(pStreamShared, pStreamR3, pSink, tsNowNs);
-                AudioMixerSinkUpdate(pSink, 0, 0);
-                AudioMixerSinkUnlock(pSink);
-            }
-            else
-                RTThreadYield();
-            Log(("hdaR3StreamUpdateDma: Gained %u bytes.\n", hdaR3StreamGetFree(pStreamR3) - cbStreamFree));
-
-            cbStreamFree = hdaR3StreamGetFree(pStreamR3);
-            if (cbStreamFree < cbPeriod)
-            {
-                /* Unable to make sufficient space.  Drop the whole buffer content.
-                 * This is needed in order to keep the device emulation running at a constant rate,
-                 * at the cost of losing valid (but too much) data. */
-                STAM_REL_COUNTER_INC(&pStreamR3->State.StatDmaFlowErrors);
-                LogRel2(("HDA: Warning: Hit stream #%RU8 overflow, dropping %u bytes of audio data\n",
-                         pStreamShared->u8SD, hdaR3StreamGetUsed(pStreamR3)));
-# ifdef HDA_STRICT
-                AssertMsgFailed(("Hit stream #%RU8 overflow -- timing bug?\n", pStreamShared->u8SD));
-# endif
-                RTCircBufReset(pStreamR3->State.pCircBuf);
-                pStreamR3->State.offWrite = 0;
-                pStreamR3->State.offRead  = 0;
-                cbStreamFree = hdaR3StreamGetFree(pStreamR3);
-            }
-        }
+            cbStreamFree = hdaR3StreamHandleDmaBufferOverrun(pStreamShared, pStreamR3, pSink, cbPeriod, tsNowNs,
+                                                             "hdaR3StreamUpdateDma", cbStreamFree);
 
         /*
          * Do the DMA transfer.
          */
-        uint64_t const offWriteBefore = pStreamR3->State.offWrite;
+        uint64_t const offWriteBefore = pStreamShared->State.offWrite;
         hdaR3StreamDoDmaOutput(pDevIns, pThis, pStreamShared, pStreamR3, RT_MIN(cbStreamFree, cbPeriod), tsNowNs);
 
@@ -2012,5 +2332,5 @@
         bool fKickAioThread;
         if (!pStreamShared->State.tsAioDelayEnd)
-            fKickAioThread = pStreamR3->State.offWrite > offWriteBefore
+            fKickAioThread = pStreamShared->State.offWrite > offWriteBefore
                           || hdaR3StreamGetFree(pStreamR3) < pStreamShared->State.cbAvgTransfer * 2;
         else if (PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer) >= pStreamShared->State.tsAioDelayEnd)
@@ -2101,5 +2421,5 @@
             {
                 AudioMixerSinkUpdate(pSink, cbStreamUsed, cbPeriod);
-                hdaR3StreamPullFromMixer(pStreamR3, pSink);
+                hdaR3StreamPullFromMixer(pStreamShared, pStreamR3, pSink);
                 AudioMixerSinkUnlock(pSink);
             }
@@ -2122,5 +2442,5 @@
                     if (cbStreamUsed < cbPeriod)
                     {
-                        hdaR3StreamPullFromMixer(pStreamR3, pSink);
+                        hdaR3StreamPullFromMixer(pStreamShared, pStreamR3, pSink);
                         cbStreamUsed = hdaR3StreamGetUsed(pStreamR3);
                         while (cbStreamUsed < cbPeriod)
@@ -2209,5 +2529,5 @@
     {
         Assert(hdaGetDirFromSD(pStreamShared->u8SD) == PDMAUDIODIR_IN);
-        hdaR3StreamPullFromMixer(pStreamR3, pSink);
+        hdaR3StreamPullFromMixer(pStreamShared, pStreamR3, pSink);
     }
 }
Index: trunk/src/VBox/Devices/Audio/DevHdaStream.h
===================================================================
--- trunk/src/VBox/Devices/Audio/DevHdaStream.h	(revision 89860)
+++ trunk/src/VBox/Devices/Audio/DevHdaStream.h	(revision 89861)
@@ -111,4 +111,21 @@
      * @note This is used for wall clock (WALCLK) calculations.  */
     uint64_t volatile       tsTransferLast;
+    /** The stream's current configuration (matches SDnFMT). */
+    PDMAUDIOSTREAMCFG       Cfg;
+    /** Timestamp (real time, in ns) of last DMA transfer. */
+    uint64_t                tsLastTransferNs;
+    /** Timestamp (real time, in ns) of last stream read (to backends).
+     *  When running in async I/O mode, this differs from \a tsLastTransferNs,
+     *  because reading / processing will be done in a separate stream. */
+    uint64_t                tsLastReadNs;
+
+    /** This is set to the timer clock time when the msInitialDelay period is over.
+     * Once reached, this is set to zero to avoid unnecessary time queries. */
+    uint64_t                tsAioDelayEnd;
+    /** The start time for the playback (on the timer clock). */
+    uint64_t                tsStart;
+
+    /** @name DMA engine
+     * @{ */
     /** Timestamp (absolute, in timer ticks) of the next DMA data transfer.
      *  Next for determining the next scheduling window.
@@ -119,21 +136,10 @@
     /** The size of an average transfer. */
     uint32_t                cbAvgTransfer;
-    /** The stream's current configuration (matches SDnFMT). */
-    PDMAUDIOSTREAMCFG       Cfg;
-    /** Timestamp (real time, in ns) of last DMA transfer. */
-    uint64_t                tsLastTransferNs;
-    /** Timestamp (real time, in ns) of last stream read (to backends).
-     *  When running in async I/O mode, this differs from \a tsLastTransferNs,
-     *  because reading / processing will be done in a separate stream. */
-    uint64_t                tsLastReadNs;
-
-    /** This is set to the timer clock time when the msInitialDelay period is over.
-     * Once reached, this is set to zero to avoid unnecessary time queries. */
-    uint64_t                tsAioDelayEnd;
-    /** The start time for the playback (on the timer clock). */
-    uint64_t                tsStart;
-
-    /** @name DMA engine
-     * @{ */
+
+    /** Current circular buffer read offset (for tracing & logging). */
+    uint64_t                offRead;
+    /** Current circular buffer write offset (for tracing & logging). */
+    uint64_t                offWrite;
+
     /** The offset into the current BDLE. */
     uint32_t                offCurBdle;
@@ -181,7 +187,18 @@
         uint8_t             abPadding[2];
     }                       aSchedule[512+8];
+
+#ifdef VBOX_HDA_WITH_ON_REG_ACCESS_DMA
+    /** Number of valid bytes in abDma.
+     * @note Volatile to prevent the compiler from re-reading it after we've
+     *       validated the value in ring-0. */
+    uint32_t volatile       cbDma;
+    /** Total number of bytes going via abDma this timer period. */
+    uint32_t                cbDmaTotal;
+    /** DMA bounce buffer for ring-0 register reads (LPIB). */
+    uint8_t                 abDma[2048 - 8];
+#endif
     /** @} */
 } HDASTREAMSTATE;
-AssertCompileSizeAlignment(HDASTREAMSTATE, 8);
+AssertCompileSizeAlignment(HDASTREAMSTATE, 16);
 AssertCompileMemberAlignment(HDASTREAMSTATE, aBdl, 8);
 AssertCompileMemberAlignment(HDASTREAMSTATE, aBdl, 16);
@@ -236,6 +253,8 @@
     TMTIMERHANDLE               hTimer;
 
+#if 0
     /** Pad the structure size to a 64 byte alignment. */
     uint64_t                    au64Padding1[2];
+#endif
 } HDASTREAM;
 AssertCompileMemberAlignment(HDASTREAM, State.aBdl, 16);
@@ -265,8 +284,4 @@
         /** Circular buffer (FIFO) for holding DMA'ed data. */
         R3PTRTYPE(PRTCIRCBUF)   pCircBuf;
-        /** Current circular buffer read offset (for tracing & logging). */
-        uint64_t                offRead;
-        /** Current circular buffer write offset (for tracing & logging). */
-        uint64_t                offWrite;
 #ifdef HDA_USE_DMA_ACCESS_HANDLER
         /** List of DMA handlers. */
@@ -296,13 +311,19 @@
         STAMPROFILE             StatReset;
         STAMPROFILE             StatStop;
-        STAMPROFILE             StatUnusedPadding;
     } State;
     /** Debug bits. */
     HDASTREAMDEBUG              Dbg;
-    uint64_t                    au64Alignment[1+4];
+    uint64_t                    au64Alignment[3];
 } HDASTREAMR3;
 AssertCompileSizeAlignment(HDASTREAMR3, 64);
 /** Pointer to an HDA stream (SDI / SDO).  */
 typedef HDASTREAMR3 *PHDASTREAMR3;
+
+/** @name Stream functions (all contexts).
+ * @{
+ */
+VBOXSTRICTRC        hdaStreamDoOnAccessDmaOutput(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared,
+                                                 uint32_t cbToTransfer);
+/** @} */
 
 #ifdef IN_RING3
