VirtualBox

Changeset 28114 in vbox for trunk


Ignore:
Timestamp:
Apr 8, 2010 8:35:17 PM (15 years ago)
Author:
vboxsync
Message:

DrvDiskIntegrity: Support for async I/O

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp

    r27561 r28114  
    3232#include <iprt/mem.h>
    3333#include <iprt/message.h>
     34#include <iprt/sg.h>
    3435
    3536#include "Builtins.h"
     
    3940*   Structures and Typedefs                                                    *
    4041*******************************************************************************/
     42
     43/**
     44 * async I/O request.
     45 */
     46typedef struct DRVDISKAIOREQ
     47{
     48    /** Flag whether this is a read or write request. */
     49    bool      fRead;
     50    /** Start offset. */
     51    uint64_t  off;
     52    /** Transfer size. */
     53    size_t    cbTransfer;
     54    /** Segment array. */
     55    PCRTSGSEG paSeg;
     56    /** Number of array entries. */
     57    unsigned  cSeg;
     58    /** User argument */
     59    void     *pvUser;
     60} DRVDISKAIOREQ, *PDRVDISKAIOREQ;
    4161
    4262/**
     
    85105    PDMIMEDIA               IMedia;
    86106
     107    /** Pointer to the media async driver below us.
     108     * This is NULL if the media is not mounted. */
     109    PPDMIMEDIAASYNC         pDrvMediaAsync;
     110    /** Our media async interface */
     111    PDMIMEDIAASYNC          IMediaAsync;
     112
     113    /** The async media port interface above. */
     114    PPDMIMEDIAASYNCPORT     pDrvMediaAsyncPort;
     115    /** Our media async port interface */
     116    PDMIMEDIAASYNCPORT      IMediaAsyncPort;
     117
    87118    /** AVL tree containing the disk blocks to check. */
    88119    PAVLRFOFFTREE           pTreeSegments;
     
    90121
    91122
    92 /* -=-=-=-=- IMedia -=-=-=-=- */
    93 
    94 /** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIA. */
    95 #define PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface)        ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMedia)) )
    96 
    97 /*******************************************************************************
    98 *   Media interface methods                                                    *
    99 *******************************************************************************/
    100 
    101 /** @copydoc PDMIMEDIA::pfnRead */
    102 static DECLCALLBACK(int) drvdiskintRead(PPDMIMEDIA pInterface,
    103                                         uint64_t off, void *pvBuf, size_t cbRead)
    104 {
    105     PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
    106     int rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, pvBuf, cbRead);
    107     if (RT_FAILURE(rc))
    108         return rc;
    109 
    110     Assert(off % 512 == 0);
    111     Assert(cbRead % 512 == 0);
    112 
    113     /* Compare read data */
    114     size_t cbLeft   = cbRead;
    115     RTFOFF offCurr  = (RTFOFF)off;
    116     uint8_t *pbBuf  = (uint8_t *)pvBuf;
    117 
    118     while (cbLeft)
     123/**
     124 * Allocate a new I/O request.
     125 *
     126 * @returns New I/O request.
     127 * @param   fRead         Flag whether this is a read or a write.
     128 * @param   off           Start offset.
     129 * @param   paSeg         Segment array.
     130 * @param   cSeg          Number of segments.
     131 * @param   cbTransfer    Number of bytes to transfer.
     132 * @param   pvUser        User argument.
     133 */
     134static PDRVDISKAIOREQ drvdiskintIoReqAlloc(bool fRead, uint64_t off, PCRTSGSEG paSeg,
     135                                           unsigned cSeg, size_t cbTransfer, void *pvUser)
     136{
     137    PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)RTMemAlloc(sizeof(DRVDISKAIOREQ));
     138
     139    if (RT_LIKELY(pIoReq))
    119140    {
    120         PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
    121         size_t cbRange  = 0;
    122         bool fCmp       = false;
    123         unsigned offSeg = 0;
    124 
    125         if (!pSeg)
    126         {
    127             /* Get next segment */
    128             pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
    129             if (!pSeg)
    130             {
    131                 /* No data in the tree for this read. Assume everything is ok. */
    132                 cbRange = cbLeft;
    133             }
    134             else if (offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
    135                 cbRange = cbLeft;
    136             else
    137                 cbRange = pSeg->Core.Key - offCurr;
    138         }
    139         else
    140         {
    141             fCmp    = true;
    142             offSeg  = offCurr - pSeg->Core.Key;
    143             cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
    144         }
    145 
    146         if (   fCmp
    147             && memcmp(pbBuf, pSeg->pbSeg + offSeg, cbRange))
    148         {
    149             unsigned offWrong = 0;
    150             for (offWrong = 0; offWrong < cbRange; offWrong++)
    151                 if (pbBuf[offWrong] != pSeg->pbSeg[offSeg + offWrong])
    152                 {
    153                     /* Corrupted disk, print I/O log entry of the last write which accessed this range. */
    154                     uint32_t cSector = (offSeg + offWrong) / 512;
    155                     AssertMsg(cSector < pSeg->cIoLogEntries, ("Internal bug!\n"));
    156 
    157                     RTMsgError("Corrupted disk at offset %llu (%u bytes in the current read buffer)!\n",
    158                                offCurr + offWrong, offWrong);
    159                     RTMsgError("Last write to this sector started at offset %llu with %u bytes (%u references to this log entry)\n",
    160                                pSeg->apIoLog[cSector]->off,
    161                                pSeg->apIoLog[cSector]->cbWrite,
    162                                pSeg->apIoLog[cSector]->cRefs);
    163                     RTAssertDebugBreak();
    164                 }
    165         }
    166 
    167         offCurr += cbRange;
    168         cbLeft  -= cbRange;
    169         pbBuf   += cbRange;
     141        pIoReq->fRead      = fRead;
     142        pIoReq->off        = off;
     143        pIoReq->cbTransfer = cbTransfer;
     144        pIoReq->paSeg      = paSeg;
     145        pIoReq->cSeg       = cSeg;
     146        pIoReq->pvUser     = pvUser;
    170147    }
    171148
    172     return rc;
    173 }
    174 
    175 /** @copydoc PDMIMEDIA::pfnWrite */
    176 static DECLCALLBACK(int) drvdiskintWrite(PPDMIMEDIA pInterface,
    177                                          uint64_t off, const void *pvBuf,
    178                                          size_t cbWrite)
    179 {
    180     PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
    181     int rc = pThis->pDrvMedia->pfnWrite(pThis->pDrvMedia, off, pvBuf, cbWrite);
    182     if (RT_FAILURE(rc))
    183         return rc;
     149    return pIoReq;
     150}
     151
     152/**
     153 * Record a successful write to the virtual disk.
     154 *
     155 * @returns VBox status code.
     156 * @param   pThis    Disk integrity driver instance data.
     157 * @param   paSeg    Segment array of the write to record.
     158 * @param   cSeg     Number of segments.
     159 * @param   off      Start offset.
     160 * @param   cbWrite  Number of bytes to record.
     161 */
     162static int drvdiskintWriteRecord(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsigned cSeg,
     163                                 uint64_t off, size_t cbWrite)
     164{
     165    int rc = VINF_SUCCESS;
    184166
    185167    /* Update the segments */
    186168    size_t cbLeft   = cbWrite;
    187169    RTFOFF offCurr  = (RTFOFF)off;
    188     uint8_t *pbBuf  = (uint8_t *)pvBuf;
     170    RTSGBUF SgBuf;
    189171    PIOLOGENT pIoLogEnt = (PIOLOGENT)RTMemAllocZ(sizeof(IOLOGENT));
    190172    if (!pIoLogEnt)
     
    194176    pIoLogEnt->cbWrite = cbWrite;
    195177    pIoLogEnt->cRefs   = 0;
     178
     179    RTSgBufInit(&SgBuf, paSeg, cSeg);
    196180
    197181    while (cbLeft)
     
    243227        {
    244228            AssertPtr(pSeg);
    245             memcpy(pSeg->pbSeg + offSeg, pbBuf, cbRange);
     229            RTSgBufCopyToBuf(&SgBuf, pSeg->pbSeg + offSeg, cbRange);
    246230
    247231            /* Update the I/O log pointers */
     
    269253            }
    270254        }
     255        else
     256            RTSgBufAdvance(&SgBuf, cbRange);
    271257
    272258        offCurr += cbRange;
    273259        cbLeft  -= cbRange;
    274         pbBuf   += cbRange;
    275260    }
    276261
     262    return rc;
     263}
     264
     265/**
     266 * Verifies a read request.
     267 *
     268 * @returns VBox status code.
     269 * @param   pThis    Disk integrity driver instance data.
     270 * @param   paSeg    Segment array of the containing the data buffers to verify.
     271 * @param   cSeg     Number of segments.
     272 * @param   off      Start offset.
     273 * @param   cbWrite  Number of bytes to verify.
     274 */
     275static int drvdiskintReadVerify(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsigned cSeg,
     276                                uint64_t off, size_t cbRead)
     277{
     278    int rc = VINF_SUCCESS;
     279
     280    Assert(off % 512 == 0);
     281    Assert(cbRead % 512 == 0);
     282
     283    /* Compare read data */
     284    size_t cbLeft   = cbRead;
     285    RTFOFF offCurr  = (RTFOFF)off;
     286    RTSGBUF SgBuf;
     287
     288    RTSgBufInit(&SgBuf, paSeg, cSeg);
     289
     290    while (cbLeft)
     291    {
     292        PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
     293        size_t cbRange  = 0;
     294        bool fCmp       = false;
     295        unsigned offSeg = 0;
     296
     297        if (!pSeg)
     298        {
     299            /* Get next segment */
     300            pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
     301            if (!pSeg)
     302            {
     303                /* No data in the tree for this read. Assume everything is ok. */
     304                cbRange = cbLeft;
     305            }
     306            else if (offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
     307                cbRange = cbLeft;
     308            else
     309                cbRange = pSeg->Core.Key - offCurr;
     310        }
     311        else
     312        {
     313            fCmp    = true;
     314            offSeg  = offCurr - pSeg->Core.Key;
     315            cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
     316        }
     317
     318        if (fCmp)
     319        {
     320            RTSGSEG Seg;
     321            RTSGBUF SgBufCmp;
     322
     323            Seg.cbSeg = cbRange;
     324            Seg.pvSeg = pSeg->pbSeg + offSeg;
     325
     326            RTSgBufInit(&SgBufCmp, &Seg, 1);
     327            if (RTSgBufCmp(&SgBuf, &SgBufCmp, cbRange))
     328            {
     329                unsigned offWrong = 0;
     330#if 0
     331                for (offWrong = 0; offWrong < cbRange; offWrong++)
     332                    if (pbBuf[offWrong] != pSeg->pbSeg[offSeg + offWrong])
     333#endif
     334                    {
     335                        /* Corrupted disk, print I/O log entry of the last write which accessed this range. */
     336                        uint32_t cSector = (offSeg + offWrong) / 512;
     337                        AssertMsg(cSector < pSeg->cIoLogEntries, ("Internal bug!\n"));
     338
     339                        RTMsgError("Corrupted disk at offset %llu (%u bytes in the current read buffer)!\n",
     340                                   offCurr + offWrong, offWrong);
     341                        RTMsgError("Last write to this sector started at offset %llu with %u bytes (%u references to this log entry)\n",
     342                                   pSeg->apIoLog[cSector]->off,
     343                                   pSeg->apIoLog[cSector]->cbWrite,
     344                                   pSeg->apIoLog[cSector]->cRefs);
     345                        RTAssertDebugBreak();
     346                    }
     347            }
     348        }
     349
     350        offCurr += cbRange;
     351        cbLeft  -= cbRange;
     352        RTSgBufAdvance(&SgBuf, cbRange);
     353    }
     354
     355    return rc;
     356}
     357
     358/* -=-=-=-=- IMedia -=-=-=-=- */
     359
     360/** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIA. */
     361#define PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface)        ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMedia)) )
     362/** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIAASYNC. */
     363#define PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface)   ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaAsync)) )
     364
     365/*******************************************************************************
     366*   Media interface methods                                                    *
     367*******************************************************************************/
     368
     369/** @copydoc PDMIMEDIA::pfnRead */
     370static DECLCALLBACK(int) drvdiskintRead(PPDMIMEDIA pInterface,
     371                                        uint64_t off, void *pvBuf, size_t cbRead)
     372{
     373    PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
     374    int rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, pvBuf, cbRead);
     375    if (RT_FAILURE(rc))
     376        return rc;
     377
     378    /* Verify the read. */
     379    RTSGSEG Seg;
     380    Seg.cbSeg = cbRead;
     381    Seg.pvSeg = pvBuf;
     382    return drvdiskintReadVerify(pThis, &Seg, 1, off, cbRead);
     383}
     384
     385/** @copydoc PDMIMEDIA::pfnWrite */
     386static DECLCALLBACK(int) drvdiskintWrite(PPDMIMEDIA pInterface,
     387                                         uint64_t off, const void *pvBuf,
     388                                         size_t cbWrite)
     389{
     390    PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
     391    int rc = pThis->pDrvMedia->pfnWrite(pThis->pDrvMedia, off, pvBuf, cbWrite);
     392    if (RT_FAILURE(rc))
     393        return rc;
     394
     395    /* Record the write. */
     396    RTSGSEG Seg;
     397    Seg.cbSeg = cbWrite;
     398    Seg.pvSeg = (void *)pvBuf;
     399    return drvdiskintWriteRecord(pThis, &Seg, 1, off, cbWrite);
     400}
     401
     402static DECLCALLBACK(int) drvdiskintStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
     403                                             PCRTSGSEG paSeg, unsigned cSeg,
     404                                             size_t cbRead, void *pvUser)
     405{
     406     LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d\n pvUser=%#p", __FUNCTION__,
     407             uOffset, paSeg, cSeg, cbRead, pvUser));
     408    PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface);
     409    PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(true, uOffset, paSeg, cSeg, cbRead, pvUser);
     410    AssertPtr(pIoReq);
     411
     412    int rc = pThis->pDrvMediaAsync->pfnStartRead(pThis->pDrvMediaAsync, uOffset, paSeg, cSeg,
     413                                                 cbRead, pIoReq);
     414    if (rc == VINF_VD_ASYNC_IO_FINISHED)
     415    {
     416        /* Verify the read now. */
     417        int rc2 = drvdiskintReadVerify(pThis, paSeg, cSeg, uOffset, cbRead);
     418        AssertRC(rc2);
     419        RTMemFree(pIoReq);
     420    }
     421    else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
     422        RTMemFree(pIoReq);
     423
     424    LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
     425    return rc;
     426}
     427
     428static DECLCALLBACK(int) drvdiskintStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
     429                                              PCRTSGSEG paSeg, unsigned cSeg,
     430                                              size_t cbWrite, void *pvUser)
     431{
     432     LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d\n pvUser=%#p", __FUNCTION__,
     433             uOffset, paSeg, cSeg, cbWrite, pvUser));
     434    PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface);
     435    PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(false, uOffset, paSeg, cSeg, cbWrite, pvUser);
     436    AssertPtr(pIoReq);
     437
     438    int rc = pThis->pDrvMediaAsync->pfnStartWrite(pThis->pDrvMediaAsync, uOffset, paSeg, cSeg,
     439                                                  cbWrite, pIoReq);
     440    if (rc == VINF_VD_ASYNC_IO_FINISHED)
     441    {
     442        /* Verify the read now. */
     443        int rc2 = drvdiskintWriteRecord(pThis, paSeg, cSeg, uOffset, cbWrite);
     444        AssertRC(rc2);
     445        RTMemFree(pIoReq);
     446    }
     447    else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
     448        RTMemFree(pIoReq);
     449
     450    LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
    277451    return rc;
    278452}
     
    338512}
    339513
     514/* -=-=-=-=- IMediaAsyncPort -=-=-=-=- */
     515
     516/** Makes a PDRVBLOCKASYNC out of a PPDMIMEDIAASYNCPORT. */
     517#define PDMIMEDIAASYNCPORT_2_DRVDISKINTEGRITY(pInterface)    ( (PDRVDISKINTEGRITY((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaAsyncPort))) )
     518
     519static DECLCALLBACK(int) drvdiskintAsyncTransferCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, void *pvUser)
     520{
     521    PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNCPORT_2_DRVDISKINTEGRITY(pInterface);
     522    PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)pvUser;
     523    int rc = VINF_SUCCESS;
     524
     525    if (pIoReq->fRead)
     526        rc = drvdiskintReadVerify(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer);
     527    else
     528        rc = drvdiskintWriteRecord(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer);
     529
     530    AssertRC(rc);
     531
     532    rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pIoReq->pvUser);
     533    RTMemFree(pIoReq);
     534
     535    return rc;
     536}
     537
    340538/* -=-=-=-=- IBase -=-=-=-=- */
    341539
     
    350548    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
    351549    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
     550    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNCPORT, pThis->pDrvMediaAsync ? &pThis->IMediaAsyncPort : NULL);
    352551    return NULL;
    353552}
     
    416615    pThis->IMedia.pfnGetUuid             = drvdiskintGetUuid;
    417616
     617    /* IMediaAsync */
     618    pThis->IMediaAsync.pfnStartRead      = drvdiskintStartRead;
     619    pThis->IMediaAsync.pfnStartWrite     = drvdiskintStartWrite;
     620
     621    /* IMediaAsyncPort. */
     622    pThis->IMediaAsyncPort.pfnTransferCompleteNotify  = drvdiskintAsyncTransferCompleteNotify;
     623
    418624    /*
    419625     * Try attach driver below and query it's media interface.
     
    430636                                N_("No media or async media interface below"));
    431637
    432     /** Create the AVL tree. */
     638    pThis->pDrvMediaAsync = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIAASYNC);
     639
     640    /* Try to attach async media port interface above.*/
     641    pThis->pDrvMediaAsyncPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAASYNCPORT);
     642
     643    /* Create the AVL tree. */
    433644    pThis->pTreeSegments = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
    434645    if (!pThis->pTreeSegments)
Note: See TracChangeset for help on using the changeset viewer.

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