Index: /trunk/include/VBox/vd-plugin.h
===================================================================
--- /trunk/include/VBox/vd-plugin.h	(revision 38875)
+++ /trunk/include/VBox/vd-plugin.h	(revision 38876)
@@ -601,4 +601,37 @@
                                            unsigned fDiscard));
 
+    /**
+     * Discards the given amount of bytes decreasing the size of the image if possible
+     * callback version for asynchronous I/O.
+     *
+     * @returns VBox status code.
+     * @retval  VERR_VD_DISCARD_ALIGNMENT_NOT_MET if the range doesn't meet the required alignment
+     *          for the discard.
+     * @param   pBackendData         Opaque state data for this image.
+     * @param   pIoCtx               I/O context associated with this request.
+     * @param   uOffset              The offset of the first byte to discard.
+     * @param   cbDiscard            How many bytes to discard.
+     * @param   pcbPreAllocated      Pointer to the returned amount of bytes that must
+     *                               be discarded before the range to perform a full
+     *                               block discard.
+     * @param   pcbPostAllocated     Pointer to the returned amount of bytes that must
+     *                               be discarded after the range to perform a full
+     *                               block discard.
+     * @param   pcbActuallyDiscarded Pointer to the returned amount of bytes which
+     *                               could be actually discarded.
+     * @param   ppbmAllocationBitmap Where to store the pointer to the allocation bitmap
+     *                               if VERR_VD_DISCARD_ALIGNMENT_NOT_MET is returned or NULL
+     *                               if the allocation bitmap should be returned.
+     * @param   fDiscard             Flags which affect discard behavior. Combination
+     *                               of the VD_DISCARD_* flags.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnAsyncDiscard, (void *pBackendData, PVDIOCTX pIoCtx,
+                                                uint64_t uOffset, size_t cbDiscard,
+                                                size_t *pcbPreAllocated,
+                                                size_t *pcbPostAllocated,
+                                                size_t *pcbActuallyDiscarded,
+                                                void   **ppbmAllocationBitmap,
+                                                unsigned fDiscard));
+
 } VBOXHDDBACKEND;
 
Index: /trunk/include/VBox/vd.h
===================================================================
--- /trunk/include/VBox/vd.h	(revision 38875)
+++ /trunk/include/VBox/vd.h	(revision 38876)
@@ -398,20 +398,4 @@
 
 /**
- * VD range descriptor.
- */
-typedef struct VDRANGE
-{
-    /** Start offset in bytes, multiple of 512. */
-    uint64_t    offStart;
-    /** Amount of bytes described by this range, multiple of 512. */
-    size_t      cbRange;
-} VDRANGE;
-
-/** Pointer to a range descriptor. */
-typedef VDRANGE *PVDRANGE;
-/** Pointer to a constant range descriptor. */
-typedef const VDRANGE *PCVDRANGE;
-
-/**
  * VBox HDD Container main structure.
  */
@@ -1122,5 +1106,5 @@
  *       appear to contain data. This method is mainly used to implement TRIM support.
  */
-VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCVDRANGE paRanges, unsigned cRanges);
+VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges);
 
 
@@ -1170,4 +1154,20 @@
                                PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
                                void *pvUser1, void *pvUser2);
+
+/**
+ * Start an asynchronous discard request.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   paRanges        The array of ranges to discard.
+ * @param   cRanges         Number of entries in the array.
+ * @param   pfnComplete     Completion callback.
+ * @param   pvUser1         User data which is passed on completion.
+ * @param   pvUser2         User data which is passed on completion.
+ */
+VBOXDDU_DECL(int) VDAsyncDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges,
+                                       PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
+                                       void *pvUser1, void *pvUser);
+
 RT_C_DECLS_END
 
Index: /trunk/include/VBox/vddbg.h
===================================================================
--- /trunk/include/VBox/vddbg.h	(revision 38875)
+++ /trunk/include/VBox/vddbg.h	(revision 38876)
@@ -175,5 +175,5 @@
  * @param   phIoLogEntry Where to store the I/O log entry handle on success.
  */
-VBOXDDU_DECL(int) VDDbgIoLogStartDiscard(VDIOLOGGER hIoLogger, bool fAsync, PVDRANGE paRanges, unsigned cRanges,
+VBOXDDU_DECL(int) VDDbgIoLogStartDiscard(VDIOLOGGER hIoLogger, bool fAsync, PCRTRANGE paRanges, unsigned cRanges,
                                          PVDIOLOGENT phIoLogEntry);
 
@@ -239,5 +239,5 @@
  */
 VBOXDDU_DECL(int) VDDbgIoLogEventGetStartDiscard(VDIOLOGGER hIoLogger, uint64_t *pidEvent, bool *pfAsync,
-                                                 PVDRANGE *ppaRanges, unsigned *pcRanges);
+                                                 PRTRANGE *ppaRanges, unsigned *pcRanges);
 
 /**
Index: /trunk/src/VBox/Storage/DMG.cpp
===================================================================
--- /trunk/src/VBox/Storage/DMG.cpp	(revision 38875)
+++ /trunk/src/VBox/Storage/DMG.cpp	(revision 38876)
@@ -2334,4 +2334,6 @@
     NULL,
     /* pfnDiscard */
+    NULL,
+    /* pfnAsyncDiscard */
     NULL
 };
Index: /trunk/src/VBox/Storage/Debug/VDDbgIoLog.cpp
===================================================================
--- /trunk/src/VBox/Storage/Debug/VDDbgIoLog.cpp	(revision 38875)
+++ /trunk/src/VBox/Storage/Debug/VDDbgIoLog.cpp	(revision 38876)
@@ -449,5 +449,5 @@
 }
 
-VBOXDDU_DECL(int) VDDbgIoLogStartDiscard(VDIOLOGGER hIoLogger, bool fAsync, PVDRANGE paRanges, unsigned cRanges,
+VBOXDDU_DECL(int) VDDbgIoLogStartDiscard(VDIOLOGGER hIoLogger, bool fAsync, PCRTRANGE paRanges, unsigned cRanges,
                                          PVDIOLOGENT phIoLogEntry)
 {
@@ -697,5 +697,5 @@
 
 VBOXDDU_DECL(int) VDDbgIoLogEventGetStartDiscard(VDIOLOGGER hIoLogger, uint64_t *pidEvent, bool *pfAsync,
-                                                 PVDRANGE *ppaRanges, unsigned *pcRanges)
+                                                 PRTRANGE *ppaRanges, unsigned *pcRanges)
 {
     int rc = VINF_SUCCESS;
@@ -716,5 +716,5 @@
         if (RT_SUCCESS(rc))
         {
-            PVDRANGE paRanges = NULL;
+            PRTRANGE paRanges = NULL;
             IoLogEntryDiscard DiscardRange;
 
@@ -724,5 +724,5 @@
             *pcRanges  = RT_LE2H_U32(Entry.Discard.cRanges);
 
-            paRanges = (PVDRANGE)RTMemAllocZ(*pcRanges * sizeof(VDRANGE));
+            paRanges = (PRTRANGE)RTMemAllocZ(*pcRanges * sizeof(RTRANGE));
             if (paRanges)
             {
Index: /trunk/src/VBox/Storage/ISCSI.cpp
===================================================================
--- /trunk/src/VBox/Storage/ISCSI.cpp	(revision 38875)
+++ /trunk/src/VBox/Storage/ISCSI.cpp	(revision 38876)
@@ -5572,4 +5572,6 @@
     NULL,
     /* pfnDiscard */
+    NULL,
+    /* pfnAsyncDiscard */
     NULL
 };
Index: /trunk/src/VBox/Storage/QCOW.cpp
===================================================================
--- /trunk/src/VBox/Storage/QCOW.cpp	(revision 38875)
+++ /trunk/src/VBox/Storage/QCOW.cpp	(revision 38876)
@@ -2854,4 +2854,6 @@
     NULL,
     /* pfnDiscard */
+    NULL,
+    /* pfnAsyncDiscard */
     NULL
 };
Index: /trunk/src/VBox/Storage/QED.cpp
===================================================================
--- /trunk/src/VBox/Storage/QED.cpp	(revision 38875)
+++ /trunk/src/VBox/Storage/QED.cpp	(revision 38876)
@@ -2921,4 +2921,6 @@
     qedResize,
     /* pfnDiscard */
+    NULL,
+    /* pfnAsyncDiscard */
     NULL
 };
Index: /trunk/src/VBox/Storage/RAW.cpp
===================================================================
--- /trunk/src/VBox/Storage/RAW.cpp	(revision 38875)
+++ /trunk/src/VBox/Storage/RAW.cpp	(revision 38876)
@@ -1292,4 +1292,6 @@
     NULL,
     /* pfnDiscard */
+    NULL,
+    /* pfnAsyncDiscard */
     NULL
 };
Index: /trunk/src/VBox/Storage/VD.cpp
===================================================================
--- /trunk/src/VBox/Storage/VD.cpp	(revision 38875)
+++ /trunk/src/VBox/Storage/VD.cpp	(revision 38876)
@@ -273,4 +273,6 @@
     /** Flush */
     VDIOCTXTXDIR_FLUSH,
+    /** Discard */
+    VDIOCTXTXDIR_DISCARD,
     /** 32bit hack */
     VDIOCTXTXDIR_32BIT_HACK = 0x7fffffff
@@ -291,19 +293,4 @@
     /** Return code. */
     int                          rcReq;
-    /** Transfer direction */
-    VDIOCTXTXDIR                 enmTxDir;
-    /** Number of bytes left until this context completes. */
-    volatile uint32_t            cbTransferLeft;
-    /** Current offset */
-    volatile uint64_t            uOffset;
-    /** Number of bytes to transfer */
-    volatile size_t              cbTransfer;
-    /** Current image in the chain. */
-    PVDIMAGE                     pImageCur;
-    /** Start image to read from. pImageCur is reset to this
-     *  value after it reached the first image in the chain. */
-    PVDIMAGE                     pImageStart;
-    /** S/G buffer */
-    RTSGBUF                      SgBuf;
     /** Flag whether the I/O context is blocked because it is in the growing list. */
     bool                         fBlocked;
@@ -321,4 +308,45 @@
     /** Next transfer part after the current one completed. */
     PFNVDIOCTXTRANSFER           pfnIoCtxTransferNext;
+    /** Transfer direction */
+    VDIOCTXTXDIR                 enmTxDir;
+    /** Request type dependent data. */
+    union
+    {
+        /** I/O request (read/write). */
+        struct
+        {
+            /** Number of bytes left until this context completes. */
+            volatile uint32_t    cbTransferLeft;
+            /** Current offset */
+            volatile uint64_t    uOffset;
+            /** Number of bytes to transfer */
+            volatile size_t      cbTransfer;
+            /** Current image in the chain. */
+            PVDIMAGE             pImageCur;
+            /** Start image to read from. pImageCur is reset to this
+             *  value after it reached the first image in the chain. */
+            PVDIMAGE             pImageStart;
+            /** S/G buffer */
+            RTSGBUF              SgBuf;
+        } Io;
+        /** Discard requests. */
+        struct
+        {
+            /** Pointer to the range descriptor array. */
+            PCRTRANGE            paRanges;
+            /** Number of ranges in the array. */
+            unsigned             cRanges;
+            /** Range descriptor index which is processed. */
+            unsigned             idxRange;
+            /** Start offset to discard currently. */
+            uint64_t             offCur;
+            /** How many bytes left to discard in the current range. */
+            size_t               cbDiscardLeft;
+            /** How many bytes to discard in the current block (<= cbDiscardLeft). */
+            size_t               cbThisDiscard;
+            /** Discard block handled currently. */
+            PVDDISCARDBLOCK      pBlock;
+        } Discard;
+    } Req;
     /** Parent I/O context if any. Sets the type of the context (root/child) */
     PVDIOCTX                     pIoCtxParent;
@@ -333,5 +361,5 @@
             /** User argument 1 passed on completion. */
             void                        *pvUser1;
-            /** User argument 1 passed on completion. */
+            /** User argument 2 passed on completion. */
             void                        *pvUser2;
         } Root;
@@ -369,4 +397,7 @@
 } VDIOCTX;
 
+/**
+ * List node for deferred I/O contexts.
+ */
 typedef struct VDIOCTXDEFERRED
 {
@@ -501,4 +532,7 @@
     &g_VciCacheBackend
 };
+
+/** Forward declaration of the async discard helper. */
+static int vdDiscardHelperAsync(PVDIOCTX pIoCtx);
 
 /**
@@ -1163,5 +1197,8 @@
                 }
                 else
+                {
+                    RTMemFree(pbmAllocated);
                     rc = VERR_NO_MEMORY;
+                }
             }
         }
@@ -1169,13 +1206,7 @@
         {
             /* Range lies partly in the block, update allocation bitmap. */
+            int32_t idxStart, idxEnd;
+
             cbThisDiscard = RT_MIN(cbDiscard, pBlock->Core.KeyLast - offStart + 1);
-            rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET;
-        }
-
-        Assert(cbDiscard >= cbThisDiscard);
-
-        if (rc == VERR_VD_DISCARD_ALIGNMENT_NOT_MET)
-        {
-            int32_t idxStart, idxEnd;
 
             AssertPtr(pBlock);
@@ -1241,5 +1272,5 @@
  * @param   cRanges  The number of ranges in the array.
  */
-static int vdDiscardHelper(PVBOXHDD pDisk, PCVDRANGE paRanges, unsigned cRanges)
+static int vdDiscardHelper(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges)
 {
     int rc = VINF_SUCCESS;
@@ -1331,9 +1362,9 @@
         pIoCtx->pDisk                 = pDisk;
         pIoCtx->enmTxDir              = enmTxDir;
-        pIoCtx->cbTransferLeft        = cbTransfer;
-        pIoCtx->uOffset               = uOffset;
-        pIoCtx->cbTransfer            = cbTransfer;
-        pIoCtx->pImageStart           = pImageStart;
-        pIoCtx->pImageCur             = pImageStart;
+        pIoCtx->Req.Io.cbTransferLeft = cbTransfer;
+        pIoCtx->Req.Io.uOffset        = uOffset;
+        pIoCtx->Req.Io.cbTransfer     = cbTransfer;
+        pIoCtx->Req.Io.pImageStart    = pImageStart;
+        pIoCtx->Req.Io.pImageCur      = pImageStart;
         pIoCtx->cDataTransfersPending = 0;
         pIoCtx->cMetaTransfersPending = 0;
@@ -1347,7 +1378,7 @@
         /* There is no S/G list for a flush request. */
         if (enmTxDir != VDIOCTXTXDIR_FLUSH)
-            RTSgBufClone(&pIoCtx->SgBuf, pcSgBuf);
+            RTSgBufClone(&pIoCtx->Req.Io.SgBuf, pcSgBuf);
         else
-            memset(&pIoCtx->SgBuf, 0, sizeof(RTSGBUF));
+            memset(&pIoCtx->Req.Io.SgBuf, 0, sizeof(RTSGBUF));
     }
 
@@ -1378,4 +1409,43 @@
 }
 
+DECLINLINE(PVDIOCTX) vdIoCtxDiscardAlloc(PVBOXHDD pDisk, PCRTRANGE paRanges,
+                                         unsigned cRanges,
+                                         PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
+                                         void *pvUser1, void *pvUser2,
+                                         void *pvAllocation,
+                                         PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
+{
+    PVDIOCTX pIoCtx = NULL;
+
+    pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
+    if (RT_LIKELY(pIoCtx))
+    {
+        pIoCtx->pDisk                     = pDisk;
+        pIoCtx->enmTxDir                  = VDIOCTXTXDIR_DISCARD;
+        pIoCtx->cDataTransfersPending     = 0;
+        pIoCtx->cMetaTransfersPending     = 0;
+        pIoCtx->fComplete                 = false;
+        pIoCtx->fBlocked                  = false;
+        pIoCtx->pvAllocation              = pvAllocation;
+        pIoCtx->pfnIoCtxTransfer          = pfnIoCtxTransfer;
+        pIoCtx->pfnIoCtxTransferNext      = NULL;
+        pIoCtx->rcReq                     = VINF_SUCCESS;
+        pIoCtx->Req.Discard.paRanges      = paRanges;
+        pIoCtx->Req.Discard.cRanges       = cRanges;
+        pIoCtx->Req.Discard.idxRange      = 0;
+        pIoCtx->Req.Discard.cbDiscardLeft = 0;
+        pIoCtx->Req.Discard.offCur        = 0;
+        pIoCtx->Req.Discard.cbThisDiscard = 0;
+
+        pIoCtx->pIoCtxParent          = NULL;
+        pIoCtx->Type.Root.pfnComplete = pfnComplete;
+        pIoCtx->Type.Root.pvUser1     = pvUser1;
+        pIoCtx->Type.Root.pvUser2     = pvUser2;
+    }
+
+    LogFlow(("Allocated discard I/O context %#p\n", pIoCtx));
+    return pIoCtx;
+}
+
 DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
                                        uint64_t uOffset, size_t cbTransfer,
@@ -1459,7 +1529,7 @@
     AssertPtr(pIoCtx->pIoCtxParent);
 
-    RTSgBufReset(&pIoCtx->SgBuf);
-    pIoCtx->uOffset        = pIoCtx->Type.Child.uOffsetSaved;
-    pIoCtx->cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
+    RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
+    pIoCtx->Req.Io.uOffset        = pIoCtx->Type.Child.uOffsetSaved;
+    pIoCtx->Req.Io.cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
 }
 
@@ -1501,15 +1571,15 @@
 static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
 {
-    return RTSgBufCopy(&pIoCtxDst->SgBuf, &pIoCtxSrc->SgBuf, cbData);
+    return RTSgBufCopy(&pIoCtxDst->Req.Io.SgBuf, &pIoCtxSrc->Req.Io.SgBuf, cbData);
 }
 
 static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
 {
-    return RTSgBufCmp(&pIoCtx1->SgBuf, &pIoCtx2->SgBuf, cbData);
+    return RTSgBufCmp(&pIoCtx1->Req.Io.SgBuf, &pIoCtx2->Req.Io.SgBuf, cbData);
 }
 
 static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
 {
-    return RTSgBufCopyToBuf(&pIoCtx->SgBuf, pbData, cbData);
+    return RTSgBufCopyToBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
 }
 
@@ -1517,10 +1587,10 @@
 static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
 {
-    return RTSgBufCopyFromBuf(&pIoCtx->SgBuf, pbData, cbData);
+    return RTSgBufCopyFromBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
 }
 
 static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
 {
-    return RTSgBufSet(&pIoCtx->SgBuf, ch, cbData);
+    return RTSgBufSet(&pIoCtx->Req.Io.SgBuf, ch, cbData);
 }
 
@@ -1534,6 +1604,5 @@
     RTCritSectEnter(&pDisk->CritSect);
 
-    if (   !pIoCtx->cbTransferLeft
-        && !pIoCtx->cMetaTransfersPending
+    if (   !pIoCtx->cMetaTransfersPending
         && !pIoCtx->cDataTransfersPending
         && !pIoCtx->pfnIoCtxTransfer)
@@ -1567,4 +1636,5 @@
         /* Call the transfer function advancing to the next while there is no error. */
         while (   pIoCtx->pfnIoCtxTransfer
+               && !pIoCtx->cMetaTransfersPending
                && RT_SUCCESS(rc))
         {
@@ -1582,5 +1652,4 @@
 
     if (   RT_SUCCESS(rc)
-        && !pIoCtx->cbTransferLeft
         && !pIoCtx->cMetaTransfersPending
         && !pIoCtx->cDataTransfersPending)
@@ -1607,6 +1676,6 @@
     RTCritSectLeave(&pDisk->CritSect);
 
-    LogFlowFunc(("pIoCtx=%#p rc=%Rrc cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
-                 pIoCtx, rc, pIoCtx->cbTransferLeft, pIoCtx->cMetaTransfersPending,
+    LogFlowFunc(("pIoCtx=%#p rc=%Rrc cDataTransfersPending=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
+                 pIoCtx, rc, pIoCtx->cDataTransfersPending, pIoCtx->cMetaTransfersPending,
                  pIoCtx->fComplete));
 
@@ -1711,6 +1780,6 @@
 {
     int rc;
-    size_t cbToRead     = pIoCtx->cbTransfer;
-    uint64_t uOffset    = pIoCtx->uOffset;
+    size_t cbToRead     = pIoCtx->Req.Io.cbTransfer;
+    uint64_t uOffset    = pIoCtx->Req.Io.uOffset;
     PVDIMAGE pCurrImage = NULL;
     size_t cbThisRead;
@@ -1719,5 +1788,5 @@
     do
     {
-        pCurrImage = pIoCtx->pImageCur;
+        pCurrImage = pIoCtx->Req.Io.pImageCur;
 
         /* Search for image with allocated block. Do not attempt to read more
@@ -1751,5 +1820,5 @@
             /* No image in the chain contains the data for the block. */
             vdIoCtxSet(pIoCtx, '\0', cbThisRead);
-            ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisRead);
+            ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbThisRead);
             rc = VINF_SUCCESS;
         }
@@ -1774,7 +1843,7 @@
     {
         /* Save the current state. */
-        pIoCtx->uOffset    = uOffset;
-        pIoCtx->cbTransfer = cbToRead;
-        pIoCtx->pImageCur  = pCurrImage ? pCurrImage : pIoCtx->pImageStart;
+        pIoCtx->Req.Io.uOffset    = uOffset;
+        pIoCtx->Req.Io.cbTransfer = cbToRead;
+        pIoCtx->Req.Io.pImageCur  = pCurrImage ? pCurrImage : pIoCtx->Req.Io.pImageStart;
     }
 
@@ -2240,5 +2309,5 @@
     int rc = VINF_SUCCESS;
     PVBOXHDD pDisk = pIoCtx->pDisk;
-    PVDIMAGE pImage = pIoCtx->pImageCur;
+    PVDIMAGE pImage = pIoCtx->Req.Io.pImageCur;
 
     rc = pImage->Backend->pfnAsyncFlush(pImage->pBackendData, pIoCtx);
@@ -2284,4 +2353,5 @@
                     else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
                     {
+                        ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
                         pIoCtx->fBlocked = true;
                     }
@@ -2394,13 +2464,13 @@
 static int vdWriteHelperOptimizedCommitAsync(PVDIOCTX pIoCtx)
 {
-    int rc = VINF_SUCCESS;
-    PVDIMAGE pImage = pIoCtx->pImageStart;
-    size_t cbPreRead      = pIoCtx->Type.Child.cbPreRead;
-    size_t cbPostRead     = pIoCtx->Type.Child.cbPostRead;
-    size_t cbThisWrite    = pIoCtx->Type.Child.cbTransferParent;
+    int rc             = VINF_SUCCESS;
+    PVDIMAGE pImage    = pIoCtx->Req.Io.pImageStart;
+    size_t cbPreRead   = pIoCtx->Type.Child.cbPreRead;
+    size_t cbPostRead  = pIoCtx->Type.Child.cbPostRead;
+    size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
 
     LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
     rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
-                                        pIoCtx->uOffset - cbPreRead,
+                                        pIoCtx->Req.Io.uOffset - cbPreRead,
                                         cbPreRead + cbThisWrite + cbPostRead,
                                         pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
@@ -2423,5 +2493,5 @@
 {
     int rc = VINF_SUCCESS;
-    PVDIMAGE pImage = pIoCtx->pImageCur;
+    PVDIMAGE pImage       = pIoCtx->Req.Io.pImageCur;
     size_t cbThisWrite    = 0;
     size_t cbPreRead      = pIoCtx->Type.Child.cbPreRead;
@@ -2436,25 +2506,25 @@
     AssertPtr(pIoCtxParent);
     Assert(!pIoCtxParent->pIoCtxParent);
-    Assert(!pIoCtx->cbTransferLeft && !pIoCtx->cMetaTransfersPending);
+    Assert(!pIoCtx->Req.Io.cbTransferLeft && !pIoCtx->cMetaTransfersPending);
 
     vdIoCtxChildReset(pIoCtx);
     cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
-    RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
+    RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbPreRead);
 
     /* Check if the write would modify anything in this block. */
-    if (!RTSgBufCmp(&pIoCtx->SgBuf, &pIoCtxParent->SgBuf, cbThisWrite))
+    if (!RTSgBufCmp(&pIoCtx->Req.Io.SgBuf, &pIoCtxParent->Req.Io.SgBuf, cbThisWrite))
     {
         RTSGBUF SgBufSrcTmp;
 
-        RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->SgBuf);
+        RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->Req.Io.SgBuf);
         RTSgBufAdvance(&SgBufSrcTmp, cbThisWrite);
-        RTSgBufAdvance(&pIoCtx->SgBuf, cbThisWrite);
-
-        if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->SgBuf, &SgBufSrcTmp, cbWriteCopy))
+        RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbThisWrite);
+
+        if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->Req.Io.SgBuf, &SgBufSrcTmp, cbWriteCopy))
         {
             /* Block is completely unchanged, so no need to write anything. */
             LogFlowFunc(("Block didn't changed\n"));
-            ASMAtomicWriteU32(&pIoCtx->cbTransferLeft, 0);
-            RTSgBufAdvance(&pIoCtxParent->SgBuf, cbThisWrite);
+            ASMAtomicWriteU32(&pIoCtx->Req.Io.cbTransferLeft, 0);
+            RTSgBufAdvance(&pIoCtxParent->Req.Io.SgBuf, cbThisWrite);
             return VINF_VD_ASYNC_IO_FINISHED;
         }
@@ -2462,6 +2532,6 @@
 
     /* Copy the data to the right place in the buffer. */
-    RTSgBufReset(&pIoCtx->SgBuf);
-    RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
+    RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
+    RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbPreRead);
     vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
 
@@ -2478,6 +2548,6 @@
             RTSGBUF SgBufParentTmp;
 
-            RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->SgBuf);
-            RTSgBufCopy(&pIoCtx->SgBuf, &SgBufParentTmp, cbWriteCopy);
+            RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->Req.Io.SgBuf);
+            RTSgBufCopy(&pIoCtx->Req.Io.SgBuf, &SgBufParentTmp, cbWriteCopy);
         }
 
@@ -2486,5 +2556,5 @@
         if (cbFill)
         {
-            RTSgBufAdvance(&pIoCtx->SgBuf, cbReadImage);
+            RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbReadImage);
             vdIoCtxSet(pIoCtx, '\0', cbFill);
         }
@@ -2492,5 +2562,5 @@
 
     /* Write the full block to the virtual disk. */
-    RTSgBufReset(&pIoCtx->SgBuf);
+    RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
     pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCommitAsync;
 
@@ -2504,9 +2574,9 @@
     LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
 
-    if (pIoCtx->cbTransferLeft)
+    if (pIoCtx->Req.Io.cbTransferLeft)
         rc = vdReadHelperAsync(pIoCtx);
 
     if (   RT_SUCCESS(rc)
-        && (   pIoCtx->cbTransferLeft
+        && (   pIoCtx->Req.Io.cbTransferLeft
             || pIoCtx->cMetaTransfersPending))
         rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
@@ -2563,7 +2633,7 @@
     /* Read the entire data of the block so that we can compare whether it will
      * be modified by the write or not. */
-    pIoCtx->cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
-    pIoCtx->cbTransfer     = pIoCtx->cbTransferLeft;
-    pIoCtx->uOffset       -= cbPreRead;
+    pIoCtx->Req.Io.cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
+    pIoCtx->Req.Io.cbTransfer     = pIoCtx->Req.Io.cbTransferLeft;
+    pIoCtx->Req.Io.uOffset       -= cbPreRead;
 
     /* Next step */
@@ -2579,7 +2649,7 @@
 {
     int rc;
-    size_t cbWrite   = pIoCtx->cbTransfer;
-    uint64_t uOffset = pIoCtx->uOffset;
-    PVDIMAGE pImage  = pIoCtx->pImageCur;
+    size_t cbWrite   = pIoCtx->Req.Io.cbTransfer;
+    uint64_t uOffset = pIoCtx->Req.Io.uOffset;
+    PVDIMAGE pImage  = pIoCtx->Req.Io.pImageCur;
     PVBOXHDD pDisk   = pIoCtx->pDisk;
     unsigned fWrite;
@@ -2589,4 +2659,8 @@
     rc = vdSetModifiedFlagAsync(pDisk, pIoCtx);
     if (RT_FAILURE(rc)) /* Includes I/O in progress. */
+        return rc;
+
+    rc = vdDiscardSetRangeAllocated(pDisk, uOffset, cbWrite);
+    if (RT_FAILURE(rc))
         return rc;
 
@@ -2659,6 +2733,6 @@
                 {
                     LogFlow(("Child write request completed\n"));
-                    Assert(pIoCtx->cbTransferLeft >= cbThisWrite);
-                    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisWrite);
+                    Assert(pIoCtx->Req.Io.cbTransferLeft >= cbThisWrite);
+                    ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbThisWrite);
                     vdIoCtxUnlockDisk(pDisk, pIoCtx, false /* fProcessDeferredReqs*/ );
                     vdIoCtxFree(pDisk, pIoCtxWrite);
@@ -2669,4 +2743,5 @@
                 {
                     LogFlow(("Child write pending\n"));
+                    ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
                     pIoCtx->fBlocked = true;
                     rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
@@ -2708,6 +2783,6 @@
             rc = VINF_SUCCESS;
 
-        pIoCtx->uOffset    = uOffset;
-        pIoCtx->cbTransfer = cbWrite;
+        pIoCtx->Req.Io.uOffset    = uOffset;
+        pIoCtx->Req.Io.cbTransfer = cbWrite;
     }
 
@@ -2722,5 +2797,5 @@
     int rc = VINF_SUCCESS;
     PVBOXHDD pDisk = pIoCtx->pDisk;
-    PVDIMAGE pImage = pIoCtx->pImageCur;
+    PVDIMAGE pImage = pIoCtx->Req.Io.pImageCur;
 
     rc = vdIoCtxLockDisk(pDisk, pIoCtx);
@@ -2735,4 +2810,332 @@
     }
 
+    return rc;
+}
+
+/**
+ * Async discard helper - discards a whole block which is recorded in the block
+ * tree.
+ *
+ * @returns VBox status code.
+ * @param   pIoCtx    The I/O context to operate on.
+ */
+static int vdDiscardWholeBlockAsync(PVDIOCTX pIoCtx)
+{
+    int rc = VINF_SUCCESS;
+    PVBOXHDD pDisk = pIoCtx->pDisk;
+    PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
+    PVDDISCARDBLOCK pBlock = pIoCtx->Req.Discard.pBlock;
+    size_t cbPreAllocated, cbPostAllocated, cbActuallyDiscarded;
+
+    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
+
+    AssertPtr(pBlock);
+
+    rc = pDisk->pLast->Backend->pfnAsyncDiscard(pDisk->pLast->pBackendData, pIoCtx,
+                                                pBlock->Core.Key, pBlock->cbDiscard,
+                                                &cbPreAllocated, &cbPostAllocated,
+                                                &cbActuallyDiscarded, NULL, 0);
+    Assert(rc != VERR_VD_DISCARD_ALIGNMENT_NOT_MET);
+    Assert(!cbPreAllocated);
+    Assert(!cbPostAllocated);
+    Assert(cbActuallyDiscarded == pBlock->cbDiscard || RT_FAILURE(rc));
+
+    /* Remove the block on success. */
+    if (   RT_SUCCESS(rc)
+        || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+    {
+        PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
+        Assert(pBlockRemove == pBlock);
+
+        pDiscard->cbDiscarding -= pBlock->cbDiscard;
+        RTListNodeRemove(&pBlock->NodeLru);
+        RTMemFree(pBlock->pbmAllocated);
+        RTMemFree(pBlock);
+        pIoCtx->Req.Discard.pBlock = NULL;/* Safety precaution. */
+        pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync; /* Next part. */
+        rc = VINF_SUCCESS;
+    }
+
+    LogFlowFunc(("returns rc=%Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Removes the least recently used blocks from the waiting list until
+ * the new value is reached - version for async I/O.
+ *
+ * @returns VBox status code.
+ * @param   pDisk              VD disk container.
+ * @param   pDiscard           The discard state.
+ * @param   cbDiscardingNew    How many bytes should be waiting on success.
+ *                             The number of bytes waiting can be less.
+ */
+static int vdDiscardRemoveBlocksAsync(PVBOXHDD pDisk, PVDIOCTX pIoCtx, size_t cbDiscardingNew)
+{
+    int rc = VINF_SUCCESS;
+    PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
+
+    LogFlowFunc(("pDisk=%#p pDiscard=%#p cbDiscardingNew=%zu\n",
+                 pDisk, pDiscard, cbDiscardingNew));
+
+    while (pDiscard->cbDiscarding > cbDiscardingNew)
+    {
+        PVDDISCARDBLOCK pBlock = RTListGetLast(&pDiscard->ListLru, VDDISCARDBLOCK, NodeLru);
+
+        Assert(!RTListIsEmpty(&pDiscard->ListLru));
+
+        /* Go over the allocation bitmap and mark all discarded sectors as unused. */
+        uint64_t offStart = pBlock->Core.Key;
+        uint32_t idxStart = 0;
+        size_t cbLeft = pBlock->cbDiscard;
+        bool fAllocated = ASMBitTest(pBlock->pbmAllocated, idxStart);
+        uint32_t cSectors = pBlock->cbDiscard / 512;
+
+        while (cbLeft > 0)
+        {
+            int32_t idxEnd;
+            size_t cbThis = cbLeft;
+
+            if (fAllocated)
+            {
+                /* Check for the first unallocated bit. */
+                idxEnd = ASMBitNextClear(pBlock->pbmAllocated, cSectors, idxStart);
+                if (idxEnd != -1)
+                {
+                    cbThis = (idxEnd - idxStart) * 512;
+                    fAllocated = false;
+                }
+            }
+            else
+            {
+                /* Mark as unused and check for the first set bit. */
+                idxEnd = ASMBitNextSet(pBlock->pbmAllocated, cSectors, idxStart);
+                if (idxEnd != -1)
+                    cbThis = (idxEnd - idxStart) * 512;
+
+                rc = pDisk->pLast->Backend->pfnAsyncDiscard(pDisk->pLast->pBackendData, pIoCtx,
+                                                            offStart, cbThis, NULL, NULL, &cbThis,
+                                                            NULL, VD_DISCARD_MARK_UNUSED);
+                if (      RT_FAILURE(rc)
+                    && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+                    break;
+
+                fAllocated = true;
+            }
+
+            idxStart  = idxEnd;
+            offStart += cbThis;
+            cbLeft   -= cbThis;
+        }
+
+        if (   RT_FAILURE(rc)
+            && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+            break;
+
+        PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
+        Assert(pBlockRemove == pBlock);
+        RTListNodeRemove(&pBlock->NodeLru);
+
+        pDiscard->cbDiscarding -= pBlock->cbDiscard;
+        RTMemFree(pBlock->pbmAllocated);
+        RTMemFree(pBlock);
+    }
+
+    if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+        rc = VINF_SUCCESS;
+
+    Assert(RT_FAILURE(rc) || pDiscard->cbDiscarding <= cbDiscardingNew);
+
+    LogFlowFunc(("returns rc=%Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Async discard helper - discards the current range if there is no matching
+ * block in the tree.
+ *
+ * @returns VBox status code.
+ * @param   pIoCtx    The I/O context to operate on.
+ */
+static int vdDiscardCurrentRangeAsync(PVDIOCTX pIoCtx)
+{
+    PVBOXHDD        pDisk         = pIoCtx->pDisk;
+    PVDDISCARDSTATE pDiscard      = pDisk->pDiscard;
+    uint64_t        offStart      = pIoCtx->Req.Discard.offCur;
+    size_t          cbThisDiscard = pIoCtx->Req.Discard.cbThisDiscard;
+    void *pbmAllocated = NULL;
+    size_t cbPreAllocated, cbPostAllocated;
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
+
+    /* No block found, try to discard using the backend first. */
+    rc = pDisk->pLast->Backend->pfnAsyncDiscard(pDisk->pLast->pBackendData, pIoCtx,
+                                                offStart, cbThisDiscard, &cbPreAllocated,
+                                                &cbPostAllocated, &cbThisDiscard,
+                                                &pbmAllocated, 0);
+    if (rc == VERR_VD_DISCARD_ALIGNMENT_NOT_MET)
+    {
+        /* Create new discard block. */
+        PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTMemAllocZ(sizeof(VDDISCARDBLOCK));
+        if (pBlock)
+        {
+            pBlock->Core.Key     = offStart - cbPreAllocated;
+            pBlock->Core.KeyLast = offStart + cbThisDiscard + cbPostAllocated - 1;
+            pBlock->cbDiscard    = cbPreAllocated + cbThisDiscard + cbPostAllocated;
+            pBlock->pbmAllocated = pbmAllocated;
+            bool fInserted = RTAvlrU64Insert(pDiscard->pTreeBlocks, &pBlock->Core);
+            Assert(fInserted);
+
+            RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru);
+            pDiscard->cbDiscarding += pBlock->cbDiscard;
+
+            Assert(pIoCtx->Req.Discard.cbDiscardLeft >= cbThisDiscard);
+            pIoCtx->Req.Discard.cbDiscardLeft -= cbThisDiscard;
+            pIoCtx->Req.Discard.offCur        += cbThisDiscard;
+            pIoCtx->Req.Discard.cbThisDiscard = cbThisDiscard;
+
+            if (pDiscard->cbDiscarding > VD_DISCARD_REMOVE_THRESHOLD)
+                rc = vdDiscardRemoveBlocksAsync(pDisk, pIoCtx, VD_DISCARD_REMOVE_THRESHOLD);
+            else
+                rc = VINF_SUCCESS;
+
+            if (RT_SUCCESS(rc))
+                pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync; /* Next part. */
+        }
+        else
+        {
+            RTMemFree(pbmAllocated);
+            rc = VERR_NO_MEMORY;
+        }
+    }
+    else if (   RT_SUCCESS(rc)
+             || rc == VERR_VD_ASYNC_IO_IN_PROGRESS) /* Save state and andvance to next range. */
+    {
+        Assert(pIoCtx->Req.Discard.cbDiscardLeft >= cbThisDiscard);
+        pIoCtx->Req.Discard.cbDiscardLeft -= cbThisDiscard;
+        pIoCtx->Req.Discard.offCur        += cbThisDiscard;
+        pIoCtx->Req.Discard.cbThisDiscard  = cbThisDiscard;
+        pIoCtx->pfnIoCtxTransferNext       = vdDiscardHelperAsync;
+        rc = VINF_SUCCESS;
+    }
+
+    LogFlowFunc(("returns rc=%Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Async discard helper - entry point.
+ *
+ * @returns VBox status code.
+ * @param   pIoCtx    The I/O context to operate on.
+ */
+static int vdDiscardHelperAsync(PVDIOCTX pIoCtx)
+{
+    int rc             = VINF_SUCCESS;
+    PVBOXHDD  pDisk    = pIoCtx->pDisk;
+    PCRTRANGE paRanges = pIoCtx->Req.Discard.paRanges;
+    unsigned  cRanges  = pIoCtx->Req.Discard.cRanges;
+    PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
+
+    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
+
+    /* Check if the I/O context processed all ranges. */
+    if (   pIoCtx->Req.Discard.idxRange == cRanges
+        && !pIoCtx->Req.Discard.cbDiscardLeft)
+    {
+        LogFlowFunc(("All ranges discarded, completing\n"));
+        vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessDeferredReqs*/);
+        return VINF_SUCCESS;
+    }
+
+    if (pDisk->pIoCtxLockOwner != pIoCtx)
+        rc = vdIoCtxLockDisk(pDisk, pIoCtx);
+
+    if (RT_SUCCESS(rc))
+    {
+        uint64_t offStart      = pIoCtx->Req.Discard.offCur;
+        size_t   cbDiscardLeft = pIoCtx->Req.Discard.cbDiscardLeft;
+        size_t   cbThisDiscard;
+
+        if (RT_UNLIKELY(!pDiscard))
+        {
+            pDiscard = vdDiscardStateCreate();
+            if (!pDiscard)
+                return VERR_NO_MEMORY;
+
+            pDisk->pDiscard = pDiscard;
+        }
+
+        if (!pIoCtx->Req.Discard.cbDiscardLeft)
+        {
+            offStart      = paRanges[pIoCtx->Req.Discard.idxRange].offStart;
+            cbDiscardLeft = paRanges[pIoCtx->Req.Discard.idxRange].cbRange;
+            LogFlowFunc(("New range descriptor loaded (%u) offStart=%llu cbDiscard=%zu\n",
+                         pIoCtx->Req.Discard.idxRange, offStart, cbDiscardLeft));
+            pIoCtx->Req.Discard.idxRange++;
+        }
+
+        /* Look for a matching block in the AVL tree first. */
+        PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, false);
+        if (!pBlock || pBlock->Core.KeyLast < offStart)
+        {
+            PVDDISCARDBLOCK pBlockAbove = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, true);
+
+            /* Clip range to remain in the current block. */
+            if (pBlockAbove)
+                cbThisDiscard = RT_MIN(cbDiscardLeft, pBlockAbove->Core.KeyLast - offStart + 1);
+            else
+                cbThisDiscard = cbDiscardLeft;
+
+            Assert(!(cbThisDiscard % 512));
+            pIoCtx->Req.Discard.pBlock   = NULL;
+            pIoCtx->pfnIoCtxTransferNext = vdDiscardCurrentRangeAsync;
+        }
+        else
+        {
+            /* Range lies partly in the block, update allocation bitmap. */
+            int32_t idxStart, idxEnd;
+
+            cbThisDiscard = RT_MIN(cbDiscardLeft, pBlock->Core.KeyLast - offStart + 1);
+
+            AssertPtr(pBlock);
+
+            Assert(!(cbThisDiscard % 512));
+            Assert(!((offStart - pBlock->Core.Key) % 512));
+
+            idxStart = (offStart - pBlock->Core.Key) / 512;
+            idxEnd = idxStart + (cbThisDiscard / 512);
+
+            ASMBitClearRange(pBlock->pbmAllocated, idxStart, idxEnd);
+
+            cbDiscardLeft -= cbThisDiscard;
+            offStart      += cbThisDiscard;
+
+            /* Call the backend to discard the block if it is completely unallocated now. */
+            if (ASMBitFirstSet((volatile void *)pBlock->pbmAllocated, pBlock->cbDiscard / 512) == -1)
+            {
+                pIoCtx->Req.Discard.pBlock   = pBlock;
+                pIoCtx->pfnIoCtxTransferNext = vdDiscardWholeBlockAsync;
+                rc = VINF_SUCCESS;
+            }
+            else
+            {
+                RTListNodeRemove(&pBlock->NodeLru);
+                RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru);
+
+                /* Start with next range. */
+                pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync;
+                rc = VINF_SUCCESS;
+            }
+        }
+
+        /* Save state in the context. */
+        pIoCtx->Req.Discard.offCur        = offStart;
+        pIoCtx->Req.Discard.cbDiscardLeft = cbDiscardLeft;
+        pIoCtx->Req.Discard.cbThisDiscard = cbThisDiscard;
+    }
+
+    LogFlowFunc(("returns rc=%Rrc\n", rc));
     return rc;
 }
@@ -3160,4 +3563,6 @@
                     ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
 
+                ASMAtomicDecU32(&pIoCtxParent->cDataTransfersPending);
+
                 if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
                 {
@@ -3166,6 +3571,6 @@
 
                     /* Update the parent state. */
-                    Assert(pIoCtxParent->cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
-                    ASMAtomicSubU32(&pIoCtxParent->cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
+                    Assert(pIoCtxParent->Req.Io.cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
+                    ASMAtomicSubU32(&pIoCtxParent->Req.Io.cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
                 }
                 else
@@ -3251,5 +3656,6 @@
                     vdThreadFinishWrite(pDisk);
                 }
-                else if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
+                else if (   pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE
+                         || pIoCtx->enmTxDir == VDIOCTXTXDIR_DISCARD)
                     vdThreadFinishWrite(pDisk);
                 else
@@ -3289,6 +3695,6 @@
 
     RTCritSectEnter(&pDisk->CritSect);
-    Assert(pIoCtx->cbTransferLeft >= cbTransfer);
-    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTransfer);
+    Assert(pIoCtx->Req.Io.cbTransferLeft >= cbTransfer);
+    ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbTransfer);
     ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
 
@@ -3565,5 +3971,5 @@
         size_t   cbTaskRead = 0;
 
-        cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbRead);
+        cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, aSeg, &cSegments, cbRead);
 
         Assert(cSegments > 0);
@@ -3593,6 +3999,6 @@
         if (RT_SUCCESS(rc))
         {
-            AssertMsg(cbTaskRead <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
-            ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskRead);
+            AssertMsg(cbTaskRead <= pIoCtx->Req.Io.cbTransferLeft, ("Impossible!\n"));
+            ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbTaskRead);
             ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
             vdIoTaskFree(pDisk, pIoTask);
@@ -3637,5 +4043,5 @@
         size_t   cbTaskWrite = 0;
 
-        cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbWrite);
+        cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, aSeg, &cSegments, cbWrite);
 
         Assert(cSegments > 0);
@@ -3665,6 +4071,6 @@
         if (RT_SUCCESS(rc))
         {
-            AssertMsg(cbTaskWrite <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
-            ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskWrite);
+            AssertMsg(cbTaskWrite <= pIoCtx->Req.Io.cbTransferLeft, ("Impossible!\n"));
+            ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbTaskWrite);
             ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
             vdIoTaskFree(pDisk, pIoTask);
@@ -3969,5 +4375,5 @@
     Assert(cbCopied == cbBuf);
 
-    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
+    ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbCopied);
 
     return cbCopied;
@@ -3986,5 +4392,5 @@
     Assert(cbCopied == cbBuf);
 
-    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
+    ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbCopied);
 
     return cbCopied;
@@ -4002,5 +4408,5 @@
     Assert(cbSet == cb);
 
-    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbSet);
+    ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbSet);
 
     return cbSet;
@@ -4017,5 +4423,5 @@
     VD_THREAD_IS_CRITSECT_OWNER(pDisk);
 
-    cbCreated = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, paSeg, pcSeg, cbData);
+    cbCreated = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, paSeg, pcSeg, cbData);
     Assert(!paSeg || cbData == cbCreated);
 
@@ -4043,9 +4449,9 @@
     /* Continue */
     pIoCtx->fBlocked = false;
-    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCompleted);
+    ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbCompleted);
 
     /* Clear the pointer to next transfer function in case we have nothing to transfer anymore.
      * @todo: Find a better way to prevent vdIoCtxContinue from calling the read/write helper again. */
-    if (!pIoCtx->cbTransferLeft)
+    if (!pIoCtx->Req.Io.cbTransferLeft)
         pIoCtx->pfnIoCtxTransfer = NULL;
 
@@ -8617,5 +9023,5 @@
 
 
-VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCVDRANGE paRanges, unsigned cRanges)
+VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges)
 {
     int rc;
@@ -8861,2 +9267,57 @@
 }
 
+VBOXDDU_DECL(int) VDAsyncDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges,
+                                       PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
+                                       void *pvUser1, void *pvUser2)
+{
+    int rc;
+    int rc2;
+    bool fLockWrite = false;
+    PVDIOCTX pIoCtx = NULL;
+
+    LogFlowFunc(("pDisk=%#p\n", pDisk));
+
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
+
+        pIoCtx = vdIoCtxDiscardAlloc(pDisk, paRanges, cRanges,
+                                     pfnComplete, pvUser1, pvUser2, NULL,
+                                     vdDiscardHelperAsync);
+        if (!pIoCtx)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        rc = vdIoCtxProcess(pIoCtx);
+        if (rc == VINF_VD_ASYNC_IO_FINISHED)
+        {
+            if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
+                vdIoCtxFree(pDisk, pIoCtx);
+            else
+                rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
+        }
+        else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
+            vdIoCtxFree(pDisk, pIoCtx);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite) && (   rc == VINF_VD_ASYNC_IO_FINISHED
+                                    || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
Index: /trunk/src/VBox/Storage/VDI.cpp
===================================================================
--- /trunk/src/VBox/Storage/VDI.cpp	(revision 38875)
+++ /trunk/src/VBox/Storage/VDI.cpp	(revision 38876)
@@ -57,5 +57,6 @@
 static int  vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
 static int  vdiUpdateHeaderAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx);
-static int  vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock, PVDIOCTX pIoCtx);
+static int  vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock, PVDIOCTX pIoCtx,
+                                    bool fUpdateHdr);
 
 /**
@@ -849,8 +850,12 @@
  */
 static int vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock,
-                                   PVDIOCTX pIoCtx)
-{
+                                   PVDIOCTX pIoCtx, bool fUpdateHdr)
+{
+    int rc = VINF_SUCCESS;
+
     /* Update image header. */
-    int rc = vdiUpdateHeaderAsync(pImage, pIoCtx);
+    if (fUpdateHdr)
+        rc = vdiUpdateHeaderAsync(pImage, pIoCtx);
+
     if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
     {
@@ -964,4 +969,154 @@
         rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, cbImage - pImage->cbTotalBlockData);
     } while (0);
+
+    LogFlowFunc(("returns rc=%Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Completion callback for meta/userdata reads or writes.
+ *
+ * @return  VBox status code.
+ *          VINF_SUCCESS if everything was successful and the transfer can continue.
+ *          VERR_VD_ASYNC_IO_IN_PROGRESS if there is another data transfer pending.
+ * @param   pBackendData    The opaque backend data.
+ * @param   pIoCtx          I/O context associated with this request.
+ * @param   pvUser          Opaque user data passed during a read/write request.
+ * @param   rcReq           Status code for the completed request.
+ */
+static DECLCALLBACK(int) vdiDiscardBlockAsyncUpdate(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
+{
+    int rc = VINF_SUCCESS;
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    PVDIBLOCKDISCARDASYNC pDiscardAsync = (PVDIBLOCKDISCARDASYNC)pvUser;
+
+    switch (pDiscardAsync->enmState)
+    {
+        case VDIBLOCKDISCARDSTATE_READ_BLOCK:
+        {
+            PVDMETAXFER pMetaXfer;
+            uint64_t u64Offset = (uint64_t)pDiscardAsync->idxLastBlock * pImage->cbTotalBlockData + pImage->offStartData;
+            rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
+                                            pDiscardAsync->pvBlock, pImage->cbTotalBlockData, pIoCtx,
+                                            &pMetaXfer, vdiDiscardBlockAsyncUpdate, pDiscardAsync);
+            if (RT_FAILURE(rc))
+                break;
+
+            /* Release immediately and go to next step. */
+            vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
+            pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_WRITE_BLOCK;
+        }
+        case VDIBLOCKDISCARDSTATE_WRITE_BLOCK:
+        {
+            /* Block read complete. Write to the new location (discarded block). */
+            uint64_t u64Offset = (uint64_t)pDiscardAsync->ptrBlockDiscard * pImage->cbTotalBlockData + pImage->offStartData;
+            rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
+                                             pDiscardAsync->pvBlock, pImage->cbTotalBlockData, pIoCtx,
+                                             vdiDiscardBlockAsyncUpdate, pDiscardAsync);
+
+            pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_UPDATE_METADATA;
+            if (RT_FAILURE(rc))
+                break;
+        }
+        case VDIBLOCKDISCARDSTATE_UPDATE_METADATA:
+        {
+            int rc2;
+            uint64_t cbImage;
+
+            /* Block write complete. Update metadata. */
+            pImage->paBlocksRev[pDiscardAsync->idxLastBlock] = VDI_IMAGE_BLOCK_FREE;
+            pImage->paBlocks[pDiscardAsync->uBlock] = VDI_IMAGE_BLOCK_ZERO;
+
+            if (pDiscardAsync->idxLastBlock != pDiscardAsync->ptrBlockDiscard)
+            {
+                pImage->paBlocks[pDiscardAsync->uBlockLast] = pDiscardAsync->ptrBlockDiscard;
+                pImage->paBlocksRev[pDiscardAsync->ptrBlockDiscard] = pDiscardAsync->uBlockLast;
+
+                rc = vdiUpdateBlockInfoAsync(pImage, pDiscardAsync->uBlockLast, pIoCtx, false /* fUpdateHdr */);
+                if (   RT_FAILURE(rc)
+                    && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+                    break;
+            }
+
+            setImageBlocksAllocated(&pImage->Header, pDiscardAsync->idxLastBlock);
+            rc = vdiUpdateBlockInfoAsync(pImage, pDiscardAsync->uBlock, pIoCtx, true /* fUpdateHdr */);
+            if (   RT_FAILURE(rc)
+                && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+                break;
+
+            /* Set new file size. */
+            rc2 = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbImage);
+            if (RT_FAILURE(rc2))
+                break;
+
+            LogFlowFunc(("Set new size %llu\n", cbImage - pImage->cbTotalBlockData));
+            rc2 = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, cbImage - pImage->cbTotalBlockData);
+            if (RT_FAILURE(rc2))
+                rc = rc2;
+
+            /* Free discard state. */
+            RTMemFree(pDiscardAsync->pvBlock);
+            RTMemFree(pDiscardAsync);
+            break;
+        }
+        default:
+            AssertMsgFailed(("Invalid state %d\n", pDiscardAsync->enmState));
+    }
+
+    if (rc == VERR_VD_NOT_ENOUGH_METADATA)
+        rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+
+    return rc;
+}
+
+/**
+ * Internal: Discard a whole block from the image filling the created hole with
+ * data from another block - async I/O version.
+ *
+ * @returns VBox status code.
+ * @param   pImage    VDI image instance data.
+ * @param   pIoCtx    I/O context associated with this request.
+ * @param   uBlock    The block to discard.
+ * @param   pvBlock   Memory to use for the I/O.
+ */
+static int vdiDiscardBlockAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx,
+                                unsigned uBlock, void *pvBlock)
+{
+    int rc = VINF_SUCCESS;
+    PVDIBLOCKDISCARDASYNC pDiscardAsync = NULL;
+
+    LogFlowFunc(("pImage=%#p uBlock=%u pvBlock=%#p\n",
+                 pImage, uBlock, pvBlock));
+
+    pDiscardAsync = (PVDIBLOCKDISCARDASYNC)RTMemAllocZ(sizeof(VDIBLOCKDISCARDASYNC));
+    if (RT_UNLIKELY(!pDiscardAsync))
+        return VERR_NO_MEMORY;
+
+    /* Init block discard state. */
+    pDiscardAsync->uBlock  = uBlock;
+    pDiscardAsync->pvBlock = pvBlock;
+    pDiscardAsync->ptrBlockDiscard = pImage->paBlocks[uBlock];
+    pDiscardAsync->idxLastBlock = getImageBlocksAllocated(&pImage->Header) - 1;
+    pDiscardAsync->uBlockLast = pImage->paBlocksRev[pDiscardAsync->idxLastBlock];
+
+    /*
+     * The block is empty, remove it.
+     * Read the last block of the image first.
+     */
+    if (pDiscardAsync->idxLastBlock != pDiscardAsync->ptrBlockDiscard)
+    {
+        LogFlowFunc(("Moving block [%u]=%u into [%u]=%u\n",
+                     pDiscardAsync->uBlockLast, pDiscardAsync->idxLastBlock,
+                     uBlock, pImage->paBlocks[uBlock]));
+        pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_READ_BLOCK;
+    }
+    else
+    {
+        pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_UPDATE_METADATA; /* Start immediately to shrink the image. */
+        LogFlowFunc(("Discard last block [%u]=%u\n", uBlock, pImage->paBlocks[uBlock]));
+    }
+
+    /* Call the update callback directly. */
+    rc = vdiDiscardBlockAsyncUpdate(pImage, pIoCtx, pDiscardAsync, VINF_SUCCESS);
 
     LogFlowFunc(("returns rc=%Rrc\n", rc));
@@ -2181,5 +2336,5 @@
                 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
 
-                rc = vdiUpdateBlockInfoAsync(pImage, uBlock, pIoCtx);
+                rc = vdiUpdateBlockInfoAsync(pImage, uBlock, pIoCtx, true /* fUpdateHdr */);
                 if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
                     goto out;
@@ -2780,4 +2935,144 @@
     if (pvBlock)
         RTMemFree(pvBlock);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnDiscard */
+static DECLCALLBACK(int) vdiAsyncDiscard(void *pBackendData, PVDIOCTX pIoCtx,
+                                         uint64_t uOffset, size_t cbDiscard,
+                                         size_t *pcbPreAllocated,
+                                         size_t *pcbPostAllocated,
+                                         size_t *pcbActuallyDiscarded,
+                                         void   **ppbmAllocationBitmap,
+                                         unsigned fDiscard)
+{
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    unsigned uBlock;
+    unsigned offDiscard;
+    int rc = VINF_SUCCESS;
+    void *pvBlock = NULL;
+
+    LogFlowFunc(("pBackendData=%#p pIoCtx=%#p uOffset=%llu cbDiscard=%zu pcbPreAllocated=%#p pcbPostAllocated=%#p pcbActuallyDiscarded=%#p ppbmAllocationBitmap=%#p fDiscard=%#x\n",
+                 pBackendData, pIoCtx, uOffset, cbDiscard, pcbPreAllocated, pcbPostAllocated, pcbActuallyDiscarded, ppbmAllocationBitmap, fDiscard));
+
+    AssertPtr(pImage);
+    Assert(!(uOffset % 512));
+    Assert(!(cbDiscard % 512));
+
+    AssertMsgReturn(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
+                    ("Image is readonly\n"), VERR_VD_IMAGE_READ_ONLY);
+    AssertMsgReturn(   uOffset + cbDiscard <= getImageDiskSize(&pImage->Header)
+                    && cbDiscard,
+                    ("Invalid parameters uOffset=%llu cbDiscard=%zu\n",
+                     uOffset, cbDiscard),
+                     VERR_INVALID_PARAMETER);
+
+    do
+    {
+        AssertMsgBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
+                           ("Image is opened readonly\n"),
+                           rc = VERR_VD_IMAGE_READ_ONLY);
+
+        AssertMsgBreakStmt(cbDiscard,
+                           ("cbDiscard=%u\n", cbDiscard),
+                           rc = VERR_INVALID_PARAMETER);
+
+        /* Calculate starting block number and offset inside it. */
+        uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
+        offDiscard = (unsigned)uOffset & pImage->uBlockMask;
+
+        /* Clip range to at most the rest of the block. */
+        cbDiscard = RT_MIN(cbDiscard, getImageBlockSize(&pImage->Header) - offDiscard);
+        Assert(!(cbDiscard % 512));
+
+        if (pcbPreAllocated)
+            *pcbPreAllocated = 0;
+
+        if (pcbPostAllocated)
+            *pcbPostAllocated = 0;
+
+        if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
+        {
+            uint8_t *pbBlockData;
+            size_t cbPreAllocated, cbPostAllocated;
+
+            cbPreAllocated = offDiscard % getImageBlockSize(&pImage->Header);
+            cbPostAllocated = getImageBlockSize(&pImage->Header) - cbDiscard - cbPreAllocated;
+
+            /* Read the block data. */
+            pvBlock = RTMemAlloc(pImage->cbTotalBlockData);
+            if (!pvBlock)
+            {
+                rc = VERR_NO_MEMORY;
+                break;
+            }
+
+            if (!cbPreAllocated && !cbPostAllocated)
+            {
+                /*
+                 * Discarding a whole block, don't check for allocated sectors.
+                 * It is possible to just remove the whole block which avoids
+                 * one read and checking the whole block for data.
+                 */
+                rc = vdiDiscardBlockAsync(pImage, pIoCtx, uBlock, pvBlock);
+            }
+            else if (fDiscard & VD_DISCARD_MARK_UNUSED)
+            {
+                /* Just zero out the given range. */
+                memset(pvBlock, 0, cbDiscard);
+
+                uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData + offDiscard;
+                rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
+                                                 u64Offset, pvBlock, cbDiscard, pIoCtx,
+                                                 NULL, NULL);
+                RTMemFree(pvBlock);
+            }
+            else
+            {
+                /*
+                 * Read complete block as metadata, the I/O context has no memory buffer
+                 * and we need to access the content directly anyway.
+                 */
+                PVDMETAXFER pMetaXfer;
+                pbBlockData = (uint8_t *)pvBlock + pImage->offStartBlockData;
+
+                uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData;
+                rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
+                                                pbBlockData, pImage->cbTotalBlockData,
+                                                pIoCtx, &pMetaXfer, NULL, NULL);
+                if (RT_FAILURE(rc))
+                    break;
+
+                vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
+
+                /* Clear data. */
+                memset(pbBlockData + offDiscard , 0, cbDiscard);
+
+                Assert(!(cbDiscard % 4));
+                Assert(getImageBlockSize(&pImage->Header) * 8 <= UINT32_MAX);
+                if (ASMBitFirstSet((volatile void *)pbBlockData, getImageBlockSize(&pImage->Header) * 8) == -1)
+                    rc = vdiDiscardBlockAsync(pImage, pIoCtx, uBlock, pvBlock);
+                else
+                {
+                    /* Block has data, create allocation bitmap. */
+                    *pcbPreAllocated = cbPreAllocated;
+                    *pcbPostAllocated = cbPostAllocated;
+                    *ppbmAllocationBitmap = vdiAllocationBitmapCreate(pbBlockData, getImageBlockSize(&pImage->Header));
+                    if (RT_UNLIKELY(!*ppbmAllocationBitmap))
+                        rc = VERR_NO_MEMORY;
+                    else
+                        rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET;
+
+                    RTMemFree(pvBlock);
+                }
+            } /* if: no complete block discarded */
+        } /* if: Block is allocated. */
+        /* else: nothing to do. */
+    } while (0);
+
+    if (pcbActuallyDiscarded)
+        *pcbActuallyDiscarded = cbDiscard;
 
     LogFlowFunc(("returns %Rrc\n", rc));
@@ -2883,4 +3178,6 @@
     vdiResize,
     /* pfnDiscard */
-    vdiDiscard
+    vdiDiscard,
+    /* pfnAsyncDiscard */
+    vdiAsyncDiscard
 };
Index: /trunk/src/VBox/Storage/VDICore.h
===================================================================
--- /trunk/src/VBox/Storage/VDICore.h	(revision 38875)
+++ /trunk/src/VBox/Storage/VDICore.h	(revision 38876)
@@ -556,4 +556,40 @@
 } VDIIMAGEDESC, *PVDIIMAGEDESC;
 
+/**
+ * Async block discard states.
+ */
+typedef enum VDIBLOCKDISCARDSTATE
+{
+    /** Invalid. */
+    VDIBLOCKDISCARDSTATE_INVALID = 0,
+    /** Read the last block. */
+    VDIBLOCKDISCARDSTATE_READ_BLOCK,
+    /** Write block into the hole. */
+    VDIBLOCKDISCARDSTATE_WRITE_BLOCK,
+    /** Update metadata. */
+    VDIBLOCKDISCARDSTATE_UPDATE_METADATA,
+    /** 32bit hack. */
+    VDIBLOCKDISCARDSTATE_32BIT_HACK = 0x7fffffff
+} VDIBLOCKDISCARDSTATE;
+
+/**
+ * Async block discard structure.
+ */
+typedef struct VDIBLOCKDISCARDASYNC
+{
+    /** State of the block discard. */
+    VDIBLOCKDISCARDSTATE    enmState;
+    /** Pointer to the block data. */
+    void                   *pvBlock;
+    /** Block index in the block table. */
+    unsigned                uBlock;
+    /** Block pointer to the block to discard. */
+    VDIIMAGEBLOCKPOINTER    ptrBlockDiscard;
+    /** Index of the last block in the reverse block table. */
+    unsigned                idxLastBlock;
+    /** Index of the last block in the block table (gathered from the reverse block table). */
+    unsigned                uBlockLast;
+} VDIBLOCKDISCARDASYNC, *PVDIBLOCKDISCARDASYNC;
+
 #endif
 
Index: /trunk/src/VBox/Storage/VHD.cpp
===================================================================
--- /trunk/src/VBox/Storage/VHD.cpp	(revision 38875)
+++ /trunk/src/VBox/Storage/VHD.cpp	(revision 38876)
@@ -3141,4 +3141,6 @@
     vhdResize,
     /* pfnDiscard */
+    NULL,
+    /* pfnAsyncDiscard */
     NULL
 };
Index: /trunk/src/VBox/Storage/VMDK.cpp
===================================================================
--- /trunk/src/VBox/Storage/VMDK.cpp	(revision 38875)
+++ /trunk/src/VBox/Storage/VMDK.cpp	(revision 38876)
@@ -7192,4 +7192,6 @@
     NULL,
     /* pfnDiscard */
+    NULL,
+    /* pfnAsyncDiscard */
     NULL
 };
Index: /trunk/src/VBox/Storage/testcase/tstVDIo.cpp
===================================================================
--- /trunk/src/VBox/Storage/testcase/tstVDIo.cpp	(revision 38875)
+++ /trunk/src/VBox/Storage/testcase/tstVDIo.cpp	(revision 38876)
@@ -134,5 +134,6 @@
     VDIOREQTXDIR_READ = 0,
     VDIOREQTXDIR_WRITE,
-    VDIOREQTXDIR_FLUSH
+    VDIOREQTXDIR_FLUSH,
+    VDIOREQTXDIR_DISCARD
 } VDIOREQTXDIR;
 
@@ -143,15 +144,15 @@
 {
     /** Transfer type. */
-    VDIOREQTXDIR enmTxDir;
+    VDIOREQTXDIR  enmTxDir;
     /** slot index. */
-    unsigned  idx;
+    unsigned      idx;
     /** Start offset. */
-    uint64_t  off;
+    uint64_t      off;
     /** Size to transfer. */
-    size_t    cbReq;
+    size_t        cbReq;
     /** S/G Buffer */
-    RTSGBUF   SgBuf;
+    RTSGBUF       SgBuf;
     /** Data segment */
-    RTSGSEG   DataSeg;
+    RTSGSEG       DataSeg;
     /** Flag whether the request is outstanding or not. */
     volatile bool fOutstanding;
@@ -385,6 +386,6 @@
     /* pcszName    chId enmType                          fFlags */
     {"disk",       'd', VDSCRIPTARGTYPE_STRING,          VDSCRIPTARGDESC_FLAG_MANDATORY},
-    {"off",        'o', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
-    {"size",       's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
+    {"async",      'a', VDSCRIPTARGTYPE_BOOL,            0},
+    {"ranges",     'r', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
 };
 
@@ -955,4 +956,6 @@
                                             break;
                                         }
+                                        case VDIOREQTXDIR_DISCARD:
+                                            AssertMsgFailed(("Invalid\n"));
                                     }
 
@@ -983,4 +986,6 @@
                                             break;
                                         }
+                                        case VDIOREQTXDIR_DISCARD:
+                                            AssertMsgFailed(("Invalid\n"));
                                     }
 
@@ -1028,4 +1033,6 @@
                                             case VDIOREQTXDIR_FLUSH:
                                                 break;
+                                            case VDIOREQTXDIR_DISCARD:
+                                                AssertMsgFailed(("Invalid\n"));
                                         }
 
@@ -1264,6 +1271,6 @@
     const char *pcszDisk = NULL;
     PVDDISK pDisk = NULL;
-    uint64_t off;
-    size_t cbDiscard;
+    bool fAsync = false;
+    const char *pcszRanges = NULL;
 
     for (unsigned i = 0; i < cScriptArgs; i++)
@@ -1276,55 +1283,218 @@
                 break;
             }
-            case 'o':
-            {
-                off = paScriptArgs[i].u.u64;
-                break;
-            }
-            case 's':
-            {
-                cbDiscard = paScriptArgs[i].u.u64;
-                break;
-            }
-
+            case 'a':
+            {
+                fAsync = paScriptArgs[i].u.fFlag;
+                break;
+            }
+            case 'r':
+            {
+                pcszRanges = paScriptArgs[i].u.pcszString;
+                break;
+            }
             default:
                 AssertMsgFailed(("Invalid argument given!\n"));
         }
-
-        if (RT_FAILURE(rc))
-            break;
-    }
-
-    if (RT_SUCCESS(rc))
-    {
-        pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
-        if (!pDisk)
-            rc = VERR_NOT_FOUND;
-        else
-        {
-            VDRANGE Range;
-
-            Range.offStart = off;
-            Range.cbRange = cbDiscard;
-
-            rc = VDDiscardRanges(pDisk->pVD, &Range, 1);
+    }
+
+    pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
+    if (!pDisk)
+        rc = VERR_NOT_FOUND;
+    else
+    {
+        unsigned cRanges = 0;
+        PRTRANGE paRanges = NULL;
+
+        /*
+         * Parse the range string which should look like this:
+         * n,off1,cb1,off2,cb2,...
+         *
+         * <n> gives the number of ranges in the string and every off<i>,cb<i>
+         * pair afterwards is a start offset + number of bytes to discard entry.
+         */
+        do
+        {
+            rc = RTStrToUInt32Ex(pcszRanges, (char **)&pcszRanges, 10, &cRanges);
+            if (RT_FAILURE(rc) && (rc != VWRN_TRAILING_CHARS))
+                break;
+
+            if (!cRanges)
+            {
+                rc = VERR_INVALID_PARAMETER;
+                break;
+            }
+
+            paRanges = (PRTRANGE)RTMemAllocZ(cRanges * sizeof(RTRANGE));
+            if (!paRanges)
+            {
+                rc = VERR_NO_MEMORY;
+                break;
+            }
+
+            if (*pcszRanges != ',')
+            {
+                rc = VERR_INVALID_PARAMETER;
+                break;
+            }
+
+            pcszRanges++;
+
+            /* Retrieve each pair from the string. */
+            for (unsigned i = 0; i < cRanges; i++)
+            {
+                uint64_t off;
+                uint32_t cb;
+
+                rc = RTStrToUInt64Ex(pcszRanges, (char **)&pcszRanges, 10, &off);
+                if (RT_FAILURE(rc) && (rc != VWRN_TRAILING_CHARS))
+                    break;
+
+                if (*pcszRanges != ',')
+                {
+                    switch (*pcszRanges)
+                    {
+                        case 'k':
+                        case 'K':
+                        {
+                            off *= _1K;
+                            break;
+                        }
+                        case 'm':
+                        case 'M':
+                        {
+                            off *= _1M;
+                            break;
+                        }
+                        case 'g':
+                        case 'G':
+                        {
+                            off *= _1G;
+                            break;
+                        }
+                        default:
+                        {
+                            RTPrintf("Invalid size suffix '%s'\n", pcszRanges);
+                            rc = VERR_INVALID_PARAMETER;
+                        }
+                    }
+                    if (RT_SUCCESS(rc))
+                        pcszRanges++;
+                }
+
+                if (*pcszRanges != ',')
+                {
+                    rc = VERR_INVALID_PARAMETER;
+                    break;
+                }
+
+                pcszRanges++;
+
+                rc = RTStrToUInt32Ex(pcszRanges, (char **)&pcszRanges, 10, &cb);
+                if (RT_FAILURE(rc) && (rc != VWRN_TRAILING_CHARS))
+                    break;
+
+                if (*pcszRanges != ',')
+                {
+                    switch (*pcszRanges)
+                    {
+                        case 'k':
+                        case 'K':
+                        {
+                            cb *= _1K;
+                            break;
+                        }
+                        case 'm':
+                        case 'M':
+                        {
+                            cb *= _1M;
+                            break;
+                        }
+                        case 'g':
+                        case 'G':
+                        {
+                            cb *= _1G;
+                            break;
+                        }
+                        default:
+                        {
+                            RTPrintf("Invalid size suffix '%s'\n", pcszRanges);
+                            rc = VERR_INVALID_PARAMETER;
+                        }
+                    }
+                    if (RT_SUCCESS(rc))
+                        pcszRanges++;
+                }
+
+                if (   *pcszRanges != ','
+                    && !(i == cRanges - 1 && *pcszRanges == '\0'))
+                {
+                    rc = VERR_INVALID_PARAMETER;
+                    break;
+                }
+
+                pcszRanges++;
+
+                paRanges[i].offStart = off;
+                paRanges[i].cbRange  = cb;
+            }
+        } while (0);
+
+        if (RT_SUCCESS(rc))
+        {
+            if (!fAsync)
+                rc = VDDiscardRanges(pDisk->pVD, paRanges, cRanges);
+            else
+            {
+                VDIOREQ IoReq;
+                RTSEMEVENT EventSem;
+
+                rc = RTSemEventCreate(&EventSem);
+                if (RT_SUCCESS(rc))
+                {
+                    memset(&IoReq, 0, sizeof(VDIOREQ));
+                    IoReq.enmTxDir = VDIOREQTXDIR_FLUSH;
+                    IoReq.pvUser   = pDisk;
+                    IoReq.idx      = 0;
+                    rc = VDAsyncDiscardRanges(pDisk->pVD, paRanges, cRanges, tstVDIoTestReqComplete, &IoReq, EventSem);
+                    if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+                    {
+                        rc = RTSemEventWait(EventSem, RT_INDEFINITE_WAIT);
+                        AssertRC(rc);
+                    }
+                    else if (rc == VINF_VD_ASYNC_IO_FINISHED)
+                        rc = VINF_SUCCESS;
+
+                    RTSemEventDestroy(EventSem);
+                }
+            }
+
             if (   RT_SUCCESS(rc)
                 && pDisk->pMemDiskVerify)
             {
-                void *pv = RTMemAllocZ(cbDiscard);
-                if (pv)
+                for (unsigned i = 0; i < cRanges; i++)
                 {
-                    RTSGSEG SgSeg;
-                    RTSGBUF SgBuf;
-
-                    SgSeg.pvSeg = pv;
-                    SgSeg.cbSeg = cbDiscard;
-                    RTSgBufInit(&SgBuf, &SgSeg, 1);
-                    rc = VDMemDiskWrite(pDisk->pMemDiskVerify, off, cbDiscard, &SgBuf);
-                    RTMemFree(pv);
+                    void *pv = RTMemAllocZ(paRanges[i].cbRange);
+                    if (pv)
+                    {
+                        RTSGSEG SgSeg;
+                        RTSGBUF SgBuf;
+
+                        SgSeg.pvSeg = pv;
+                        SgSeg.cbSeg = paRanges[i].cbRange;
+                        RTSgBufInit(&SgBuf, &SgSeg, 1);
+                        rc = VDMemDiskWrite(pDisk->pMemDiskVerify, paRanges[i].offStart, paRanges[i].cbRange, &SgBuf);
+                        RTMemFree(pv);
+                    }
+                    else
+                    {
+                        rc = VERR_NO_MEMORY;
+                        break;
+                    }
                 }
-                else
-                    rc = VERR_NO_MEMORY;
-            }
-        }
+            }
+        }
+
+        if (paRanges)
+            RTMemFree(paRanges);
     }
 
@@ -1647,5 +1817,5 @@
                     case VDDBGIOLOGREQ_DISCARD:
                     {
-                        PVDRANGE paRanges = NULL;
+                        PRTRANGE paRanges = NULL;
                         unsigned cRanges = 0;
 
@@ -2726,4 +2896,5 @@
             }
             case VDIOREQTXDIR_FLUSH:
+            case VDIOREQTXDIR_DISCARD:
                 break;
         }
