Index: /trunk/src/VBox/Devices/Storage/DevATA.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/DevATA.cpp	(revision 426)
+++ /trunk/src/VBox/Devices/Storage/DevATA.cpp	(revision 427)
@@ -77,5 +77,5 @@
  * The SSM saved state version.
  */
-#define ATA_SAVED_STATE_VERSION 14
+#define ATA_SAVED_STATE_VERSION 15
 
 /** The maximum number of release log entries per device. */
@@ -146,9 +146,9 @@
     /** Elementary ATA/ATAPI transfer size, shared PIO/DMA. */
     uint32_t cbElementaryTransfer;
+    /** Current read/write buffer position, shared PIO/DMA. */
+    uint32_t iIOBufferCur;
     /** First element beyond end of valid buffer content, shared PIO/DMA. */
     uint32_t iIOBufferEnd;
 
-    /** ATA/ATAPI current PIO read/write buffer position. Not shared with DMA for safety reasons. */
-    uint32_t iIOBufferPIO;
     /** ATA/ATAPI current PIO read/write transfer position. Not shared with DMA for safety reasons. */
     uint32_t iIOBufferPIODataStart;
@@ -295,7 +295,21 @@
     bool        fChainedTransfer;
     /** Set when the reset processing is currently active on this controller. */
-    bool volatile fReset;
+    bool        fReset;
+    /** Flag whether the current transfer needs to be redone. */
+    bool        fRedo;
+    /** Flag whether the redo suspend has been finished. */
+    bool        fRedoIdle;
+    /** Flag whether the DMA operation to be redone is the final transfer. */
+    bool        fRedoDMALastDesc;
     /** The BusMaster DMA state. */
     BMDMAState  BmDma;
+    /** Pointer to first DMA descriptor. */
+    RTGCPHYS    pFirstDMADesc;
+    /** Pointer to last DMA descriptor. */
+    RTGCPHYS    pLastDMADesc;
+    /** Pointer to current DMA buffer (for redo operations). */
+    RTGCPHYS    pRedoDMABuffer;
+    /** Size of current DMA buffer (for redo operations). */
+    uint32_t    cbRedoDMABuffer;
 
     /** The ATA/ATAPI interfaces of this controller. */
@@ -321,4 +335,6 @@
     /** The mutex protecting the request queue. */
     RTSEMMUTEX          AsyncIORequestMutex;
+    /** The event semaphore the thread is waiting on during suspended I/O. */
+    RTSEMEVENT          SuspendIOSem;
     /** Magic delay before triggering interrupts in DMA mode. */
     uint32_t            DelayIRQMillies;
@@ -422,5 +438,5 @@
 
 typedef void (*PBeginTransferFunc)(ATADevState *);
-typedef void (*PSourceSinkFunc)(ATADevState *);
+typedef bool (*PSourceSinkFunc)(ATADevState *);
 
 static void ataReadWriteSectorsBT(ATADevState *);
@@ -429,25 +445,25 @@
 static void atapiPassthroughCmdBT(ATADevState *);
 
-static void ataIdentifySS(ATADevState *);
-static void ataFlushSS(ATADevState *);
-static void ataReadSectorsSS(ATADevState *);
-static void ataWriteSectorsSS(ATADevState *);
-static void ataExecuteDeviceDiagnosticSS(ATADevState *);
-static void ataPacketSS(ATADevState *);
-static void atapiGetConfigurationSS(ATADevState *);
-static void atapiIdentifySS(ATADevState *);
-static void atapiInquirySS(ATADevState *);
-static void atapiMechanismStatusSS(ATADevState *);
-static void atapiModeSenseErrorRecoverySS(ATADevState *);
-static void atapiModeSenseCDStatusSS(ATADevState *);
-static void atapiReadSS(ATADevState *);
-static void atapiReadCapacitySS(ATADevState *);
-static void atapiReadDiscInformationSS(ATADevState *);
-static void atapiReadTOCNormalSS(ATADevState *);
-static void atapiReadTOCMultiSS(ATADevState *);
-static void atapiReadTOCRawSS(ATADevState *);
-static void atapiReadTrackInformationSS(ATADevState *);
-static void atapiRequestSenseSS(ATADevState *);
-static void atapiPassthroughSS(ATADevState *);
+static bool ataIdentifySS(ATADevState *);
+static bool ataFlushSS(ATADevState *);
+static bool ataReadSectorsSS(ATADevState *);
+static bool ataWriteSectorsSS(ATADevState *);
+static bool ataExecuteDeviceDiagnosticSS(ATADevState *);
+static bool ataPacketSS(ATADevState *);
+static bool atapiGetConfigurationSS(ATADevState *);
+static bool atapiIdentifySS(ATADevState *);
+static bool atapiInquirySS(ATADevState *);
+static bool atapiMechanismStatusSS(ATADevState *);
+static bool atapiModeSenseErrorRecoverySS(ATADevState *);
+static bool atapiModeSenseCDStatusSS(ATADevState *);
+static bool atapiReadSS(ATADevState *);
+static bool atapiReadCapacitySS(ATADevState *);
+static bool atapiReadDiscInformationSS(ATADevState *);
+static bool atapiReadTOCNormalSS(ATADevState *);
+static bool atapiReadTOCMultiSS(ATADevState *);
+static bool atapiReadTOCRawSS(ATADevState *);
+static bool atapiReadTrackInformationSS(ATADevState *);
+static bool atapiRequestSenseSS(ATADevState *);
+static bool atapiPassthroughSS(ATADevState *);
 
 /**
@@ -686,5 +702,7 @@
     rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
     AssertRC(rc);
-    fIdle = (pCtl->AsyncIOReqHead == pCtl->AsyncIOReqTail);
+    fIdle = pCtl->fRedoIdle;
+    if (!fIdle)
+        fIdle = (pCtl->AsyncIOReqHead == pCtl->AsyncIOReqTail);
     if (fStrict)
         fIdle &= (pCtl->uAsyncIOState == ATA_AIO_NEW);
@@ -991,5 +1009,5 @@
     s->cbTotalTransfer = 0;
     s->cbElementaryTransfer = 0;
-    s->iIOBufferPIO = 0;
+    s->iIOBufferCur = 0;
     s->iIOBufferEnd = 0;
     s->uTxDir = PDMBLOCKTXDIR_NONE;
@@ -999,5 +1017,5 @@
 
 
-static void ataIdentifySS(ATADevState *s)
+static bool ataIdentifySS(ATADevState *s)
 {
     uint16_t *p;
@@ -1092,8 +1110,9 @@
     s->iSourceSink = ATAFN_SS_NULL;
     ataCmdOK(s, ATA_STAT_SEEK);
-}
-
-
-static void ataFlushSS(ATADevState *s)
+    return false;
+}
+
+
+static bool ataFlushSS(ATADevState *s)
 {
     PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
@@ -1114,8 +1133,9 @@
     STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
     ataCmdOK(s, 0);
-}
-
-
-static void atapiIdentifySS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiIdentifySS(ATADevState *s)
 {
     uint16_t *p;
@@ -1173,4 +1193,5 @@
     s->iSourceSink = ATAFN_SS_NULL;
     ataCmdOK(s, ATA_STAT_SEEK);
+    return false;
 }
 
@@ -1328,5 +1349,17 @@
 }
 
-static void ataReadSectorsSS(ATADevState *s)
+
+static void ataDiskFull(PPDMDEVINS pDevIns)
+{
+    int rc;
+    LogRel(("PIIX3 ATA: Host disk full\n"));
+    rc = VMSetRuntimeError(PDMDevHlpGetVM(ATADEVSTATE_2_DEVINS(s)),
+                           false, "DevATA_DISKFULL",
+                           N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
+    AssertRC(rc);
+}
+
+
+static bool ataReadSectorsSS(ATADevState *s)
 {
     int rc;
@@ -1348,14 +1381,20 @@
     else
     {
-        if (    rc == VERR_DISK_FULL
-            ||  s->cErrors++ < MAX_LOG_REL_ERRORS)
+        if (rc == VERR_DISK_FULL)
+        {
+            ataDiskFull(ATADEVSTATE_2_DEVINS(s));
+            return true;
+        }
+        if (s->cErrors++ < MAX_LOG_REL_ERRORS)
             LogRel(("PIIX3 ATA: LUN#%d: disk read error (rc=%Vrc iSector=%#RX64 cSectors=%#RX32)\n",
                     s->iLUN, rc, iLBA, cSectors));
         ataCmdError(s, ID_ERR);
     }
-}
-
-
-static void ataWriteSectorsSS(ATADevState *s)
+    /** @todo implement redo for iSCSI */
+    return false;
+}
+
+
+static bool ataWriteSectorsSS(ATADevState *s)
 {
     int rc;
@@ -1377,12 +1416,16 @@
     else
     {
-        if (    rc == VERR_DISK_FULL
-            ||  s->cErrors++ < MAX_LOG_REL_ERRORS)
+        if (rc == VERR_DISK_FULL)
+        {
+            ataDiskFull(ATADEVSTATE_2_DEVINS(s));
+            return true;
+        }
+        if (s->cErrors++ < MAX_LOG_REL_ERRORS)
             LogRel(("PIIX3 ATA: LUN#%d: disk write error (rc=%Vrc iSector=%#RX64 cSectors=%#RX32)\n",
                     s->iLUN, rc, iLBA, cSectors));
-        if (rc == VERR_DISK_FULL)
-            AssertReleaseMsgFailed(("Host disk full\n"));
         ataCmdError(s, ID_ERR);
     }
+    /** @todo implement redo for iSCSI */
+    return false;
 }
 
@@ -1412,6 +1455,6 @@
     s->cbTotalTransfer = 0;
     s->cbElementaryTransfer = 0;
+    s->iIOBufferCur = 0;
     s->iIOBufferEnd = 0;
-    s->iIOBufferPIO = 0;
     s->uTxDir = PDMBLOCKTXDIR_NONE;
     s->iBeginTransfer = ATAFN_BT_NULL;
@@ -1507,5 +1550,5 @@
 
 
-static void atapiReadSS(ATADevState *s)
+static bool atapiReadSS(ATADevState *s)
 {
     PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
@@ -1577,8 +1620,9 @@
         atapiCmdError(s, SCSI_SENSE_MEDIUM_ERROR, SCSI_ASC_READ_ERROR);
     }
-}
-
-
-static void atapiPassthroughSS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiPassthroughSS(ATADevState *s)
 {
     PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
@@ -1653,5 +1697,5 @@
                 STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
                 }
-                return;
+                return false;
         }
         memcpy(aATAPICmd, s->aATAPICmd, ATAPI_PACKET_SIZE);
@@ -1752,8 +1796,9 @@
         s->uATAPIASC = SCSI_ASC_NONE;
     }
-}
-
-
-static void atapiReadSectors(ATADevState *s, uint32_t iATAPILBA, uint32_t cSectors, uint32_t cbSector)
+    return false;
+}
+
+
+static bool atapiReadSectors(ATADevState *s, uint32_t iATAPILBA, uint32_t cSectors, uint32_t cbSector)
 {
     Assert(cSectors > 0);
@@ -1761,8 +1806,9 @@
     s->cbATAPISector = cbSector;
     ataStartTransfer(s, cSectors * cbSector, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ, true);
-}
-
-
-static void atapiReadCapacitySS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiReadCapacitySS(ATADevState *s)
 {
     uint8_t *pbBuf = s->CTXSUFF(pbIOBuffer);
@@ -1774,8 +1820,9 @@
     s->iSourceSink = ATAFN_SS_NULL;
     atapiCmdOK(s);
-}
-
-
-static void atapiReadDiscInformationSS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiReadDiscInformationSS(ATADevState *s)
 {
     uint8_t *pbBuf = s->CTXSUFF(pbIOBuffer);
@@ -1799,8 +1846,9 @@
     s->iSourceSink = ATAFN_SS_NULL;
     atapiCmdOK(s);
-}
-
-
-static void atapiReadTrackInformationSS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiReadTrackInformationSS(ATADevState *s)
 {
     uint8_t *pbBuf = s->CTXSUFF(pbIOBuffer);
@@ -1812,5 +1860,5 @@
     {
         atapiCmdError(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
-        return;
+        return false;
     }
     memset(pbBuf, '\0', 36);
@@ -1827,8 +1875,9 @@
     s->iSourceSink = ATAFN_SS_NULL;
     atapiCmdOK(s);
-}
-
-
-static void atapiGetConfigurationSS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiGetConfigurationSS(ATADevState *s)
 {
     uint8_t *pbBuf = s->CTXSUFF(pbIOBuffer);
@@ -1840,5 +1889,5 @@
     {
         atapiCmdError(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
-        return;
+        return false;
     }
     memset(pbBuf, '\0', 32);
@@ -1853,8 +1902,9 @@
     s->iSourceSink = ATAFN_SS_NULL;
     atapiCmdOK(s);
-}
-
-
-static void atapiInquirySS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiInquirySS(ATADevState *s)
 {
     uint8_t *pbBuf = s->CTXSUFF(pbIOBuffer);
@@ -1880,8 +1930,9 @@
     s->iSourceSink = ATAFN_SS_NULL;
     atapiCmdOK(s);
-}
-
-
-static void atapiModeSenseErrorRecoverySS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiModeSenseErrorRecoverySS(ATADevState *s)
 {
     uint8_t *pbBuf = s->CTXSUFF(pbIOBuffer);
@@ -1907,8 +1958,9 @@
     s->iSourceSink = ATAFN_SS_NULL;
     atapiCmdOK(s);
-}
-
-
-static void atapiModeSenseCDStatusSS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiModeSenseCDStatusSS(ATADevState *s)
 {
     uint8_t *pbBuf = s->CTXSUFF(pbIOBuffer);
@@ -1954,8 +2006,9 @@
     s->iSourceSink = ATAFN_SS_NULL;
     atapiCmdOK(s);
-}
-
-
-static void atapiRequestSenseSS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiRequestSenseSS(ATADevState *s)
 {
     uint8_t *pbBuf = s->CTXSUFF(pbIOBuffer);
@@ -1970,8 +2023,9 @@
     s->iSourceSink = ATAFN_SS_NULL;
     atapiCmdOK(s);
-}
-
-
-static void atapiMechanismStatusSS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiMechanismStatusSS(ATADevState *s)
 {
     uint8_t *pbBuf = s->CTXSUFF(pbIOBuffer);
@@ -1988,8 +2042,9 @@
     s->iSourceSink = ATAFN_SS_NULL;
     atapiCmdOK(s);
-}
-
-
-static void atapiReadTOCNormalSS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiReadTOCNormalSS(ATADevState *s)
 {
     uint8_t *pbBuf = s->CTXSUFF(pbIOBuffer), *q, iStartTrack;
@@ -2003,5 +2058,5 @@
     {
         atapiCmdError(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
-        return;
+        return false;
     }
     q = pbBuf + 2;
@@ -2049,8 +2104,9 @@
     s->iSourceSink = ATAFN_SS_NULL;
     atapiCmdOK(s);
-}
-
-
-static void atapiReadTOCMultiSS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiReadTOCMultiSS(ATADevState *s)
 {
     uint8_t *pbBuf = s->CTXSUFF(pbIOBuffer);
@@ -2080,8 +2136,9 @@
     s->iSourceSink = ATAFN_SS_NULL;
     atapiCmdOK(s);
-}
-
-
-static void atapiReadTOCRawSS(ATADevState *s)
+    return false;
+}
+
+
+static bool atapiReadTOCRawSS(ATADevState *s)
 {
     uint8_t *pbBuf = s->CTXSUFF(pbIOBuffer), *q, iStartTrack;
@@ -2166,4 +2223,5 @@
     s->iSourceSink = ATAFN_SS_NULL;
     atapiCmdOK(s);
+    return false;
 }
 
@@ -2385,7 +2443,5 @@
                         break;
                     case 2: /* 10 - Eject media */
-#if 0
-                        rc = s->pDrvMount->pfnUnmount(s->pDrvMount);
-#else
+                        /* This must be done from EMT. */
                         {
                         PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s);
@@ -2396,5 +2452,4 @@
                         VMR3ReqFree(pReq);
                         }
-#endif
                         break;
                     case 3: /* 11 - Load media */
@@ -2820,5 +2875,5 @@
 
 
-static void ataPacketSS(ATADevState *s)
+static bool ataPacketSS(ATADevState *s)
 {
     s->fDMA = !!(s->uATARegFeature & 1);
@@ -2828,4 +2883,5 @@
     s->cbElementaryTransfer = 0;
     atapiParseCmd(s);
+    return false;
 }
 
@@ -2906,9 +2962,10 @@
 
 
-static void ataExecuteDeviceDiagnosticSS(ATADevState *s)
+static bool ataExecuteDeviceDiagnosticSS(ATADevState *s)
 {
     ataSetSignature(s);
     ataSetStatusValue(s, 0); /* NOTE: READY is _not_ set */
     s->uATARegError = 0x01;
+    return false;
 }
 
@@ -3590,37 +3647,33 @@
 
 
+/**
+ * Perform the entire DMA transfer in one go (unless a source/sink operation
+ * has to be redone or a RESET comes in between). Unlike the PIO counterpart
+ * this function cannot handle empty transfers.
+ *
+ * @param pCtl      Controller for which to perform the transfer.
+ */
 static void ataDMATransfer(PATACONTROLLER pCtl)
 {
-    BMDMAState *bm = &pCtl->BmDma;
     PPDMDEVINS pDevIns = CONTROLLER_2_DEVINS(pCtl);
-    ATADevState *s;
-    ATAFNSS iOriginalSourceSink;
-    struct {
-        uint32_t addr;
-        uint32_t size;
-    } prd;
-    RTGCPHYS cur_addr;
+    ATADevState *s = &pCtl->aIfs[pCtl->iAIOIf];
+    bool fRedo;
+    RTGCPHYS pDesc;
     uint32_t cbTotalTransfer, cbElementaryTransfer;
-    uint32_t iIOBufferDMA, iIOBufferEnd;
+    uint32_t iIOBufferCur, iIOBufferEnd;
     uint32_t dmalen;
     PDMBLOCKTXDIR uTxDir;
-    bool fLastPRD = false;
-
-    Assert(sizeof(prd) == 8);
-
-    s = &pCtl->aIfs[pCtl->iAIOIf];
-    Assert(s->cbTotalTransfer);
-    iOriginalSourceSink = (ATAFNSS)s->iSourceSink; /* Used by the hack below, but gets reset by then. */
+    bool fLastDesc = false;
+
+    Assert(sizeof(BMDMADesc) == 8);
+
+    fRedo = pCtl->fRedo;
+    if (RT_LIKELY(!fRedo))
+        Assert(s->cbTotalTransfer);
     uTxDir = (PDMBLOCKTXDIR)s->uTxDir;
     cbTotalTransfer = s->cbTotalTransfer;
     cbElementaryTransfer = s->cbElementaryTransfer;
-    iIOBufferDMA = 0;
+    iIOBufferCur = s->iIOBufferCur;
     iIOBufferEnd = s->iIOBufferEnd;
-
-    Log3(("%s: bm=%p if=%p\n", __FUNCTION__, bm, s));
-    if (uTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
-        AssertRelease(bm->u8Cmd & BM_CMD_WRITE);
-    else
-        AssertRelease(!(bm->u8Cmd & BM_CMD_WRITE));
 
     /* The DMA loop is designed to hold the lock only when absolutely
@@ -3632,32 +3685,47 @@
          __FUNCTION__, uTxDir == PDMBLOCKTXDIR_FROM_DEVICE ? "T2I" : "I2T",
          cbTotalTransfer, cbElementaryTransfer,
-         iIOBufferDMA, iIOBufferEnd));
-    /* The specs say that the descriptor table must not cross a 4K boundary. */
-    for (cur_addr = bm->pvAddr; cur_addr + sizeof(prd) <= RT_ALIGN_32(bm->pvAddr + 1, _4K); cur_addr += sizeof(prd))
-    {
-        PDMDevHlpPhysRead(pDevIns, cur_addr, &prd, sizeof(prd));
-        prd.addr = RT_LE2H_U32(prd.addr);
-        prd.size = RT_LE2H_U32(prd.size);
-        fLastPRD = !!(prd.size & 0x80000000);
-        prd.size &= 0xfffe;
-        if (prd.size == 0)
-            prd.size = 0x10000;
-        if (prd.size > cbTotalTransfer)
-            prd.size = cbTotalTransfer;
-
-        while (prd.size && cbTotalTransfer)
-        {
-            dmalen = RT_MIN(prd.size, iIOBufferEnd - iIOBufferDMA);
-            Log2(("%s: prd %#010x: addr=%#010x size=%#010x\n", __FUNCTION__,
-                   (int)cur_addr, prd.addr, prd.size));
-            if (uTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
-                PDMDevHlpPhysWrite(pDevIns, prd.addr, s->CTXSUFF(pbIOBuffer) + iIOBufferDMA, dmalen);
-            else
-                PDMDevHlpPhysRead(pDevIns, prd.addr, s->CTXSUFF(pbIOBuffer) + iIOBufferDMA, dmalen);
-            iIOBufferDMA += dmalen;
-            cbTotalTransfer -= dmalen;
-            prd.size -= dmalen;
-            prd.addr += dmalen;
-            if (    iIOBufferDMA == iIOBufferEnd
+         iIOBufferCur, iIOBufferEnd));
+    for (pDesc = pCtl->pFirstDMADesc; pDesc <= pCtl->pLastDMADesc; pDesc += sizeof(BMDMADesc))
+    {
+        BMDMADesc DMADesc;
+        RTGCPHYS pBuffer;
+        uint32_t cbBuffer;
+
+        if (RT_UNLIKELY(fRedo))
+        {
+            pBuffer = pCtl->pRedoDMABuffer;
+            cbBuffer = pCtl->cbRedoDMABuffer;
+            fLastDesc = pCtl->fRedoDMALastDesc;
+        }
+        else
+        {
+            PDMDevHlpPhysRead(pDevIns, pDesc, &DMADesc, sizeof(BMDMADesc));
+            pBuffer = RT_LE2H_U32(DMADesc.pBuffer);
+            cbBuffer = RT_LE2H_U32(DMADesc.cbBuffer);
+            fLastDesc = !!(cbBuffer & 0x80000000);
+            cbBuffer &= 0xfffe;
+            if (cbBuffer == 0)
+                cbBuffer = 0x10000;
+            if (cbBuffer > cbTotalTransfer)
+                cbBuffer = cbTotalTransfer;
+        }
+
+        while (RT_UNLIKELY(fRedo) || (cbBuffer && cbTotalTransfer))
+        {
+            if (RT_LIKELY(!fRedo))
+            {
+                dmalen = RT_MIN(cbBuffer, iIOBufferEnd - iIOBufferCur);
+                Log2(("%s: DMA desc %#010x: addr=%#010x size=%#010x\n", __FUNCTION__,
+                       (int)pDesc, pBuffer, cbBuffer));
+                if (uTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
+                    PDMDevHlpPhysWrite(pDevIns, pBuffer, s->CTXSUFF(pbIOBuffer) + iIOBufferCur, dmalen);
+                else
+                    PDMDevHlpPhysRead(pDevIns, pBuffer, s->CTXSUFF(pbIOBuffer) + iIOBufferCur, dmalen);
+                iIOBufferCur += dmalen;
+                cbTotalTransfer -= dmalen;
+                cbBuffer -= dmalen;
+                pBuffer += dmalen;
+            }
+            if (    iIOBufferCur == iIOBufferEnd
                 &&  (uTxDir == PDMBLOCKTXDIR_TO_DEVICE || cbTotalTransfer))
             {
@@ -3677,32 +3745,46 @@
                 if (s->iSourceSink != ATAFN_SS_NULL)
                 {
+                    s->iIOBufferCur = iIOBufferCur;
                     s->iIOBufferEnd = iIOBufferEnd;
                     s->cbElementaryTransfer = cbElementaryTransfer;
                     s->cbTotalTransfer = cbTotalTransfer;
                     Log2(("%s: calling source/sink function\n", __FUNCTION__));
-                    g_apfnSourceSinkFuncs[s->iSourceSink](s);
-                    cbTotalTransfer = s->cbTotalTransfer;
-                    cbElementaryTransfer = s->cbElementaryTransfer;
+                    fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s);
+                    if (RT_UNLIKELY(fRedo))
+                    {
+                        pCtl->pFirstDMADesc = pDesc;
+                        pCtl->pRedoDMABuffer = pBuffer;
+                        pCtl->cbRedoDMABuffer = cbBuffer;
+                        pCtl->fRedoDMALastDesc = fLastDesc;
+                    }
+                    else
+                    {
+                        cbTotalTransfer = s->cbTotalTransfer;
+                        cbElementaryTransfer = s->cbElementaryTransfer;
+
+                        if (uTxDir == PDMBLOCKTXDIR_TO_DEVICE && cbElementaryTransfer > cbTotalTransfer)
+                            cbElementaryTransfer = cbTotalTransfer;
+                        iIOBufferCur = 0;
+                        iIOBufferEnd = cbElementaryTransfer;
+                    }
+                    pCtl->fRedo = fRedo;
                 }
                 else
                 {
-                    /* This forces the loop to exit immediately. Just to be
-                     * on the safe side. */
-                    cur_addr = bm->pvAddr + _4K;
+                    /* This forces the loop to exit immediately. */
+                    pDesc = pCtl->pLastDMADesc + 1;
                 }
-                cbTotalTransfer = s->cbTotalTransfer;
-                cbElementaryTransfer = s->cbElementaryTransfer;
 
                 PDMCritSectLeave(&pCtl->lock);
-
-                if (uTxDir == PDMBLOCKTXDIR_TO_DEVICE && cbElementaryTransfer > cbTotalTransfer)
-                    cbElementaryTransfer = cbTotalTransfer;
-                iIOBufferDMA = 0;
-                iIOBufferEnd = cbElementaryTransfer;
+                if (RT_UNLIKELY(fRedo))
+                    break;
             }
         }
 
+        if (RT_UNLIKELY(fRedo))
+            break;
+
         /* end of transfer */
-        if (!cbTotalTransfer || fLastPRD)
+        if (!cbTotalTransfer || fLastDesc)
             break;
 
@@ -3713,10 +3795,11 @@
         }
 
-        if (!(pCtl->BmDma.u8Cmd & BM_CMD_START))
-        {
-            LogRel(("PIIX3 ATA: Ctl#%d: ABORT DMA\n", ATACONTROLLER_IDX(pCtl)));
-            ataDMATransferStop(s);
+        if (!(pCtl->BmDma.u8Cmd & BM_CMD_START) || pCtl->fReset)
+        {
+            LogRel(("PIIX3 ATA: Ctl#%d: ABORT DMA%s\n", ATACONTROLLER_IDX(pCtl), pCtl->fReset ? " due to RESET" : ""));
+            if (!pCtl->fReset)
+                ataDMATransferStop(s);
             /* This forces the loop to exit immediately. */
-            cur_addr = bm->pvAddr + _4K;
+            pDesc = pCtl->pLastDMADesc + 1;
         }
 
@@ -3730,21 +3813,13 @@
     }
 
-    if (fLastPRD)
-        bm->u8Status &= ~BM_STATUS_DMAING;
+    if (RT_UNLIKELY(fRedo))
+        return;
+
+    if (fLastDesc)
+        pCtl->BmDma.u8Status &= ~BM_STATUS_DMAING;
     s->cbTotalTransfer = cbTotalTransfer;
     s->cbElementaryTransfer = cbElementaryTransfer;
+    s->iIOBufferCur = iIOBufferCur;
     s->iIOBufferEnd = iIOBufferEnd;
-
-    /* The infamous delay IRQ hack. */
-    if (iOriginalSourceSink == ATAFN_SS_WRITE_SECTORS && pCtl->DelayIRQMillies)
-    {
-        /* Delay IRQ for writing. Required to get the Win2K installation
-         * work reliably (otherwise it crashes, usually during component
-         * install). So far no better solution has been found. */
-        Log(("%s: delay IRQ hack\n", __FUNCTION__));
-        PDMCritSectLeave(&pCtl->lock);
-        RTThreadSleep(pCtl->DelayIRQMillies);
-        PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
-    }
 }
 
@@ -3757,5 +3832,5 @@
     Log3(("%s: if=%p\n", __FUNCTION__, s));
 
-    if (s->cbTotalTransfer && s->iIOBufferPIO > s->iIOBufferEnd)
+    if (s->cbTotalTransfer && s->iIOBufferCur > s->iIOBufferEnd)
     {
         LogRel(("PIIX3 ATA: LUN#%d: %s data in the middle of a PIO transfer - VERY SLOW\n", s->iLUN, s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE ? "storing" : "loading"));
@@ -3766,10 +3841,14 @@
         if (s->iSourceSink != ATAFN_SS_NULL)
         {
+            bool fRedo;
             uint8_t status = s->uATARegStatus;
             ataSetStatusValue(s, ATA_STAT_BUSY);
             Log2(("%s: calling source/sink function\n", __FUNCTION__));
-            g_apfnSourceSinkFuncs[s->iSourceSink](s);
+            fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s);
+            pCtl->fRedo = fRedo;
+            if (RT_UNLIKELY(fRedo))
+                return;
             ataSetStatusValue(s, status);
-            s->iIOBufferPIO = 0;
+            s->iIOBufferCur = 0;
             s->iIOBufferEnd = s->cbElementaryTransfer;
         }
@@ -3786,8 +3865,8 @@
              __FUNCTION__, s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE ? "T2I" : "I2T",
              s->cbTotalTransfer, s->cbElementaryTransfer,
-             s->iIOBufferPIO, s->iIOBufferEnd));
-        ataPIOTransferStart(s, s->iIOBufferPIO, s->cbElementaryTransfer);
+             s->iIOBufferCur, s->iIOBufferEnd));
+        ataPIOTransferStart(s, s->iIOBufferCur, s->cbElementaryTransfer);
         s->cbTotalTransfer -= s->cbElementaryTransfer;
-        s->iIOBufferPIO += s->cbElementaryTransfer;
+        s->iIOBufferCur += s->cbElementaryTransfer;
 
         if (s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE && s->cbElementaryTransfer > s->cbTotalTransfer)
@@ -3798,4 +3877,25 @@
 }
 
+
+/**
+ * Suspend I/O operations on a controller. Also suspends EMT, because it's
+ * waiting for I/O to make progress. The next attempt to perform an I/O
+ * operation will be made when EMT is resumed up again (as the resume
+ * callback below restarts I/O).
+ *
+ * @param pCtl      Controller for which to suspend I/O.
+ */
+static void ataSuspendRedo(PATACONTROLLER pCtl)
+{
+    PPDMDEVINS  pDevIns = CONTROLLER_2_DEVINS(pCtl);
+    PVMREQ      pReq;
+    int         rc;
+
+    pCtl->fRedoIdle = true;
+    rc = VMR3ReqCall(PDMDevHlpGetVM(pDevIns), &pReq, RT_INDEFINITE_WAIT,
+                     (PFNRT)pDevIns->pDevHlp->pfnVMSuspend, 1, pDevIns);
+    AssertReleaseRC(rc);
+    VMR3ReqFree(pReq);
+}
 
 /** Asynch I/O thread for an interface. Once upon a time this was readable
@@ -3816,4 +3916,14 @@
     while (!pCtl->fShutdown)
     {
+        /* Keep this thread from doing anything as long as EMT is suspended. */
+        while (pCtl->fRedoIdle)
+        {
+            rc = RTSemEventWait(pCtl->SuspendIOSem, RT_INDEFINITE_WAIT);
+            if (VBOX_FAILURE(rc) || pCtl->fShutdown)
+                break;
+
+            pCtl->fRedoIdle = false;
+        }
+
         /* Wait for work.  */
         if (pReq == NULL)
@@ -3888,5 +3998,5 @@
                     s->iIOBufferEnd = s->cbTotalTransfer;
                 }
-                s->iIOBufferPIO = 0;
+                s->iIOBufferCur = 0;
 
                 if (s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE)
@@ -3894,6 +4004,18 @@
                     if (s->iSourceSink != ATAFN_SS_NULL)
                     {
+                        bool fRedo;
                         Log2(("%s: Ctl#%d: calling source/sink function\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
-                        g_apfnSourceSinkFuncs[s->iSourceSink](s);
+                        fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s);
+                        pCtl->fRedo = fRedo;
+                        if (RT_UNLIKELY(fRedo))
+                        {
+                            /* Operation failed at the initial transfer, restart
+                             * everything from scratch by resending the current
+                             * request. Occurs very rarely, not worth optimizing. */
+                            LogRel(("%s: Ctl#%d: redo entire operation\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
+                            ataAsyncIOPutRequest(pCtl, pReq);
+                            ataSuspendRedo(pCtl);
+                            break;
+                        }
                     }
                     else
@@ -3938,4 +4060,5 @@
                     {
                         ataPIOTransfer(pCtl);
+                        Assert(!pCtl->fRedo);
                         if (s->fATAPITransfer || s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE)
                             ataSetIRQ(s);
@@ -3948,4 +4071,5 @@
                         /* Finish PIO transfer. */
                         ataPIOTransfer(pCtl);
+                        Assert(!pCtl->fRedo);
                         if (!s->fATAPITransfer)
                             ataSetIRQ(s);
@@ -3956,7 +4080,45 @@
 
             case ATA_AIO_DMA:
+            {
+                BMDMAState *bm = &pCtl->BmDma;
                 s = &pCtl->aIfs[pCtl->iAIOIf]; /* Do not remove or there's an instant crash after loading the saved state */
-
+                ATAFNSS iOriginalSourceSink = (ATAFNSS)s->iSourceSink; /* Used by the hack below, but gets reset by then. */
+
+                if (s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
+                    AssertRelease(bm->u8Cmd & BM_CMD_WRITE);
+                else
+                    AssertRelease(!(bm->u8Cmd & BM_CMD_WRITE));
+
+                if (RT_LIKELY(!pCtl->fRedo))
+                {
+                    /* The specs say that the descriptor table must not cross a
+                     * 4K boundary. */
+                    pCtl->pFirstDMADesc = bm->pvAddr;
+                    pCtl->pLastDMADesc = RT_ALIGN_32(bm->pvAddr + 1, _4K) - sizeof(BMDMADesc);
+                }
                 ataDMATransfer(pCtl);
+
+                if (RT_UNLIKELY(pCtl->fRedo))
+                {
+                    LogRel(("PIIX3 ATA: Ctl#%d: redo DMA operation\n", ATACONTROLLER_IDX(pCtl)));
+                    ataAsyncIOPutRequest(pCtl, &ataDMARequest);
+                    ataSuspendRedo(pCtl);
+                    break;
+                }
+
+                /* The infamous delay IRQ hack. */
+                if (   iOriginalSourceSink == ATAFN_SS_WRITE_SECTORS
+                    && s->cbTotalTransfer == 0
+                    && pCtl->DelayIRQMillies)
+                {
+                    /* Delay IRQ for writing. Required to get the Win2K
+                     * installation work reliably (otherwise it crashes,
+                     * usually during component install). So far no better
+                     * solution has been found. */
+                    Log(("%s: delay IRQ hack\n", __FUNCTION__));
+                    PDMCritSectLeave(&pCtl->lock);
+                    RTThreadSleep(pCtl->DelayIRQMillies);
+                    PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
+                }
 
                 ataUnsetStatus(s, ATA_STAT_DRQ);
@@ -3972,4 +4134,5 @@
                 pCtl->uAsyncIOState = ATA_AIO_NEW;
                 break;
+            }
 
             case ATA_AIO_PIO:
@@ -3978,7 +4141,16 @@
                 if (s->iSourceSink != ATAFN_SS_NULL)
                 {
+                    bool fRedo;
                     Log2(("%s: Ctl#%d: calling source/sink function\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
-                    g_apfnSourceSinkFuncs[s->iSourceSink](s);
-                    s->iIOBufferPIO = 0;
+                    fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s);
+                    pCtl->fRedo = fRedo;
+                    if (RT_UNLIKELY(fRedo))
+                    {
+                        LogRel(("PIIX3 ATA: Ctl#%d: redo PIO operation\n", ATACONTROLLER_IDX(pCtl)));
+                        ataAsyncIOPutRequest(pCtl, &ataPIORequest);
+                        ataSuspendRedo(pCtl);
+                        break;
+                    }
+                    s->iIOBufferCur = 0;
                     s->iIOBufferEnd = s->cbElementaryTransfer;
                 }
@@ -4134,4 +4306,10 @@
         pCtl->AsyncIOSem = NIL_RTSEMEVENT;
     }
+    if (pCtl->SuspendIOSem)
+    {
+        RTSemEventDestroy(pCtl->SuspendIOSem);
+        pCtl->SuspendIOSem = NIL_RTSEMEVENT;
+    }
+    /* Do not destroy request mutex yet, still needed for proper shutdown. */
     pCtl->fShutdown = false;
     /* This must be last, as it also signals thread exit to EMT. */
@@ -4376,4 +4554,6 @@
 
         pData->aCts[i].fReset = true;
+        pData->aCts[i].fRedo = false;
+        pData->aCts[i].fRedoIdle = false;
         ataAsyncIOClearRequests(&pData->aCts[i]);
         Log2(("%s: Ctl#%d: message to async I/O thread, reset controller\n", __FUNCTION__, i));
@@ -4775,18 +4955,14 @@
     PCIATAState *pData = PDMINS2DATA(pDevIns, PCIATAState *);
 
-    pData->aCts[0].pDevInsGC += offDelta;
-    pData->aCts[1].pDevInsGC += offDelta;
-    pData->aCts[0].aIfs[0].pDevInsGC += offDelta;
-    pData->aCts[0].aIfs[0].pControllerGC += offDelta;
-    ataRelocBuffer(pDevIns, &pData->aCts[0].aIfs[0]);
-    pData->aCts[0].aIfs[1].pDevInsGC += offDelta;
-    pData->aCts[0].aIfs[1].pControllerGC += offDelta;
-    ataRelocBuffer(pDevIns, &pData->aCts[0].aIfs[1]);
-    pData->aCts[1].aIfs[0].pDevInsGC += offDelta;
-    pData->aCts[1].aIfs[0].pControllerGC += offDelta;
-    ataRelocBuffer(pDevIns, &pData->aCts[1].aIfs[0]);
-    pData->aCts[1].aIfs[1].pDevInsGC += offDelta;
-    pData->aCts[1].aIfs[1].pControllerGC += offDelta;
-    ataRelocBuffer(pDevIns, &pData->aCts[1].aIfs[1]);
+    for (uint32_t i = 0; i < RT_ELEMENTS(pData->aCts); i++)
+    {
+        pData->aCts[i].pDevInsGC += offDelta;
+        pData->aCts[i].aIfs[0].pDevInsGC += offDelta;
+        pData->aCts[i].aIfs[0].pControllerGC += offDelta;
+        ataRelocBuffer(pDevIns, &pData->aCts[i].aIfs[0]);
+        pData->aCts[i].aIfs[1].pDevInsGC += offDelta;
+        pData->aCts[i].aIfs[1].pControllerGC += offDelta;
+        ataRelocBuffer(pDevIns, &pData->aCts[i].aIfs[1]);
+    }
 }
 
@@ -4803,10 +4979,7 @@
 {
     PCIATAState    *pData = PDMINS2DATA(pDevIns, PCIATAState *);
+    int             rc;
 
     Log(("%s:\n", __FUNCTION__));
-
-    /* sanity - power off will have made sure they are all idle. */
-    Assert(!(pData->aCts[0].BmDma.u8Status & BM_STATUS_DMAING));
-    Assert(!(pData->aCts[1].BmDma.u8Status & BM_STATUS_DMAING));
 
     /*
@@ -4818,5 +4991,6 @@
         {
             ASMAtomicXchgU32(&pData->aCts[i].fShutdown, true);
-            RTSemEventSignal(pData->aCts[i].AsyncIOSem);
+            rc = RTSemEventSignal(pData->aCts[i].AsyncIOSem);
+            AssertRC(rc);
         }
     }
@@ -4848,4 +5022,16 @@
     else
         AssertMsgFailed(("Async I/O is still busy!\n"));
+
+    /*
+     * Now the request mutexes are no longer needed. Free resources.
+     */
+    for (uint32_t i = 0; i < RT_ELEMENTS(pData->aCts); i++)
+    {
+        if (pData->aCts[i].AsyncIORequestMutex)
+        {
+            RTSemMutexDestroy(pData->aCts[i].AsyncIORequestMutex);
+            pData->aCts[i].AsyncIORequestMutex = NIL_RTSEMEVENT;
+        }
+    }
     return VINF_SUCCESS;
 }
@@ -5092,4 +5278,28 @@
 
 /**
+ * Resume notification.
+ *
+ * @returns VBox status.
+ * @param   pDrvIns     The driver instance data.
+ */
+static DECLCALLBACK(void) ataResume(PPDMDEVINS pDevIns)
+{
+    PCIATAState    *pData = PDMINS2DATA(pDevIns, PCIATAState *);
+    int             rc;
+
+    Log(("%s:\n", __FUNCTION__));
+    for (uint32_t i = 0; i < RT_ELEMENTS(pData->aCts); i++)
+    {
+        if (pData->aCts[i].fRedo && pData->aCts[i].fRedoIdle)
+        {
+            rc = RTSemEventSignal(pData->aCts[i].SuspendIOSem);
+            AssertRC(rc);
+        }
+    }
+    return;
+}
+
+
+/**
  * Power Off notification.
  *
@@ -5118,10 +5328,10 @@
 
     /* sanity - the suspend notification will wait on the async stuff. */
-    Assert(ataAsyncIOIsIdle(&pData->aCts[0], false));
-    Assert(ataAsyncIOIsIdle(&pData->aCts[1], false));
-
-    if (    !ataAsyncIOIsIdle(&pData->aCts[0], false)
-        ||  !ataAsyncIOIsIdle(&pData->aCts[1], false))
-        return VERR_SSM_IDE_ASYNC_TIMEOUT;
+    for (uint32_t i = 0; i < RT_ELEMENTS(pData->aCts); i++)
+    {
+        Assert(ataAsyncIOIsIdle(&pData->aCts[i], false));
+        if (!ataAsyncIOIsIdle(&pData->aCts[i], false))
+            return VERR_SSM_IDE_ASYNC_TIMEOUT;
+    }
     return VINF_SUCCESS;
 }
@@ -5139,8 +5349,4 @@
     PCIATAState    *pData = PDMINS2DATA(pDevIns, PCIATAState *);
 
-    /* No dmaing now. */
-    Assert(!(pData->aCts[0].BmDma.u8Status & BM_STATUS_DMAING));
-    Assert(!(pData->aCts[1].BmDma.u8Status & BM_STATUS_DMAING));
-
     for (uint32_t i = 0; i < RT_ELEMENTS(pData->aCts); i++)
     {
@@ -5150,5 +5356,12 @@
         SSMR3PutBool(pSSMHandle, pData->aCts[i].fChainedTransfer);
         SSMR3PutBool(pSSMHandle, pData->aCts[i].fReset);
+        SSMR3PutBool(pSSMHandle, pData->aCts[i].fRedo);
+        SSMR3PutBool(pSSMHandle, pData->aCts[i].fRedoIdle);
+        SSMR3PutBool(pSSMHandle, pData->aCts[i].fRedoDMALastDesc);
         SSMR3PutMem(pSSMHandle, &pData->aCts[i].BmDma, sizeof(pData->aCts[i].BmDma));
+        SSMR3PutGCPhys(pSSMHandle, pData->aCts[i].pFirstDMADesc);
+        SSMR3PutGCPhys(pSSMHandle, pData->aCts[i].pLastDMADesc);
+        SSMR3PutGCPhys(pSSMHandle, pData->aCts[i].pRedoDMABuffer);
+        SSMR3PutU32(pSSMHandle, pData->aCts[i].cbRedoDMABuffer);
 
         for (uint32_t j = 0; j < RT_ELEMENTS(pData->aCts[i].aIfs); j++)
@@ -5186,6 +5399,6 @@
             SSMR3PutU32(pSSMHandle, pData->aCts[i].aIfs[j].cbTotalTransfer);
             SSMR3PutU32(pSSMHandle, pData->aCts[i].aIfs[j].cbElementaryTransfer);
+            SSMR3PutU32(pSSMHandle, pData->aCts[i].aIfs[j].iIOBufferCur);
             SSMR3PutU32(pSSMHandle, pData->aCts[i].aIfs[j].iIOBufferEnd);
-            SSMR3PutU32(pSSMHandle, pData->aCts[i].aIfs[j].iIOBufferPIO);
             SSMR3PutU32(pSSMHandle, pData->aCts[i].aIfs[j].iIOBufferPIODataStart);
             SSMR3PutU32(pSSMHandle, pData->aCts[i].aIfs[j].iIOBufferPIODataEnd);
@@ -5235,6 +5448,5 @@
     {
         /* integrity check */
-        if (    (pData->aCts[i].BmDma.u8Status & BM_STATUS_DMAING)
-            ||  !ataAsyncIOIsIdle(&pData->aCts[i], false))
+        if (!ataAsyncIOIsIdle(&pData->aCts[i], false))
         {
             AssertMsgFailed(("Async I/O for controller %d is active\n", i));
@@ -5248,5 +5460,12 @@
         SSMR3GetBool(pSSMHandle, &pData->aCts[i].fChainedTransfer);
         SSMR3GetBool(pSSMHandle, (bool *)&pData->aCts[i].fReset);
+        SSMR3GetBool(pSSMHandle, (bool *)&pData->aCts[i].fRedo);
+        SSMR3GetBool(pSSMHandle, (bool *)&pData->aCts[i].fRedoIdle);
+        SSMR3GetBool(pSSMHandle, (bool *)&pData->aCts[i].fRedoDMALastDesc);
         SSMR3GetMem(pSSMHandle, &pData->aCts[i].BmDma, sizeof(pData->aCts[i].BmDma));
+        SSMR3GetGCPhys(pSSMHandle, &pData->aCts[i].pFirstDMADesc);
+        SSMR3GetGCPhys(pSSMHandle, &pData->aCts[i].pLastDMADesc);
+        SSMR3GetGCPhys(pSSMHandle, &pData->aCts[i].pRedoDMABuffer);
+        SSMR3GetU32(pSSMHandle, &pData->aCts[i].cbRedoDMABuffer);
 
         for (uint32_t j = 0; j < RT_ELEMENTS(pData->aCts[i].aIfs); j++)
@@ -5284,6 +5503,6 @@
             SSMR3GetU32(pSSMHandle, &pData->aCts[i].aIfs[j].cbTotalTransfer);
             SSMR3GetU32(pSSMHandle, &pData->aCts[i].aIfs[j].cbElementaryTransfer);
+            SSMR3GetU32(pSSMHandle, &pData->aCts[i].aIfs[j].iIOBufferCur);
             SSMR3GetU32(pSSMHandle, &pData->aCts[i].aIfs[j].iIOBufferEnd);
-            SSMR3GetU32(pSSMHandle, &pData->aCts[i].aIfs[j].iIOBufferPIO);
             SSMR3GetU32(pSSMHandle, &pData->aCts[i].aIfs[j].iIOBufferPIODataStart);
             SSMR3GetU32(pSSMHandle, &pData->aCts[i].aIfs[j].iIOBufferPIODataEnd);
@@ -5541,4 +5760,6 @@
         rc = RTSemEventCreate(&pCtl->AsyncIOSem);
         AssertRC(rc);
+        rc = RTSemEventCreate(&pCtl->SuspendIOSem);
+        AssertRC(rc);
         rc = RTSemMutexCreate(&pCtl->AsyncIORequestMutex);
         AssertRC(rc);
@@ -5546,6 +5767,6 @@
         rc = RTThreadCreate(&pCtl->AsyncIOThread, ataAsyncIOLoop, (void *)pCtl, 128*1024, RTTHREADTYPE_IO, 0, "ATA");
         AssertRC(rc);
-        Assert(pCtl->AsyncIOThread != NIL_RTTHREAD && pCtl->AsyncIOSem != NIL_RTSEMEVENT);
-        Log(("%s: controller %d AIO thread id %#x; sem %p mutex %p\n", __FUNCTION__, i, pCtl->AsyncIOThread, pCtl->AsyncIOSem, pCtl->AsyncIORequestMutex));
+        Assert(pCtl->AsyncIOThread != NIL_RTTHREAD && pCtl->AsyncIOSem != NIL_RTSEMEVENT && pCtl->SuspendIOSem != NIL_RTSEMEVENT && pCtl->AsyncIORequestMutex != NIL_RTSEMMUTEX);
+        Log(("%s: controller %d AIO thread id %#x; sem %p susp_sem %p mutex %p\n", __FUNCTION__, i, pCtl->AsyncIOThread, pCtl->AsyncIOSem, pCtl->SuspendIOSem, pCtl->AsyncIORequestMutex));
 
         for (uint32_t j = 0; j < RT_ELEMENTS(pCtl->aIfs); j++)
@@ -5651,5 +5872,5 @@
     ataSuspend,
     /* pfnResume */
-    NULL,
+    ataResume,
     /* pfnAttach */
     ataAttach,
