VirtualBox

Changeset 38876 in vbox


Ignore:
Timestamp:
Sep 27, 2011 9:03:15 AM (14 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
74198
Message:

Storage: Add async discard API

Location:
trunk
Files:
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/VBox/vd-plugin.h

    r38621 r38876  
    601601                                           unsigned fDiscard));
    602602
     603    /**
     604     * Discards the given amount of bytes decreasing the size of the image if possible
     605     * callback version for asynchronous I/O.
     606     *
     607     * @returns VBox status code.
     608     * @retval  VERR_VD_DISCARD_ALIGNMENT_NOT_MET if the range doesn't meet the required alignment
     609     *          for the discard.
     610     * @param   pBackendData         Opaque state data for this image.
     611     * @param   pIoCtx               I/O context associated with this request.
     612     * @param   uOffset              The offset of the first byte to discard.
     613     * @param   cbDiscard            How many bytes to discard.
     614     * @param   pcbPreAllocated      Pointer to the returned amount of bytes that must
     615     *                               be discarded before the range to perform a full
     616     *                               block discard.
     617     * @param   pcbPostAllocated     Pointer to the returned amount of bytes that must
     618     *                               be discarded after the range to perform a full
     619     *                               block discard.
     620     * @param   pcbActuallyDiscarded Pointer to the returned amount of bytes which
     621     *                               could be actually discarded.
     622     * @param   ppbmAllocationBitmap Where to store the pointer to the allocation bitmap
     623     *                               if VERR_VD_DISCARD_ALIGNMENT_NOT_MET is returned or NULL
     624     *                               if the allocation bitmap should be returned.
     625     * @param   fDiscard             Flags which affect discard behavior. Combination
     626     *                               of the VD_DISCARD_* flags.
     627     */
     628    DECLR3CALLBACKMEMBER(int, pfnAsyncDiscard, (void *pBackendData, PVDIOCTX pIoCtx,
     629                                                uint64_t uOffset, size_t cbDiscard,
     630                                                size_t *pcbPreAllocated,
     631                                                size_t *pcbPostAllocated,
     632                                                size_t *pcbActuallyDiscarded,
     633                                                void   **ppbmAllocationBitmap,
     634                                                unsigned fDiscard));
     635
    603636} VBOXHDDBACKEND;
    604637
  • trunk/include/VBox/vd.h

    r38621 r38876  
    398398
    399399/**
    400  * VD range descriptor.
    401  */
    402 typedef struct VDRANGE
    403 {
    404     /** Start offset in bytes, multiple of 512. */
    405     uint64_t    offStart;
    406     /** Amount of bytes described by this range, multiple of 512. */
    407     size_t      cbRange;
    408 } VDRANGE;
    409 
    410 /** Pointer to a range descriptor. */
    411 typedef VDRANGE *PVDRANGE;
    412 /** Pointer to a constant range descriptor. */
    413 typedef const VDRANGE *PCVDRANGE;
    414 
    415 /**
    416400 * VBox HDD Container main structure.
    417401 */
     
    11221106 *       appear to contain data. This method is mainly used to implement TRIM support.
    11231107 */
    1124 VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCVDRANGE paRanges, unsigned cRanges);
     1108VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges);
    11251109
    11261110
     
    11701154                               PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
    11711155                               void *pvUser1, void *pvUser2);
     1156
     1157/**
     1158 * Start an asynchronous discard request.
     1159 *
     1160 * @return  VBox status code.
     1161 * @param   pDisk           Pointer to HDD container.
     1162 * @param   paRanges        The array of ranges to discard.
     1163 * @param   cRanges         Number of entries in the array.
     1164 * @param   pfnComplete     Completion callback.
     1165 * @param   pvUser1         User data which is passed on completion.
     1166 * @param   pvUser2         User data which is passed on completion.
     1167 */
     1168VBOXDDU_DECL(int) VDAsyncDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges,
     1169                                       PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
     1170                                       void *pvUser1, void *pvUser);
     1171
    11721172RT_C_DECLS_END
    11731173
  • trunk/include/VBox/vddbg.h

    r38632 r38876  
    175175 * @param   phIoLogEntry Where to store the I/O log entry handle on success.
    176176 */
    177 VBOXDDU_DECL(int) VDDbgIoLogStartDiscard(VDIOLOGGER hIoLogger, bool fAsync, PVDRANGE paRanges, unsigned cRanges,
     177VBOXDDU_DECL(int) VDDbgIoLogStartDiscard(VDIOLOGGER hIoLogger, bool fAsync, PCRTRANGE paRanges, unsigned cRanges,
    178178                                         PVDIOLOGENT phIoLogEntry);
    179179
     
    239239 */
    240240VBOXDDU_DECL(int) VDDbgIoLogEventGetStartDiscard(VDIOLOGGER hIoLogger, uint64_t *pidEvent, bool *pfAsync,
    241                                                  PVDRANGE *ppaRanges, unsigned *pcRanges);
     241                                                 PRTRANGE *ppaRanges, unsigned *pcRanges);
    242242
    243243/**
  • trunk/src/VBox/Storage/DMG.cpp

    r38623 r38876  
    23342334    NULL,
    23352335    /* pfnDiscard */
     2336    NULL,
     2337    /* pfnAsyncDiscard */
    23362338    NULL
    23372339};
  • trunk/src/VBox/Storage/Debug/VDDbgIoLog.cpp

    r38644 r38876  
    449449}
    450450
    451 VBOXDDU_DECL(int) VDDbgIoLogStartDiscard(VDIOLOGGER hIoLogger, bool fAsync, PVDRANGE paRanges, unsigned cRanges,
     451VBOXDDU_DECL(int) VDDbgIoLogStartDiscard(VDIOLOGGER hIoLogger, bool fAsync, PCRTRANGE paRanges, unsigned cRanges,
    452452                                         PVDIOLOGENT phIoLogEntry)
    453453{
     
    697697
    698698VBOXDDU_DECL(int) VDDbgIoLogEventGetStartDiscard(VDIOLOGGER hIoLogger, uint64_t *pidEvent, bool *pfAsync,
    699                                                  PVDRANGE *ppaRanges, unsigned *pcRanges)
     699                                                 PRTRANGE *ppaRanges, unsigned *pcRanges)
    700700{
    701701    int rc = VINF_SUCCESS;
     
    716716        if (RT_SUCCESS(rc))
    717717        {
    718             PVDRANGE paRanges = NULL;
     718            PRTRANGE paRanges = NULL;
    719719            IoLogEntryDiscard DiscardRange;
    720720
     
    724724            *pcRanges  = RT_LE2H_U32(Entry.Discard.cRanges);
    725725
    726             paRanges = (PVDRANGE)RTMemAllocZ(*pcRanges * sizeof(VDRANGE));
     726            paRanges = (PRTRANGE)RTMemAllocZ(*pcRanges * sizeof(RTRANGE));
    727727            if (paRanges)
    728728            {
  • trunk/src/VBox/Storage/ISCSI.cpp

    r38621 r38876  
    55725572    NULL,
    55735573    /* pfnDiscard */
     5574    NULL,
     5575    /* pfnAsyncDiscard */
    55745576    NULL
    55755577};
  • trunk/src/VBox/Storage/QCOW.cpp

    r38875 r38876  
    28542854    NULL,
    28552855    /* pfnDiscard */
     2856    NULL,
     2857    /* pfnAsyncDiscard */
    28562858    NULL
    28572859};
  • trunk/src/VBox/Storage/QED.cpp

    r38623 r38876  
    29212921    qedResize,
    29222922    /* pfnDiscard */
     2923    NULL,
     2924    /* pfnAsyncDiscard */
    29232925    NULL
    29242926};
  • trunk/src/VBox/Storage/RAW.cpp

    r38621 r38876  
    12921292    NULL,
    12931293    /* pfnDiscard */
     1294    NULL,
     1295    /* pfnAsyncDiscard */
    12941296    NULL
    12951297};
  • trunk/src/VBox/Storage/VD.cpp

    r38673 r38876  
    273273    /** Flush */
    274274    VDIOCTXTXDIR_FLUSH,
     275    /** Discard */
     276    VDIOCTXTXDIR_DISCARD,
    275277    /** 32bit hack */
    276278    VDIOCTXTXDIR_32BIT_HACK = 0x7fffffff
     
    291293    /** Return code. */
    292294    int                          rcReq;
    293     /** Transfer direction */
    294     VDIOCTXTXDIR                 enmTxDir;
    295     /** Number of bytes left until this context completes. */
    296     volatile uint32_t            cbTransferLeft;
    297     /** Current offset */
    298     volatile uint64_t            uOffset;
    299     /** Number of bytes to transfer */
    300     volatile size_t              cbTransfer;
    301     /** Current image in the chain. */
    302     PVDIMAGE                     pImageCur;
    303     /** Start image to read from. pImageCur is reset to this
    304      *  value after it reached the first image in the chain. */
    305     PVDIMAGE                     pImageStart;
    306     /** S/G buffer */
    307     RTSGBUF                      SgBuf;
    308295    /** Flag whether the I/O context is blocked because it is in the growing list. */
    309296    bool                         fBlocked;
     
    321308    /** Next transfer part after the current one completed. */
    322309    PFNVDIOCTXTRANSFER           pfnIoCtxTransferNext;
     310    /** Transfer direction */
     311    VDIOCTXTXDIR                 enmTxDir;
     312    /** Request type dependent data. */
     313    union
     314    {
     315        /** I/O request (read/write). */
     316        struct
     317        {
     318            /** Number of bytes left until this context completes. */
     319            volatile uint32_t    cbTransferLeft;
     320            /** Current offset */
     321            volatile uint64_t    uOffset;
     322            /** Number of bytes to transfer */
     323            volatile size_t      cbTransfer;
     324            /** Current image in the chain. */
     325            PVDIMAGE             pImageCur;
     326            /** Start image to read from. pImageCur is reset to this
     327             *  value after it reached the first image in the chain. */
     328            PVDIMAGE             pImageStart;
     329            /** S/G buffer */
     330            RTSGBUF              SgBuf;
     331        } Io;
     332        /** Discard requests. */
     333        struct
     334        {
     335            /** Pointer to the range descriptor array. */
     336            PCRTRANGE            paRanges;
     337            /** Number of ranges in the array. */
     338            unsigned             cRanges;
     339            /** Range descriptor index which is processed. */
     340            unsigned             idxRange;
     341            /** Start offset to discard currently. */
     342            uint64_t             offCur;
     343            /** How many bytes left to discard in the current range. */
     344            size_t               cbDiscardLeft;
     345            /** How many bytes to discard in the current block (<= cbDiscardLeft). */
     346            size_t               cbThisDiscard;
     347            /** Discard block handled currently. */
     348            PVDDISCARDBLOCK      pBlock;
     349        } Discard;
     350    } Req;
    323351    /** Parent I/O context if any. Sets the type of the context (root/child) */
    324352    PVDIOCTX                     pIoCtxParent;
     
    333361            /** User argument 1 passed on completion. */
    334362            void                        *pvUser1;
    335             /** User argument 1 passed on completion. */
     363            /** User argument 2 passed on completion. */
    336364            void                        *pvUser2;
    337365        } Root;
     
    369397} VDIOCTX;
    370398
     399/**
     400 * List node for deferred I/O contexts.
     401 */
    371402typedef struct VDIOCTXDEFERRED
    372403{
     
    501532    &g_VciCacheBackend
    502533};
     534
     535/** Forward declaration of the async discard helper. */
     536static int vdDiscardHelperAsync(PVDIOCTX pIoCtx);
    503537
    504538/**
     
    11631197                }
    11641198                else
     1199                {
     1200                    RTMemFree(pbmAllocated);
    11651201                    rc = VERR_NO_MEMORY;
     1202                }
    11661203            }
    11671204        }
     
    11691206        {
    11701207            /* Range lies partly in the block, update allocation bitmap. */
     1208            int32_t idxStart, idxEnd;
     1209
    11711210            cbThisDiscard = RT_MIN(cbDiscard, pBlock->Core.KeyLast - offStart + 1);
    1172             rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET;
    1173         }
    1174 
    1175         Assert(cbDiscard >= cbThisDiscard);
    1176 
    1177         if (rc == VERR_VD_DISCARD_ALIGNMENT_NOT_MET)
    1178         {
    1179             int32_t idxStart, idxEnd;
    11801211
    11811212            AssertPtr(pBlock);
     
    12411272 * @param   cRanges  The number of ranges in the array.
    12421273 */
    1243 static int vdDiscardHelper(PVBOXHDD pDisk, PCVDRANGE paRanges, unsigned cRanges)
     1274static int vdDiscardHelper(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges)
    12441275{
    12451276    int rc = VINF_SUCCESS;
     
    13311362        pIoCtx->pDisk                 = pDisk;
    13321363        pIoCtx->enmTxDir              = enmTxDir;
    1333         pIoCtx->cbTransferLeft        = cbTransfer;
    1334         pIoCtx->uOffset               = uOffset;
    1335         pIoCtx->cbTransfer            = cbTransfer;
    1336         pIoCtx->pImageStart           = pImageStart;
    1337         pIoCtx->pImageCur             = pImageStart;
     1364        pIoCtx->Req.Io.cbTransferLeft = cbTransfer;
     1365        pIoCtx->Req.Io.uOffset        = uOffset;
     1366        pIoCtx->Req.Io.cbTransfer     = cbTransfer;
     1367        pIoCtx->Req.Io.pImageStart    = pImageStart;
     1368        pIoCtx->Req.Io.pImageCur      = pImageStart;
    13381369        pIoCtx->cDataTransfersPending = 0;
    13391370        pIoCtx->cMetaTransfersPending = 0;
     
    13471378        /* There is no S/G list for a flush request. */
    13481379        if (enmTxDir != VDIOCTXTXDIR_FLUSH)
    1349             RTSgBufClone(&pIoCtx->SgBuf, pcSgBuf);
     1380            RTSgBufClone(&pIoCtx->Req.Io.SgBuf, pcSgBuf);
    13501381        else
    1351             memset(&pIoCtx->SgBuf, 0, sizeof(RTSGBUF));
     1382            memset(&pIoCtx->Req.Io.SgBuf, 0, sizeof(RTSGBUF));
    13521383    }
    13531384
     
    13781409}
    13791410
     1411DECLINLINE(PVDIOCTX) vdIoCtxDiscardAlloc(PVBOXHDD pDisk, PCRTRANGE paRanges,
     1412                                         unsigned cRanges,
     1413                                         PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
     1414                                         void *pvUser1, void *pvUser2,
     1415                                         void *pvAllocation,
     1416                                         PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
     1417{
     1418    PVDIOCTX pIoCtx = NULL;
     1419
     1420    pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
     1421    if (RT_LIKELY(pIoCtx))
     1422    {
     1423        pIoCtx->pDisk                     = pDisk;
     1424        pIoCtx->enmTxDir                  = VDIOCTXTXDIR_DISCARD;
     1425        pIoCtx->cDataTransfersPending     = 0;
     1426        pIoCtx->cMetaTransfersPending     = 0;
     1427        pIoCtx->fComplete                 = false;
     1428        pIoCtx->fBlocked                  = false;
     1429        pIoCtx->pvAllocation              = pvAllocation;
     1430        pIoCtx->pfnIoCtxTransfer          = pfnIoCtxTransfer;
     1431        pIoCtx->pfnIoCtxTransferNext      = NULL;
     1432        pIoCtx->rcReq                     = VINF_SUCCESS;
     1433        pIoCtx->Req.Discard.paRanges      = paRanges;
     1434        pIoCtx->Req.Discard.cRanges       = cRanges;
     1435        pIoCtx->Req.Discard.idxRange      = 0;
     1436        pIoCtx->Req.Discard.cbDiscardLeft = 0;
     1437        pIoCtx->Req.Discard.offCur        = 0;
     1438        pIoCtx->Req.Discard.cbThisDiscard = 0;
     1439
     1440        pIoCtx->pIoCtxParent          = NULL;
     1441        pIoCtx->Type.Root.pfnComplete = pfnComplete;
     1442        pIoCtx->Type.Root.pvUser1     = pvUser1;
     1443        pIoCtx->Type.Root.pvUser2     = pvUser2;
     1444    }
     1445
     1446    LogFlow(("Allocated discard I/O context %#p\n", pIoCtx));
     1447    return pIoCtx;
     1448}
     1449
    13801450DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
    13811451                                       uint64_t uOffset, size_t cbTransfer,
     
    14591529    AssertPtr(pIoCtx->pIoCtxParent);
    14601530
    1461     RTSgBufReset(&pIoCtx->SgBuf);
    1462     pIoCtx->uOffset        = pIoCtx->Type.Child.uOffsetSaved;
    1463     pIoCtx->cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
     1531    RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
     1532    pIoCtx->Req.Io.uOffset        = pIoCtx->Type.Child.uOffsetSaved;
     1533    pIoCtx->Req.Io.cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
    14641534}
    14651535
     
    15011571static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
    15021572{
    1503     return RTSgBufCopy(&pIoCtxDst->SgBuf, &pIoCtxSrc->SgBuf, cbData);
     1573    return RTSgBufCopy(&pIoCtxDst->Req.Io.SgBuf, &pIoCtxSrc->Req.Io.SgBuf, cbData);
    15041574}
    15051575
    15061576static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
    15071577{
    1508     return RTSgBufCmp(&pIoCtx1->SgBuf, &pIoCtx2->SgBuf, cbData);
     1578    return RTSgBufCmp(&pIoCtx1->Req.Io.SgBuf, &pIoCtx2->Req.Io.SgBuf, cbData);
    15091579}
    15101580
    15111581static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
    15121582{
    1513     return RTSgBufCopyToBuf(&pIoCtx->SgBuf, pbData, cbData);
     1583    return RTSgBufCopyToBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
    15141584}
    15151585
     
    15171587static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
    15181588{
    1519     return RTSgBufCopyFromBuf(&pIoCtx->SgBuf, pbData, cbData);
     1589    return RTSgBufCopyFromBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
    15201590}
    15211591
    15221592static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
    15231593{
    1524     return RTSgBufSet(&pIoCtx->SgBuf, ch, cbData);
     1594    return RTSgBufSet(&pIoCtx->Req.Io.SgBuf, ch, cbData);
    15251595}
    15261596
     
    15341604    RTCritSectEnter(&pDisk->CritSect);
    15351605
    1536     if (   !pIoCtx->cbTransferLeft
    1537         && !pIoCtx->cMetaTransfersPending
     1606    if (   !pIoCtx->cMetaTransfersPending
    15381607        && !pIoCtx->cDataTransfersPending
    15391608        && !pIoCtx->pfnIoCtxTransfer)
     
    15671636        /* Call the transfer function advancing to the next while there is no error. */
    15681637        while (   pIoCtx->pfnIoCtxTransfer
     1638               && !pIoCtx->cMetaTransfersPending
    15691639               && RT_SUCCESS(rc))
    15701640        {
     
    15821652
    15831653    if (   RT_SUCCESS(rc)
    1584         && !pIoCtx->cbTransferLeft
    15851654        && !pIoCtx->cMetaTransfersPending
    15861655        && !pIoCtx->cDataTransfersPending)
     
    16071676    RTCritSectLeave(&pDisk->CritSect);
    16081677
    1609     LogFlowFunc(("pIoCtx=%#p rc=%Rrc cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
    1610                  pIoCtx, rc, pIoCtx->cbTransferLeft, pIoCtx->cMetaTransfersPending,
     1678    LogFlowFunc(("pIoCtx=%#p rc=%Rrc cDataTransfersPending=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
     1679                 pIoCtx, rc, pIoCtx->cDataTransfersPending, pIoCtx->cMetaTransfersPending,
    16111680                 pIoCtx->fComplete));
    16121681
     
    17111780{
    17121781    int rc;
    1713     size_t cbToRead     = pIoCtx->cbTransfer;
    1714     uint64_t uOffset    = pIoCtx->uOffset;
     1782    size_t cbToRead     = pIoCtx->Req.Io.cbTransfer;
     1783    uint64_t uOffset    = pIoCtx->Req.Io.uOffset;
    17151784    PVDIMAGE pCurrImage = NULL;
    17161785    size_t cbThisRead;
     
    17191788    do
    17201789    {
    1721         pCurrImage = pIoCtx->pImageCur;
     1790        pCurrImage = pIoCtx->Req.Io.pImageCur;
    17221791
    17231792        /* Search for image with allocated block. Do not attempt to read more
     
    17511820            /* No image in the chain contains the data for the block. */
    17521821            vdIoCtxSet(pIoCtx, '\0', cbThisRead);
    1753             ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisRead);
     1822            ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbThisRead);
    17541823            rc = VINF_SUCCESS;
    17551824        }
     
    17741843    {
    17751844        /* Save the current state. */
    1776         pIoCtx->uOffset    = uOffset;
    1777         pIoCtx->cbTransfer = cbToRead;
    1778         pIoCtx->pImageCur  = pCurrImage ? pCurrImage : pIoCtx->pImageStart;
     1845        pIoCtx->Req.Io.uOffset    = uOffset;
     1846        pIoCtx->Req.Io.cbTransfer = cbToRead;
     1847        pIoCtx->Req.Io.pImageCur  = pCurrImage ? pCurrImage : pIoCtx->Req.Io.pImageStart;
    17791848    }
    17801849
     
    22402309    int rc = VINF_SUCCESS;
    22412310    PVBOXHDD pDisk = pIoCtx->pDisk;
    2242     PVDIMAGE pImage = pIoCtx->pImageCur;
     2311    PVDIMAGE pImage = pIoCtx->Req.Io.pImageCur;
    22432312
    22442313    rc = pImage->Backend->pfnAsyncFlush(pImage->pBackendData, pIoCtx);
     
    22842353                    else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
    22852354                    {
     2355                        ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
    22862356                        pIoCtx->fBlocked = true;
    22872357                    }
     
    23942464static int vdWriteHelperOptimizedCommitAsync(PVDIOCTX pIoCtx)
    23952465{
    2396     int rc = VINF_SUCCESS;
    2397     PVDIMAGE pImage = pIoCtx->pImageStart;
    2398     size_t cbPreRead      = pIoCtx->Type.Child.cbPreRead;
    2399     size_t cbPostRead     = pIoCtx->Type.Child.cbPostRead;
    2400     size_t cbThisWrite    = pIoCtx->Type.Child.cbTransferParent;
     2466    int rc             = VINF_SUCCESS;
     2467    PVDIMAGE pImage    = pIoCtx->Req.Io.pImageStart;
     2468    size_t cbPreRead   = pIoCtx->Type.Child.cbPreRead;
     2469    size_t cbPostRead  = pIoCtx->Type.Child.cbPostRead;
     2470    size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
    24012471
    24022472    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
    24032473    rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
    2404                                         pIoCtx->uOffset - cbPreRead,
     2474                                        pIoCtx->Req.Io.uOffset - cbPreRead,
    24052475                                        cbPreRead + cbThisWrite + cbPostRead,
    24062476                                        pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
     
    24232493{
    24242494    int rc = VINF_SUCCESS;
    2425     PVDIMAGE pImage = pIoCtx->pImageCur;
     2495    PVDIMAGE pImage       = pIoCtx->Req.Io.pImageCur;
    24262496    size_t cbThisWrite    = 0;
    24272497    size_t cbPreRead      = pIoCtx->Type.Child.cbPreRead;
     
    24362506    AssertPtr(pIoCtxParent);
    24372507    Assert(!pIoCtxParent->pIoCtxParent);
    2438     Assert(!pIoCtx->cbTransferLeft && !pIoCtx->cMetaTransfersPending);
     2508    Assert(!pIoCtx->Req.Io.cbTransferLeft && !pIoCtx->cMetaTransfersPending);
    24392509
    24402510    vdIoCtxChildReset(pIoCtx);
    24412511    cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
    2442     RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
     2512    RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbPreRead);
    24432513
    24442514    /* Check if the write would modify anything in this block. */
    2445     if (!RTSgBufCmp(&pIoCtx->SgBuf, &pIoCtxParent->SgBuf, cbThisWrite))
     2515    if (!RTSgBufCmp(&pIoCtx->Req.Io.SgBuf, &pIoCtxParent->Req.Io.SgBuf, cbThisWrite))
    24462516    {
    24472517        RTSGBUF SgBufSrcTmp;
    24482518
    2449         RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->SgBuf);
     2519        RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->Req.Io.SgBuf);
    24502520        RTSgBufAdvance(&SgBufSrcTmp, cbThisWrite);
    2451         RTSgBufAdvance(&pIoCtx->SgBuf, cbThisWrite);
    2452 
    2453         if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->SgBuf, &SgBufSrcTmp, cbWriteCopy))
     2521        RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbThisWrite);
     2522
     2523        if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->Req.Io.SgBuf, &SgBufSrcTmp, cbWriteCopy))
    24542524        {
    24552525            /* Block is completely unchanged, so no need to write anything. */
    24562526            LogFlowFunc(("Block didn't changed\n"));
    2457             ASMAtomicWriteU32(&pIoCtx->cbTransferLeft, 0);
    2458             RTSgBufAdvance(&pIoCtxParent->SgBuf, cbThisWrite);
     2527            ASMAtomicWriteU32(&pIoCtx->Req.Io.cbTransferLeft, 0);
     2528            RTSgBufAdvance(&pIoCtxParent->Req.Io.SgBuf, cbThisWrite);
    24592529            return VINF_VD_ASYNC_IO_FINISHED;
    24602530        }
     
    24622532
    24632533    /* Copy the data to the right place in the buffer. */
    2464     RTSgBufReset(&pIoCtx->SgBuf);
    2465     RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
     2534    RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
     2535    RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbPreRead);
    24662536    vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
    24672537
     
    24782548            RTSGBUF SgBufParentTmp;
    24792549
    2480             RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->SgBuf);
    2481             RTSgBufCopy(&pIoCtx->SgBuf, &SgBufParentTmp, cbWriteCopy);
     2550            RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->Req.Io.SgBuf);
     2551            RTSgBufCopy(&pIoCtx->Req.Io.SgBuf, &SgBufParentTmp, cbWriteCopy);
    24822552        }
    24832553
     
    24862556        if (cbFill)
    24872557        {
    2488             RTSgBufAdvance(&pIoCtx->SgBuf, cbReadImage);
     2558            RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbReadImage);
    24892559            vdIoCtxSet(pIoCtx, '\0', cbFill);
    24902560        }
     
    24922562
    24932563    /* Write the full block to the virtual disk. */
    2494     RTSgBufReset(&pIoCtx->SgBuf);
     2564    RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
    24952565    pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCommitAsync;
    24962566
     
    25042574    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
    25052575
    2506     if (pIoCtx->cbTransferLeft)
     2576    if (pIoCtx->Req.Io.cbTransferLeft)
    25072577        rc = vdReadHelperAsync(pIoCtx);
    25082578
    25092579    if (   RT_SUCCESS(rc)
    2510         && (   pIoCtx->cbTransferLeft
     2580        && (   pIoCtx->Req.Io.cbTransferLeft
    25112581            || pIoCtx->cMetaTransfersPending))
    25122582        rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
     
    25632633    /* Read the entire data of the block so that we can compare whether it will
    25642634     * be modified by the write or not. */
    2565     pIoCtx->cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
    2566     pIoCtx->cbTransfer     = pIoCtx->cbTransferLeft;
    2567     pIoCtx->uOffset       -= cbPreRead;
     2635    pIoCtx->Req.Io.cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
     2636    pIoCtx->Req.Io.cbTransfer     = pIoCtx->Req.Io.cbTransferLeft;
     2637    pIoCtx->Req.Io.uOffset       -= cbPreRead;
    25682638
    25692639    /* Next step */
     
    25792649{
    25802650    int rc;
    2581     size_t cbWrite   = pIoCtx->cbTransfer;
    2582     uint64_t uOffset = pIoCtx->uOffset;
    2583     PVDIMAGE pImage  = pIoCtx->pImageCur;
     2651    size_t cbWrite   = pIoCtx->Req.Io.cbTransfer;
     2652    uint64_t uOffset = pIoCtx->Req.Io.uOffset;
     2653    PVDIMAGE pImage  = pIoCtx->Req.Io.pImageCur;
    25842654    PVBOXHDD pDisk   = pIoCtx->pDisk;
    25852655    unsigned fWrite;
     
    25892659    rc = vdSetModifiedFlagAsync(pDisk, pIoCtx);
    25902660    if (RT_FAILURE(rc)) /* Includes I/O in progress. */
     2661        return rc;
     2662
     2663    rc = vdDiscardSetRangeAllocated(pDisk, uOffset, cbWrite);
     2664    if (RT_FAILURE(rc))
    25912665        return rc;
    25922666
     
    26592733                {
    26602734                    LogFlow(("Child write request completed\n"));
    2661                     Assert(pIoCtx->cbTransferLeft >= cbThisWrite);
    2662                     ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisWrite);
     2735                    Assert(pIoCtx->Req.Io.cbTransferLeft >= cbThisWrite);
     2736                    ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbThisWrite);
    26632737                    vdIoCtxUnlockDisk(pDisk, pIoCtx, false /* fProcessDeferredReqs*/ );
    26642738                    vdIoCtxFree(pDisk, pIoCtxWrite);
     
    26692743                {
    26702744                    LogFlow(("Child write pending\n"));
     2745                    ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
    26712746                    pIoCtx->fBlocked = true;
    26722747                    rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
     
    27082783            rc = VINF_SUCCESS;
    27092784
    2710         pIoCtx->uOffset    = uOffset;
    2711         pIoCtx->cbTransfer = cbWrite;
     2785        pIoCtx->Req.Io.uOffset    = uOffset;
     2786        pIoCtx->Req.Io.cbTransfer = cbWrite;
    27122787    }
    27132788
     
    27222797    int rc = VINF_SUCCESS;
    27232798    PVBOXHDD pDisk = pIoCtx->pDisk;
    2724     PVDIMAGE pImage = pIoCtx->pImageCur;
     2799    PVDIMAGE pImage = pIoCtx->Req.Io.pImageCur;
    27252800
    27262801    rc = vdIoCtxLockDisk(pDisk, pIoCtx);
     
    27352810    }
    27362811
     2812    return rc;
     2813}
     2814
     2815/**
     2816 * Async discard helper - discards a whole block which is recorded in the block
     2817 * tree.
     2818 *
     2819 * @returns VBox status code.
     2820 * @param   pIoCtx    The I/O context to operate on.
     2821 */
     2822static int vdDiscardWholeBlockAsync(PVDIOCTX pIoCtx)
     2823{
     2824    int rc = VINF_SUCCESS;
     2825    PVBOXHDD pDisk = pIoCtx->pDisk;
     2826    PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
     2827    PVDDISCARDBLOCK pBlock = pIoCtx->Req.Discard.pBlock;
     2828    size_t cbPreAllocated, cbPostAllocated, cbActuallyDiscarded;
     2829
     2830    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
     2831
     2832    AssertPtr(pBlock);
     2833
     2834    rc = pDisk->pLast->Backend->pfnAsyncDiscard(pDisk->pLast->pBackendData, pIoCtx,
     2835                                                pBlock->Core.Key, pBlock->cbDiscard,
     2836                                                &cbPreAllocated, &cbPostAllocated,
     2837                                                &cbActuallyDiscarded, NULL, 0);
     2838    Assert(rc != VERR_VD_DISCARD_ALIGNMENT_NOT_MET);
     2839    Assert(!cbPreAllocated);
     2840    Assert(!cbPostAllocated);
     2841    Assert(cbActuallyDiscarded == pBlock->cbDiscard || RT_FAILURE(rc));
     2842
     2843    /* Remove the block on success. */
     2844    if (   RT_SUCCESS(rc)
     2845        || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
     2846    {
     2847        PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
     2848        Assert(pBlockRemove == pBlock);
     2849
     2850        pDiscard->cbDiscarding -= pBlock->cbDiscard;
     2851        RTListNodeRemove(&pBlock->NodeLru);
     2852        RTMemFree(pBlock->pbmAllocated);
     2853        RTMemFree(pBlock);
     2854        pIoCtx->Req.Discard.pBlock = NULL;/* Safety precaution. */
     2855        pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync; /* Next part. */
     2856        rc = VINF_SUCCESS;
     2857    }
     2858
     2859    LogFlowFunc(("returns rc=%Rrc\n", rc));
     2860    return rc;
     2861}
     2862
     2863/**
     2864 * Removes the least recently used blocks from the waiting list until
     2865 * the new value is reached - version for async I/O.
     2866 *
     2867 * @returns VBox status code.
     2868 * @param   pDisk              VD disk container.
     2869 * @param   pDiscard           The discard state.
     2870 * @param   cbDiscardingNew    How many bytes should be waiting on success.
     2871 *                             The number of bytes waiting can be less.
     2872 */
     2873static int vdDiscardRemoveBlocksAsync(PVBOXHDD pDisk, PVDIOCTX pIoCtx, size_t cbDiscardingNew)
     2874{
     2875    int rc = VINF_SUCCESS;
     2876    PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
     2877
     2878    LogFlowFunc(("pDisk=%#p pDiscard=%#p cbDiscardingNew=%zu\n",
     2879                 pDisk, pDiscard, cbDiscardingNew));
     2880
     2881    while (pDiscard->cbDiscarding > cbDiscardingNew)
     2882    {
     2883        PVDDISCARDBLOCK pBlock = RTListGetLast(&pDiscard->ListLru, VDDISCARDBLOCK, NodeLru);
     2884
     2885        Assert(!RTListIsEmpty(&pDiscard->ListLru));
     2886
     2887        /* Go over the allocation bitmap and mark all discarded sectors as unused. */
     2888        uint64_t offStart = pBlock->Core.Key;
     2889        uint32_t idxStart = 0;
     2890        size_t cbLeft = pBlock->cbDiscard;
     2891        bool fAllocated = ASMBitTest(pBlock->pbmAllocated, idxStart);
     2892        uint32_t cSectors = pBlock->cbDiscard / 512;
     2893
     2894        while (cbLeft > 0)
     2895        {
     2896            int32_t idxEnd;
     2897            size_t cbThis = cbLeft;
     2898
     2899            if (fAllocated)
     2900            {
     2901                /* Check for the first unallocated bit. */
     2902                idxEnd = ASMBitNextClear(pBlock->pbmAllocated, cSectors, idxStart);
     2903                if (idxEnd != -1)
     2904                {
     2905                    cbThis = (idxEnd - idxStart) * 512;
     2906                    fAllocated = false;
     2907                }
     2908            }
     2909            else
     2910            {
     2911                /* Mark as unused and check for the first set bit. */
     2912                idxEnd = ASMBitNextSet(pBlock->pbmAllocated, cSectors, idxStart);
     2913                if (idxEnd != -1)
     2914                    cbThis = (idxEnd - idxStart) * 512;
     2915
     2916                rc = pDisk->pLast->Backend->pfnAsyncDiscard(pDisk->pLast->pBackendData, pIoCtx,
     2917                                                            offStart, cbThis, NULL, NULL, &cbThis,
     2918                                                            NULL, VD_DISCARD_MARK_UNUSED);
     2919                if (      RT_FAILURE(rc)
     2920                    && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
     2921                    break;
     2922
     2923                fAllocated = true;
     2924            }
     2925
     2926            idxStart  = idxEnd;
     2927            offStart += cbThis;
     2928            cbLeft   -= cbThis;
     2929        }
     2930
     2931        if (   RT_FAILURE(rc)
     2932            && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
     2933            break;
     2934
     2935        PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
     2936        Assert(pBlockRemove == pBlock);
     2937        RTListNodeRemove(&pBlock->NodeLru);
     2938
     2939        pDiscard->cbDiscarding -= pBlock->cbDiscard;
     2940        RTMemFree(pBlock->pbmAllocated);
     2941        RTMemFree(pBlock);
     2942    }
     2943
     2944    if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
     2945        rc = VINF_SUCCESS;
     2946
     2947    Assert(RT_FAILURE(rc) || pDiscard->cbDiscarding <= cbDiscardingNew);
     2948
     2949    LogFlowFunc(("returns rc=%Rrc\n", rc));
     2950    return rc;
     2951}
     2952
     2953/**
     2954 * Async discard helper - discards the current range if there is no matching
     2955 * block in the tree.
     2956 *
     2957 * @returns VBox status code.
     2958 * @param   pIoCtx    The I/O context to operate on.
     2959 */
     2960static int vdDiscardCurrentRangeAsync(PVDIOCTX pIoCtx)
     2961{
     2962    PVBOXHDD        pDisk         = pIoCtx->pDisk;
     2963    PVDDISCARDSTATE pDiscard      = pDisk->pDiscard;
     2964    uint64_t        offStart      = pIoCtx->Req.Discard.offCur;
     2965    size_t          cbThisDiscard = pIoCtx->Req.Discard.cbThisDiscard;
     2966    void *pbmAllocated = NULL;
     2967    size_t cbPreAllocated, cbPostAllocated;
     2968    int rc = VINF_SUCCESS;
     2969
     2970    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
     2971
     2972    /* No block found, try to discard using the backend first. */
     2973    rc = pDisk->pLast->Backend->pfnAsyncDiscard(pDisk->pLast->pBackendData, pIoCtx,
     2974                                                offStart, cbThisDiscard, &cbPreAllocated,
     2975                                                &cbPostAllocated, &cbThisDiscard,
     2976                                                &pbmAllocated, 0);
     2977    if (rc == VERR_VD_DISCARD_ALIGNMENT_NOT_MET)
     2978    {
     2979        /* Create new discard block. */
     2980        PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTMemAllocZ(sizeof(VDDISCARDBLOCK));
     2981        if (pBlock)
     2982        {
     2983            pBlock->Core.Key     = offStart - cbPreAllocated;
     2984            pBlock->Core.KeyLast = offStart + cbThisDiscard + cbPostAllocated - 1;
     2985            pBlock->cbDiscard    = cbPreAllocated + cbThisDiscard + cbPostAllocated;
     2986            pBlock->pbmAllocated = pbmAllocated;
     2987            bool fInserted = RTAvlrU64Insert(pDiscard->pTreeBlocks, &pBlock->Core);
     2988            Assert(fInserted);
     2989
     2990            RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru);
     2991            pDiscard->cbDiscarding += pBlock->cbDiscard;
     2992
     2993            Assert(pIoCtx->Req.Discard.cbDiscardLeft >= cbThisDiscard);
     2994            pIoCtx->Req.Discard.cbDiscardLeft -= cbThisDiscard;
     2995            pIoCtx->Req.Discard.offCur        += cbThisDiscard;
     2996            pIoCtx->Req.Discard.cbThisDiscard = cbThisDiscard;
     2997
     2998            if (pDiscard->cbDiscarding > VD_DISCARD_REMOVE_THRESHOLD)
     2999                rc = vdDiscardRemoveBlocksAsync(pDisk, pIoCtx, VD_DISCARD_REMOVE_THRESHOLD);
     3000            else
     3001                rc = VINF_SUCCESS;
     3002
     3003            if (RT_SUCCESS(rc))
     3004                pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync; /* Next part. */
     3005        }
     3006        else
     3007        {
     3008            RTMemFree(pbmAllocated);
     3009            rc = VERR_NO_MEMORY;
     3010        }
     3011    }
     3012    else if (   RT_SUCCESS(rc)
     3013             || rc == VERR_VD_ASYNC_IO_IN_PROGRESS) /* Save state and andvance to next range. */
     3014    {
     3015        Assert(pIoCtx->Req.Discard.cbDiscardLeft >= cbThisDiscard);
     3016        pIoCtx->Req.Discard.cbDiscardLeft -= cbThisDiscard;
     3017        pIoCtx->Req.Discard.offCur        += cbThisDiscard;
     3018        pIoCtx->Req.Discard.cbThisDiscard  = cbThisDiscard;
     3019        pIoCtx->pfnIoCtxTransferNext       = vdDiscardHelperAsync;
     3020        rc = VINF_SUCCESS;
     3021    }
     3022
     3023    LogFlowFunc(("returns rc=%Rrc\n", rc));
     3024    return rc;
     3025}
     3026
     3027/**
     3028 * Async discard helper - entry point.
     3029 *
     3030 * @returns VBox status code.
     3031 * @param   pIoCtx    The I/O context to operate on.
     3032 */
     3033static int vdDiscardHelperAsync(PVDIOCTX pIoCtx)
     3034{
     3035    int rc             = VINF_SUCCESS;
     3036    PVBOXHDD  pDisk    = pIoCtx->pDisk;
     3037    PCRTRANGE paRanges = pIoCtx->Req.Discard.paRanges;
     3038    unsigned  cRanges  = pIoCtx->Req.Discard.cRanges;
     3039    PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
     3040
     3041    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
     3042
     3043    /* Check if the I/O context processed all ranges. */
     3044    if (   pIoCtx->Req.Discard.idxRange == cRanges
     3045        && !pIoCtx->Req.Discard.cbDiscardLeft)
     3046    {
     3047        LogFlowFunc(("All ranges discarded, completing\n"));
     3048        vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessDeferredReqs*/);
     3049        return VINF_SUCCESS;
     3050    }
     3051
     3052    if (pDisk->pIoCtxLockOwner != pIoCtx)
     3053        rc = vdIoCtxLockDisk(pDisk, pIoCtx);
     3054
     3055    if (RT_SUCCESS(rc))
     3056    {
     3057        uint64_t offStart      = pIoCtx->Req.Discard.offCur;
     3058        size_t   cbDiscardLeft = pIoCtx->Req.Discard.cbDiscardLeft;
     3059        size_t   cbThisDiscard;
     3060
     3061        if (RT_UNLIKELY(!pDiscard))
     3062        {
     3063            pDiscard = vdDiscardStateCreate();
     3064            if (!pDiscard)
     3065                return VERR_NO_MEMORY;
     3066
     3067            pDisk->pDiscard = pDiscard;
     3068        }
     3069
     3070        if (!pIoCtx->Req.Discard.cbDiscardLeft)
     3071        {
     3072            offStart      = paRanges[pIoCtx->Req.Discard.idxRange].offStart;
     3073            cbDiscardLeft = paRanges[pIoCtx->Req.Discard.idxRange].cbRange;
     3074            LogFlowFunc(("New range descriptor loaded (%u) offStart=%llu cbDiscard=%zu\n",
     3075                         pIoCtx->Req.Discard.idxRange, offStart, cbDiscardLeft));
     3076            pIoCtx->Req.Discard.idxRange++;
     3077        }
     3078
     3079        /* Look for a matching block in the AVL tree first. */
     3080        PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, false);
     3081        if (!pBlock || pBlock->Core.KeyLast < offStart)
     3082        {
     3083            PVDDISCARDBLOCK pBlockAbove = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, true);
     3084
     3085            /* Clip range to remain in the current block. */
     3086            if (pBlockAbove)
     3087                cbThisDiscard = RT_MIN(cbDiscardLeft, pBlockAbove->Core.KeyLast - offStart + 1);
     3088            else
     3089                cbThisDiscard = cbDiscardLeft;
     3090
     3091            Assert(!(cbThisDiscard % 512));
     3092            pIoCtx->Req.Discard.pBlock   = NULL;
     3093            pIoCtx->pfnIoCtxTransferNext = vdDiscardCurrentRangeAsync;
     3094        }
     3095        else
     3096        {
     3097            /* Range lies partly in the block, update allocation bitmap. */
     3098            int32_t idxStart, idxEnd;
     3099
     3100            cbThisDiscard = RT_MIN(cbDiscardLeft, pBlock->Core.KeyLast - offStart + 1);
     3101
     3102            AssertPtr(pBlock);
     3103
     3104            Assert(!(cbThisDiscard % 512));
     3105            Assert(!((offStart - pBlock->Core.Key) % 512));
     3106
     3107            idxStart = (offStart - pBlock->Core.Key) / 512;
     3108            idxEnd = idxStart + (cbThisDiscard / 512);
     3109
     3110            ASMBitClearRange(pBlock->pbmAllocated, idxStart, idxEnd);
     3111
     3112            cbDiscardLeft -= cbThisDiscard;
     3113            offStart      += cbThisDiscard;
     3114
     3115            /* Call the backend to discard the block if it is completely unallocated now. */
     3116            if (ASMBitFirstSet((volatile void *)pBlock->pbmAllocated, pBlock->cbDiscard / 512) == -1)
     3117            {
     3118                pIoCtx->Req.Discard.pBlock   = pBlock;
     3119                pIoCtx->pfnIoCtxTransferNext = vdDiscardWholeBlockAsync;
     3120                rc = VINF_SUCCESS;
     3121            }
     3122            else
     3123            {
     3124                RTListNodeRemove(&pBlock->NodeLru);
     3125                RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru);
     3126
     3127                /* Start with next range. */
     3128                pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync;
     3129                rc = VINF_SUCCESS;
     3130            }
     3131        }
     3132
     3133        /* Save state in the context. */
     3134        pIoCtx->Req.Discard.offCur        = offStart;
     3135        pIoCtx->Req.Discard.cbDiscardLeft = cbDiscardLeft;
     3136        pIoCtx->Req.Discard.cbThisDiscard = cbThisDiscard;
     3137    }
     3138
     3139    LogFlowFunc(("returns rc=%Rrc\n", rc));
    27373140    return rc;
    27383141}
     
    31603563                    ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
    31613564
     3565                ASMAtomicDecU32(&pIoCtxParent->cDataTransfersPending);
     3566
    31623567                if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
    31633568                {
     
    31663571
    31673572                    /* Update the parent state. */
    3168                     Assert(pIoCtxParent->cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
    3169                     ASMAtomicSubU32(&pIoCtxParent->cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
     3573                    Assert(pIoCtxParent->Req.Io.cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
     3574                    ASMAtomicSubU32(&pIoCtxParent->Req.Io.cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
    31703575                }
    31713576                else
     
    32513656                    vdThreadFinishWrite(pDisk);
    32523657                }
    3253                 else if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
     3658                else if (   pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE
     3659                         || pIoCtx->enmTxDir == VDIOCTXTXDIR_DISCARD)
    32543660                    vdThreadFinishWrite(pDisk);
    32553661                else
     
    32893695
    32903696    RTCritSectEnter(&pDisk->CritSect);
    3291     Assert(pIoCtx->cbTransferLeft >= cbTransfer);
    3292     ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTransfer);
     3697    Assert(pIoCtx->Req.Io.cbTransferLeft >= cbTransfer);
     3698    ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbTransfer);
    32933699    ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
    32943700
     
    35653971        size_t   cbTaskRead = 0;
    35663972
    3567         cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbRead);
     3973        cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, aSeg, &cSegments, cbRead);
    35683974
    35693975        Assert(cSegments > 0);
     
    35933999        if (RT_SUCCESS(rc))
    35944000        {
    3595             AssertMsg(cbTaskRead <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
    3596             ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskRead);
     4001            AssertMsg(cbTaskRead <= pIoCtx->Req.Io.cbTransferLeft, ("Impossible!\n"));
     4002            ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbTaskRead);
    35974003            ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
    35984004            vdIoTaskFree(pDisk, pIoTask);
     
    36374043        size_t   cbTaskWrite = 0;
    36384044
    3639         cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbWrite);
     4045        cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, aSeg, &cSegments, cbWrite);
    36404046
    36414047        Assert(cSegments > 0);
     
    36654071        if (RT_SUCCESS(rc))
    36664072        {
    3667             AssertMsg(cbTaskWrite <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
    3668             ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskWrite);
     4073            AssertMsg(cbTaskWrite <= pIoCtx->Req.Io.cbTransferLeft, ("Impossible!\n"));
     4074            ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbTaskWrite);
    36694075            ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
    36704076            vdIoTaskFree(pDisk, pIoTask);
     
    39694375    Assert(cbCopied == cbBuf);
    39704376
    3971     ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
     4377    ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbCopied);
    39724378
    39734379    return cbCopied;
     
    39864392    Assert(cbCopied == cbBuf);
    39874393
    3988     ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
     4394    ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbCopied);
    39894395
    39904396    return cbCopied;
     
    40024408    Assert(cbSet == cb);
    40034409
    4004     ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbSet);
     4410    ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbSet);
    40054411
    40064412    return cbSet;
     
    40174423    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
    40184424
    4019     cbCreated = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, paSeg, pcSeg, cbData);
     4425    cbCreated = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, paSeg, pcSeg, cbData);
    40204426    Assert(!paSeg || cbData == cbCreated);
    40214427
     
    40434449    /* Continue */
    40444450    pIoCtx->fBlocked = false;
    4045     ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCompleted);
     4451    ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbCompleted);
    40464452
    40474453    /* Clear the pointer to next transfer function in case we have nothing to transfer anymore.
    40484454     * @todo: Find a better way to prevent vdIoCtxContinue from calling the read/write helper again. */
    4049     if (!pIoCtx->cbTransferLeft)
     4455    if (!pIoCtx->Req.Io.cbTransferLeft)
    40504456        pIoCtx->pfnIoCtxTransfer = NULL;
    40514457
     
    86179023
    86189024
    8619 VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCVDRANGE paRanges, unsigned cRanges)
     9025VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges)
    86209026{
    86219027    int rc;
     
    88619267}
    88629268
     9269VBOXDDU_DECL(int) VDAsyncDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges,
     9270                                       PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
     9271                                       void *pvUser1, void *pvUser2)
     9272{
     9273    int rc;
     9274    int rc2;
     9275    bool fLockWrite = false;
     9276    PVDIOCTX pIoCtx = NULL;
     9277
     9278    LogFlowFunc(("pDisk=%#p\n", pDisk));
     9279
     9280    do
     9281    {
     9282        /* sanity check */
     9283        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
     9284        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
     9285
     9286        rc2 = vdThreadStartWrite(pDisk);
     9287        AssertRC(rc2);
     9288        fLockWrite = true;
     9289
     9290        AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
     9291
     9292        pIoCtx = vdIoCtxDiscardAlloc(pDisk, paRanges, cRanges,
     9293                                     pfnComplete, pvUser1, pvUser2, NULL,
     9294                                     vdDiscardHelperAsync);
     9295        if (!pIoCtx)
     9296        {
     9297            rc = VERR_NO_MEMORY;
     9298            break;
     9299        }
     9300
     9301        rc = vdIoCtxProcess(pIoCtx);
     9302        if (rc == VINF_VD_ASYNC_IO_FINISHED)
     9303        {
     9304            if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
     9305                vdIoCtxFree(pDisk, pIoCtx);
     9306            else
     9307                rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
     9308        }
     9309        else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
     9310            vdIoCtxFree(pDisk, pIoCtx);
     9311    } while (0);
     9312
     9313    if (RT_UNLIKELY(fLockWrite) && (   rc == VINF_VD_ASYNC_IO_FINISHED
     9314                                    || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
     9315    {
     9316        rc2 = vdThreadFinishWrite(pDisk);
     9317        AssertRC(rc2);
     9318    }
     9319
     9320    LogFlowFunc(("returns %Rrc\n", rc));
     9321    return rc;
     9322}
     9323
  • trunk/src/VBox/Storage/VDI.cpp

    r38676 r38876  
    5757static int  vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
    5858static int  vdiUpdateHeaderAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx);
    59 static int  vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock, PVDIOCTX pIoCtx);
     59static int  vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock, PVDIOCTX pIoCtx,
     60                                    bool fUpdateHdr);
    6061
    6162/**
     
    849850 */
    850851static int vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock,
    851                                    PVDIOCTX pIoCtx)
    852 {
     852                                   PVDIOCTX pIoCtx, bool fUpdateHdr)
     853{
     854    int rc = VINF_SUCCESS;
     855
    853856    /* Update image header. */
    854     int rc = vdiUpdateHeaderAsync(pImage, pIoCtx);
     857    if (fUpdateHdr)
     858        rc = vdiUpdateHeaderAsync(pImage, pIoCtx);
     859
    855860    if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
    856861    {
     
    964969        rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, cbImage - pImage->cbTotalBlockData);
    965970    } while (0);
     971
     972    LogFlowFunc(("returns rc=%Rrc\n", rc));
     973    return rc;
     974}
     975
     976/**
     977 * Completion callback for meta/userdata reads or writes.
     978 *
     979 * @return  VBox status code.
     980 *          VINF_SUCCESS if everything was successful and the transfer can continue.
     981 *          VERR_VD_ASYNC_IO_IN_PROGRESS if there is another data transfer pending.
     982 * @param   pBackendData    The opaque backend data.
     983 * @param   pIoCtx          I/O context associated with this request.
     984 * @param   pvUser          Opaque user data passed during a read/write request.
     985 * @param   rcReq           Status code for the completed request.
     986 */
     987static DECLCALLBACK(int) vdiDiscardBlockAsyncUpdate(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
     988{
     989    int rc = VINF_SUCCESS;
     990    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     991    PVDIBLOCKDISCARDASYNC pDiscardAsync = (PVDIBLOCKDISCARDASYNC)pvUser;
     992
     993    switch (pDiscardAsync->enmState)
     994    {
     995        case VDIBLOCKDISCARDSTATE_READ_BLOCK:
     996        {
     997            PVDMETAXFER pMetaXfer;
     998            uint64_t u64Offset = (uint64_t)pDiscardAsync->idxLastBlock * pImage->cbTotalBlockData + pImage->offStartData;
     999            rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
     1000                                            pDiscardAsync->pvBlock, pImage->cbTotalBlockData, pIoCtx,
     1001                                            &pMetaXfer, vdiDiscardBlockAsyncUpdate, pDiscardAsync);
     1002            if (RT_FAILURE(rc))
     1003                break;
     1004
     1005            /* Release immediately and go to next step. */
     1006            vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
     1007            pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_WRITE_BLOCK;
     1008        }
     1009        case VDIBLOCKDISCARDSTATE_WRITE_BLOCK:
     1010        {
     1011            /* Block read complete. Write to the new location (discarded block). */
     1012            uint64_t u64Offset = (uint64_t)pDiscardAsync->ptrBlockDiscard * pImage->cbTotalBlockData + pImage->offStartData;
     1013            rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
     1014                                             pDiscardAsync->pvBlock, pImage->cbTotalBlockData, pIoCtx,
     1015                                             vdiDiscardBlockAsyncUpdate, pDiscardAsync);
     1016
     1017            pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_UPDATE_METADATA;
     1018            if (RT_FAILURE(rc))
     1019                break;
     1020        }
     1021        case VDIBLOCKDISCARDSTATE_UPDATE_METADATA:
     1022        {
     1023            int rc2;
     1024            uint64_t cbImage;
     1025
     1026            /* Block write complete. Update metadata. */
     1027            pImage->paBlocksRev[pDiscardAsync->idxLastBlock] = VDI_IMAGE_BLOCK_FREE;
     1028            pImage->paBlocks[pDiscardAsync->uBlock] = VDI_IMAGE_BLOCK_ZERO;
     1029
     1030            if (pDiscardAsync->idxLastBlock != pDiscardAsync->ptrBlockDiscard)
     1031            {
     1032                pImage->paBlocks[pDiscardAsync->uBlockLast] = pDiscardAsync->ptrBlockDiscard;
     1033                pImage->paBlocksRev[pDiscardAsync->ptrBlockDiscard] = pDiscardAsync->uBlockLast;
     1034
     1035                rc = vdiUpdateBlockInfoAsync(pImage, pDiscardAsync->uBlockLast, pIoCtx, false /* fUpdateHdr */);
     1036                if (   RT_FAILURE(rc)
     1037                    && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
     1038                    break;
     1039            }
     1040
     1041            setImageBlocksAllocated(&pImage->Header, pDiscardAsync->idxLastBlock);
     1042            rc = vdiUpdateBlockInfoAsync(pImage, pDiscardAsync->uBlock, pIoCtx, true /* fUpdateHdr */);
     1043            if (   RT_FAILURE(rc)
     1044                && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
     1045                break;
     1046
     1047            /* Set new file size. */
     1048            rc2 = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbImage);
     1049            if (RT_FAILURE(rc2))
     1050                break;
     1051
     1052            LogFlowFunc(("Set new size %llu\n", cbImage - pImage->cbTotalBlockData));
     1053            rc2 = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, cbImage - pImage->cbTotalBlockData);
     1054            if (RT_FAILURE(rc2))
     1055                rc = rc2;
     1056
     1057            /* Free discard state. */
     1058            RTMemFree(pDiscardAsync->pvBlock);
     1059            RTMemFree(pDiscardAsync);
     1060            break;
     1061        }
     1062        default:
     1063            AssertMsgFailed(("Invalid state %d\n", pDiscardAsync->enmState));
     1064    }
     1065
     1066    if (rc == VERR_VD_NOT_ENOUGH_METADATA)
     1067        rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
     1068
     1069    return rc;
     1070}
     1071
     1072/**
     1073 * Internal: Discard a whole block from the image filling the created hole with
     1074 * data from another block - async I/O version.
     1075 *
     1076 * @returns VBox status code.
     1077 * @param   pImage    VDI image instance data.
     1078 * @param   pIoCtx    I/O context associated with this request.
     1079 * @param   uBlock    The block to discard.
     1080 * @param   pvBlock   Memory to use for the I/O.
     1081 */
     1082static int vdiDiscardBlockAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx,
     1083                                unsigned uBlock, void *pvBlock)
     1084{
     1085    int rc = VINF_SUCCESS;
     1086    PVDIBLOCKDISCARDASYNC pDiscardAsync = NULL;
     1087
     1088    LogFlowFunc(("pImage=%#p uBlock=%u pvBlock=%#p\n",
     1089                 pImage, uBlock, pvBlock));
     1090
     1091    pDiscardAsync = (PVDIBLOCKDISCARDASYNC)RTMemAllocZ(sizeof(VDIBLOCKDISCARDASYNC));
     1092    if (RT_UNLIKELY(!pDiscardAsync))
     1093        return VERR_NO_MEMORY;
     1094
     1095    /* Init block discard state. */
     1096    pDiscardAsync->uBlock  = uBlock;
     1097    pDiscardAsync->pvBlock = pvBlock;
     1098    pDiscardAsync->ptrBlockDiscard = pImage->paBlocks[uBlock];
     1099    pDiscardAsync->idxLastBlock = getImageBlocksAllocated(&pImage->Header) - 1;
     1100    pDiscardAsync->uBlockLast = pImage->paBlocksRev[pDiscardAsync->idxLastBlock];
     1101
     1102    /*
     1103     * The block is empty, remove it.
     1104     * Read the last block of the image first.
     1105     */
     1106    if (pDiscardAsync->idxLastBlock != pDiscardAsync->ptrBlockDiscard)
     1107    {
     1108        LogFlowFunc(("Moving block [%u]=%u into [%u]=%u\n",
     1109                     pDiscardAsync->uBlockLast, pDiscardAsync->idxLastBlock,
     1110                     uBlock, pImage->paBlocks[uBlock]));
     1111        pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_READ_BLOCK;
     1112    }
     1113    else
     1114    {
     1115        pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_UPDATE_METADATA; /* Start immediately to shrink the image. */
     1116        LogFlowFunc(("Discard last block [%u]=%u\n", uBlock, pImage->paBlocks[uBlock]));
     1117    }
     1118
     1119    /* Call the update callback directly. */
     1120    rc = vdiDiscardBlockAsyncUpdate(pImage, pIoCtx, pDiscardAsync, VINF_SUCCESS);
    9661121
    9671122    LogFlowFunc(("returns rc=%Rrc\n", rc));
     
    21812336                setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
    21822337
    2183                 rc = vdiUpdateBlockInfoAsync(pImage, uBlock, pIoCtx);
     2338                rc = vdiUpdateBlockInfoAsync(pImage, uBlock, pIoCtx, true /* fUpdateHdr */);
    21842339                if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
    21852340                    goto out;
     
    27802935    if (pvBlock)
    27812936        RTMemFree(pvBlock);
     2937
     2938    LogFlowFunc(("returns %Rrc\n", rc));
     2939    return rc;
     2940}
     2941
     2942/** @copydoc VBOXHDDBACKEND::pfnDiscard */
     2943static DECLCALLBACK(int) vdiAsyncDiscard(void *pBackendData, PVDIOCTX pIoCtx,
     2944                                         uint64_t uOffset, size_t cbDiscard,
     2945                                         size_t *pcbPreAllocated,
     2946                                         size_t *pcbPostAllocated,
     2947                                         size_t *pcbActuallyDiscarded,
     2948                                         void   **ppbmAllocationBitmap,
     2949                                         unsigned fDiscard)
     2950{
     2951    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     2952    unsigned uBlock;
     2953    unsigned offDiscard;
     2954    int rc = VINF_SUCCESS;
     2955    void *pvBlock = NULL;
     2956
     2957    LogFlowFunc(("pBackendData=%#p pIoCtx=%#p uOffset=%llu cbDiscard=%zu pcbPreAllocated=%#p pcbPostAllocated=%#p pcbActuallyDiscarded=%#p ppbmAllocationBitmap=%#p fDiscard=%#x\n",
     2958                 pBackendData, pIoCtx, uOffset, cbDiscard, pcbPreAllocated, pcbPostAllocated, pcbActuallyDiscarded, ppbmAllocationBitmap, fDiscard));
     2959
     2960    AssertPtr(pImage);
     2961    Assert(!(uOffset % 512));
     2962    Assert(!(cbDiscard % 512));
     2963
     2964    AssertMsgReturn(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
     2965                    ("Image is readonly\n"), VERR_VD_IMAGE_READ_ONLY);
     2966    AssertMsgReturn(   uOffset + cbDiscard <= getImageDiskSize(&pImage->Header)
     2967                    && cbDiscard,
     2968                    ("Invalid parameters uOffset=%llu cbDiscard=%zu\n",
     2969                     uOffset, cbDiscard),
     2970                     VERR_INVALID_PARAMETER);
     2971
     2972    do
     2973    {
     2974        AssertMsgBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
     2975                           ("Image is opened readonly\n"),
     2976                           rc = VERR_VD_IMAGE_READ_ONLY);
     2977
     2978        AssertMsgBreakStmt(cbDiscard,
     2979                           ("cbDiscard=%u\n", cbDiscard),
     2980                           rc = VERR_INVALID_PARAMETER);
     2981
     2982        /* Calculate starting block number and offset inside it. */
     2983        uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
     2984        offDiscard = (unsigned)uOffset & pImage->uBlockMask;
     2985
     2986        /* Clip range to at most the rest of the block. */
     2987        cbDiscard = RT_MIN(cbDiscard, getImageBlockSize(&pImage->Header) - offDiscard);
     2988        Assert(!(cbDiscard % 512));
     2989
     2990        if (pcbPreAllocated)
     2991            *pcbPreAllocated = 0;
     2992
     2993        if (pcbPostAllocated)
     2994            *pcbPostAllocated = 0;
     2995
     2996        if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
     2997        {
     2998            uint8_t *pbBlockData;
     2999            size_t cbPreAllocated, cbPostAllocated;
     3000
     3001            cbPreAllocated = offDiscard % getImageBlockSize(&pImage->Header);
     3002            cbPostAllocated = getImageBlockSize(&pImage->Header) - cbDiscard - cbPreAllocated;
     3003
     3004            /* Read the block data. */
     3005            pvBlock = RTMemAlloc(pImage->cbTotalBlockData);
     3006            if (!pvBlock)
     3007            {
     3008                rc = VERR_NO_MEMORY;
     3009                break;
     3010            }
     3011
     3012            if (!cbPreAllocated && !cbPostAllocated)
     3013            {
     3014                /*
     3015                 * Discarding a whole block, don't check for allocated sectors.
     3016                 * It is possible to just remove the whole block which avoids
     3017                 * one read and checking the whole block for data.
     3018                 */
     3019                rc = vdiDiscardBlockAsync(pImage, pIoCtx, uBlock, pvBlock);
     3020            }
     3021            else if (fDiscard & VD_DISCARD_MARK_UNUSED)
     3022            {
     3023                /* Just zero out the given range. */
     3024                memset(pvBlock, 0, cbDiscard);
     3025
     3026                uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData + offDiscard;
     3027                rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
     3028                                                 u64Offset, pvBlock, cbDiscard, pIoCtx,
     3029                                                 NULL, NULL);
     3030                RTMemFree(pvBlock);
     3031            }
     3032            else
     3033            {
     3034                /*
     3035                 * Read complete block as metadata, the I/O context has no memory buffer
     3036                 * and we need to access the content directly anyway.
     3037                 */
     3038                PVDMETAXFER pMetaXfer;
     3039                pbBlockData = (uint8_t *)pvBlock + pImage->offStartBlockData;
     3040
     3041                uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData;
     3042                rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
     3043                                                pbBlockData, pImage->cbTotalBlockData,
     3044                                                pIoCtx, &pMetaXfer, NULL, NULL);
     3045                if (RT_FAILURE(rc))
     3046                    break;
     3047
     3048                vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
     3049
     3050                /* Clear data. */
     3051                memset(pbBlockData + offDiscard , 0, cbDiscard);
     3052
     3053                Assert(!(cbDiscard % 4));
     3054                Assert(getImageBlockSize(&pImage->Header) * 8 <= UINT32_MAX);
     3055                if (ASMBitFirstSet((volatile void *)pbBlockData, getImageBlockSize(&pImage->Header) * 8) == -1)
     3056                    rc = vdiDiscardBlockAsync(pImage, pIoCtx, uBlock, pvBlock);
     3057                else
     3058                {
     3059                    /* Block has data, create allocation bitmap. */
     3060                    *pcbPreAllocated = cbPreAllocated;
     3061                    *pcbPostAllocated = cbPostAllocated;
     3062                    *ppbmAllocationBitmap = vdiAllocationBitmapCreate(pbBlockData, getImageBlockSize(&pImage->Header));
     3063                    if (RT_UNLIKELY(!*ppbmAllocationBitmap))
     3064                        rc = VERR_NO_MEMORY;
     3065                    else
     3066                        rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET;
     3067
     3068                    RTMemFree(pvBlock);
     3069                }
     3070            } /* if: no complete block discarded */
     3071        } /* if: Block is allocated. */
     3072        /* else: nothing to do. */
     3073    } while (0);
     3074
     3075    if (pcbActuallyDiscarded)
     3076        *pcbActuallyDiscarded = cbDiscard;
    27823077
    27833078    LogFlowFunc(("returns %Rrc\n", rc));
     
    28833178    vdiResize,
    28843179    /* pfnDiscard */
    2885     vdiDiscard
     3180    vdiDiscard,
     3181    /* pfnAsyncDiscard */
     3182    vdiAsyncDiscard
    28863183};
  • trunk/src/VBox/Storage/VDICore.h

    r38621 r38876  
    556556} VDIIMAGEDESC, *PVDIIMAGEDESC;
    557557
     558/**
     559 * Async block discard states.
     560 */
     561typedef enum VDIBLOCKDISCARDSTATE
     562{
     563    /** Invalid. */
     564    VDIBLOCKDISCARDSTATE_INVALID = 0,
     565    /** Read the last block. */
     566    VDIBLOCKDISCARDSTATE_READ_BLOCK,
     567    /** Write block into the hole. */
     568    VDIBLOCKDISCARDSTATE_WRITE_BLOCK,
     569    /** Update metadata. */
     570    VDIBLOCKDISCARDSTATE_UPDATE_METADATA,
     571    /** 32bit hack. */
     572    VDIBLOCKDISCARDSTATE_32BIT_HACK = 0x7fffffff
     573} VDIBLOCKDISCARDSTATE;
     574
     575/**
     576 * Async block discard structure.
     577 */
     578typedef struct VDIBLOCKDISCARDASYNC
     579{
     580    /** State of the block discard. */
     581    VDIBLOCKDISCARDSTATE    enmState;
     582    /** Pointer to the block data. */
     583    void                   *pvBlock;
     584    /** Block index in the block table. */
     585    unsigned                uBlock;
     586    /** Block pointer to the block to discard. */
     587    VDIIMAGEBLOCKPOINTER    ptrBlockDiscard;
     588    /** Index of the last block in the reverse block table. */
     589    unsigned                idxLastBlock;
     590    /** Index of the last block in the block table (gathered from the reverse block table). */
     591    unsigned                uBlockLast;
     592} VDIBLOCKDISCARDASYNC, *PVDIBLOCKDISCARDASYNC;
     593
    558594#endif
    559595
  • trunk/src/VBox/Storage/VHD.cpp

    r38621 r38876  
    31413141    vhdResize,
    31423142    /* pfnDiscard */
     3143    NULL,
     3144    /* pfnAsyncDiscard */
    31433145    NULL
    31443146};
  • trunk/src/VBox/Storage/VMDK.cpp

    r38819 r38876  
    71927192    NULL,
    71937193    /* pfnDiscard */
     7194    NULL,
     7195    /* pfnAsyncDiscard */
    71947196    NULL
    71957197};
  • trunk/src/VBox/Storage/testcase/tstVDIo.cpp

    r38644 r38876  
    134134    VDIOREQTXDIR_READ = 0,
    135135    VDIOREQTXDIR_WRITE,
    136     VDIOREQTXDIR_FLUSH
     136    VDIOREQTXDIR_FLUSH,
     137    VDIOREQTXDIR_DISCARD
    137138} VDIOREQTXDIR;
    138139
     
    143144{
    144145    /** Transfer type. */
    145     VDIOREQTXDIR enmTxDir;
     146    VDIOREQTXDIR  enmTxDir;
    146147    /** slot index. */
    147     unsigned  idx;
     148    unsigned      idx;
    148149    /** Start offset. */
    149     uint64_t  off;
     150    uint64_t      off;
    150151    /** Size to transfer. */
    151     size_t    cbReq;
     152    size_t        cbReq;
    152153    /** S/G Buffer */
    153     RTSGBUF   SgBuf;
     154    RTSGBUF       SgBuf;
    154155    /** Data segment */
    155     RTSGSEG   DataSeg;
     156    RTSGSEG       DataSeg;
    156157    /** Flag whether the request is outstanding or not. */
    157158    volatile bool fOutstanding;
     
    385386    /* pcszName    chId enmType                          fFlags */
    386387    {"disk",       'd', VDSCRIPTARGTYPE_STRING,          VDSCRIPTARGDESC_FLAG_MANDATORY},
    387     {"off",        'o', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
    388     {"size",       's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
     388    {"async",      'a', VDSCRIPTARGTYPE_BOOL,            0},
     389    {"ranges",     'r', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
    389390};
    390391
     
    955956                                            break;
    956957                                        }
     958                                        case VDIOREQTXDIR_DISCARD:
     959                                            AssertMsgFailed(("Invalid\n"));
    957960                                    }
    958961
     
    983986                                            break;
    984987                                        }
     988                                        case VDIOREQTXDIR_DISCARD:
     989                                            AssertMsgFailed(("Invalid\n"));
    985990                                    }
    986991
     
    10281033                                            case VDIOREQTXDIR_FLUSH:
    10291034                                                break;
     1035                                            case VDIOREQTXDIR_DISCARD:
     1036                                                AssertMsgFailed(("Invalid\n"));
    10301037                                        }
    10311038
     
    12641271    const char *pcszDisk = NULL;
    12651272    PVDDISK pDisk = NULL;
    1266     uint64_t off;
    1267     size_t cbDiscard;
     1273    bool fAsync = false;
     1274    const char *pcszRanges = NULL;
    12681275
    12691276    for (unsigned i = 0; i < cScriptArgs; i++)
     
    12761283                break;
    12771284            }
    1278             case 'o':
    1279             {
    1280                 off = paScriptArgs[i].u.u64;
    1281                 break;
    1282             }
    1283             case 's':
    1284             {
    1285                 cbDiscard = paScriptArgs[i].u.u64;
    1286                 break;
    1287             }
    1288 
     1285            case 'a':
     1286            {
     1287                fAsync = paScriptArgs[i].u.fFlag;
     1288                break;
     1289            }
     1290            case 'r':
     1291            {
     1292                pcszRanges = paScriptArgs[i].u.pcszString;
     1293                break;
     1294            }
    12891295            default:
    12901296                AssertMsgFailed(("Invalid argument given!\n"));
    12911297        }
    1292 
    1293         if (RT_FAILURE(rc))
    1294             break;
    1295     }
    1296 
    1297     if (RT_SUCCESS(rc))
    1298     {
    1299         pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
    1300         if (!pDisk)
    1301             rc = VERR_NOT_FOUND;
    1302         else
    1303         {
    1304             VDRANGE Range;
    1305 
    1306             Range.offStart = off;
    1307             Range.cbRange = cbDiscard;
    1308 
    1309             rc = VDDiscardRanges(pDisk->pVD, &Range, 1);
     1298    }
     1299
     1300    pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
     1301    if (!pDisk)
     1302        rc = VERR_NOT_FOUND;
     1303    else
     1304    {
     1305        unsigned cRanges = 0;
     1306        PRTRANGE paRanges = NULL;
     1307
     1308        /*
     1309         * Parse the range string which should look like this:
     1310         * n,off1,cb1,off2,cb2,...
     1311         *
     1312         * <n> gives the number of ranges in the string and every off<i>,cb<i>
     1313         * pair afterwards is a start offset + number of bytes to discard entry.
     1314         */
     1315        do
     1316        {
     1317            rc = RTStrToUInt32Ex(pcszRanges, (char **)&pcszRanges, 10, &cRanges);
     1318            if (RT_FAILURE(rc) && (rc != VWRN_TRAILING_CHARS))
     1319                break;
     1320
     1321            if (!cRanges)
     1322            {
     1323                rc = VERR_INVALID_PARAMETER;
     1324                break;
     1325            }
     1326
     1327            paRanges = (PRTRANGE)RTMemAllocZ(cRanges * sizeof(RTRANGE));
     1328            if (!paRanges)
     1329            {
     1330                rc = VERR_NO_MEMORY;
     1331                break;
     1332            }
     1333
     1334            if (*pcszRanges != ',')
     1335            {
     1336                rc = VERR_INVALID_PARAMETER;
     1337                break;
     1338            }
     1339
     1340            pcszRanges++;
     1341
     1342            /* Retrieve each pair from the string. */
     1343            for (unsigned i = 0; i < cRanges; i++)
     1344            {
     1345                uint64_t off;
     1346                uint32_t cb;
     1347
     1348                rc = RTStrToUInt64Ex(pcszRanges, (char **)&pcszRanges, 10, &off);
     1349                if (RT_FAILURE(rc) && (rc != VWRN_TRAILING_CHARS))
     1350                    break;
     1351
     1352                if (*pcszRanges != ',')
     1353                {
     1354                    switch (*pcszRanges)
     1355                    {
     1356                        case 'k':
     1357                        case 'K':
     1358                        {
     1359                            off *= _1K;
     1360                            break;
     1361                        }
     1362                        case 'm':
     1363                        case 'M':
     1364                        {
     1365                            off *= _1M;
     1366                            break;
     1367                        }
     1368                        case 'g':
     1369                        case 'G':
     1370                        {
     1371                            off *= _1G;
     1372                            break;
     1373                        }
     1374                        default:
     1375                        {
     1376                            RTPrintf("Invalid size suffix '%s'\n", pcszRanges);
     1377                            rc = VERR_INVALID_PARAMETER;
     1378                        }
     1379                    }
     1380                    if (RT_SUCCESS(rc))
     1381                        pcszRanges++;
     1382                }
     1383
     1384                if (*pcszRanges != ',')
     1385                {
     1386                    rc = VERR_INVALID_PARAMETER;
     1387                    break;
     1388                }
     1389
     1390                pcszRanges++;
     1391
     1392                rc = RTStrToUInt32Ex(pcszRanges, (char **)&pcszRanges, 10, &cb);
     1393                if (RT_FAILURE(rc) && (rc != VWRN_TRAILING_CHARS))
     1394                    break;
     1395
     1396                if (*pcszRanges != ',')
     1397                {
     1398                    switch (*pcszRanges)
     1399                    {
     1400                        case 'k':
     1401                        case 'K':
     1402                        {
     1403                            cb *= _1K;
     1404                            break;
     1405                        }
     1406                        case 'm':
     1407                        case 'M':
     1408                        {
     1409                            cb *= _1M;
     1410                            break;
     1411                        }
     1412                        case 'g':
     1413                        case 'G':
     1414                        {
     1415                            cb *= _1G;
     1416                            break;
     1417                        }
     1418                        default:
     1419                        {
     1420                            RTPrintf("Invalid size suffix '%s'\n", pcszRanges);
     1421                            rc = VERR_INVALID_PARAMETER;
     1422                        }
     1423                    }
     1424                    if (RT_SUCCESS(rc))
     1425                        pcszRanges++;
     1426                }
     1427
     1428                if (   *pcszRanges != ','
     1429                    && !(i == cRanges - 1 && *pcszRanges == '\0'))
     1430                {
     1431                    rc = VERR_INVALID_PARAMETER;
     1432                    break;
     1433                }
     1434
     1435                pcszRanges++;
     1436
     1437                paRanges[i].offStart = off;
     1438                paRanges[i].cbRange  = cb;
     1439            }
     1440        } while (0);
     1441
     1442        if (RT_SUCCESS(rc))
     1443        {
     1444            if (!fAsync)
     1445                rc = VDDiscardRanges(pDisk->pVD, paRanges, cRanges);
     1446            else
     1447            {
     1448                VDIOREQ IoReq;
     1449                RTSEMEVENT EventSem;
     1450
     1451                rc = RTSemEventCreate(&EventSem);
     1452                if (RT_SUCCESS(rc))
     1453                {
     1454                    memset(&IoReq, 0, sizeof(VDIOREQ));
     1455                    IoReq.enmTxDir = VDIOREQTXDIR_FLUSH;
     1456                    IoReq.pvUser   = pDisk;
     1457                    IoReq.idx      = 0;
     1458                    rc = VDAsyncDiscardRanges(pDisk->pVD, paRanges, cRanges, tstVDIoTestReqComplete, &IoReq, EventSem);
     1459                    if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
     1460                    {
     1461                        rc = RTSemEventWait(EventSem, RT_INDEFINITE_WAIT);
     1462                        AssertRC(rc);
     1463                    }
     1464                    else if (rc == VINF_VD_ASYNC_IO_FINISHED)
     1465                        rc = VINF_SUCCESS;
     1466
     1467                    RTSemEventDestroy(EventSem);
     1468                }
     1469            }
     1470
    13101471            if (   RT_SUCCESS(rc)
    13111472                && pDisk->pMemDiskVerify)
    13121473            {
    1313                 void *pv = RTMemAllocZ(cbDiscard);
    1314                 if (pv)
     1474                for (unsigned i = 0; i < cRanges; i++)
    13151475                {
    1316                     RTSGSEG SgSeg;
    1317                     RTSGBUF SgBuf;
    1318 
    1319                     SgSeg.pvSeg = pv;
    1320                     SgSeg.cbSeg = cbDiscard;
    1321                     RTSgBufInit(&SgBuf, &SgSeg, 1);
    1322                     rc = VDMemDiskWrite(pDisk->pMemDiskVerify, off, cbDiscard, &SgBuf);
    1323                     RTMemFree(pv);
     1476                    void *pv = RTMemAllocZ(paRanges[i].cbRange);
     1477                    if (pv)
     1478                    {
     1479                        RTSGSEG SgSeg;
     1480                        RTSGBUF SgBuf;
     1481
     1482                        SgSeg.pvSeg = pv;
     1483                        SgSeg.cbSeg = paRanges[i].cbRange;
     1484                        RTSgBufInit(&SgBuf, &SgSeg, 1);
     1485                        rc = VDMemDiskWrite(pDisk->pMemDiskVerify, paRanges[i].offStart, paRanges[i].cbRange, &SgBuf);
     1486                        RTMemFree(pv);
     1487                    }
     1488                    else
     1489                    {
     1490                        rc = VERR_NO_MEMORY;
     1491                        break;
     1492                    }
    13241493                }
    1325                 else
    1326                     rc = VERR_NO_MEMORY;
    1327             }
    1328         }
     1494            }
     1495        }
     1496
     1497        if (paRanges)
     1498            RTMemFree(paRanges);
    13291499    }
    13301500
     
    16471817                    case VDDBGIOLOGREQ_DISCARD:
    16481818                    {
    1649                         PVDRANGE paRanges = NULL;
     1819                        PRTRANGE paRanges = NULL;
    16501820                        unsigned cRanges = 0;
    16511821
     
    27262896            }
    27272897            case VDIOREQTXDIR_FLUSH:
     2898            case VDIOREQTXDIR_DISCARD:
    27282899                break;
    27292900        }
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette