VirtualBox

Changeset 38622 in vbox for trunk/src/VBox/Devices


Ignore:
Timestamp:
Sep 4, 2011 5:05:03 PM (14 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
73829
Message:

AHCI+DrvBlock+DrvVD: Add support for the TRIM command and connect with the VD discard support.

Location:
trunk/src/VBox/Devices/Storage
Files:
5 edited

Legend:

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

    r28800 r38622  
    3333    "",                                    /* 0x04 */
    3434    "",                                    /* 0x05 */
    35     "",                                    /* 0x06 */
     35    "DATA SET MANAGEMENT",                 /* 0x06 */
    3636    "",                                    /* 0x07 */
    3737    "DEVICE RESET",                        /* 0x08 */
  • trunk/src/VBox/Devices/Storage/DevAHCI.cpp

    r38328 r38622  
    252252    AHCITXDIR_WRITE,
    253253    /** Flush */
    254     AHCITXDIR_FLUSH
     254    AHCITXDIR_FLUSH,
     255    /** Trim */
     256    AHCITXDIR_TRIM
    255257} AHCITXDIR;
    256258
     
    326328     * and the callback copies the data to the destination. */
    327329    PFNAHCIPOSTPROCESS         pfnPostProcess;
     330    /** Pointer to the array of PDM ranges. */
     331    PPDMRANGE                  paRanges;
     332    /** Number of entries in the array. */
     333    unsigned                   cRanges;
    328334} AHCIPORTTASKSTATE;
    329335
     
    875881#define AHCI_RECFIS_SDBFIS_OFFSET 0x58 /* Set Device Bits FIS */
    876882#define AHCI_RECFIS_UFIS_OFFSET   0x60 /* Unknown FIS type */
     883
     884/** Mask to get the LBA value from a LBA range. */
     885#define AHCI_RANGE_LBA_MASK    UINT64_C(0xffffffffffff)
     886/** Mas to get the length value from a LBA range. */
     887#define AHCI_RANGE_LENGTH_MASK UINT64_C(0xffff000000000000)
     888/** Returns the length of the range in sectors. */
     889#define AHCI_RANGE_LENGTH_GET(val) (((val) & AHCI_RANGE_LENGTH_MASK) >> 48)
    877890
    878891/**
     
    30633076    p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */
    30643077    p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */
    3065     p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */
    3066     p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */
     3078    if (pAhciPort->pDrvBlock->pfnDiscard)
     3079    {
     3080        p[80] = RT_H2LE_U16(0x1f0); /* support everything up to ATA/ATAPI-8 ACS */
     3081        p[81] = RT_H2LE_U16(0x28); /* conforms to ATA/ATAPI-8 ACS */
     3082    }
     3083    else
     3084    {
     3085        p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */
     3086        p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */
     3087    }
    30673088    p[82] = RT_H2LE_U16(1 << 3 | 1 << 5 | 1 << 6); /* supports power management,  write cache and look-ahead */
    30683089    p[83] = RT_H2LE_U16(1 << 14 | 1 << 10 | 1 << 12 | 1 << 13); /* supports LBA48, FLUSH CACHE and FLUSH CACHE EXT */
     
    30793100    if (pAhciPort->fNonRotational)
    30803101        p[217] = RT_H2LE_U16(1); /* Non-rotational medium */
     3102
     3103    if (pAhciPort->pDrvBlock->pfnDiscard) /** @todo: Set bit 14 in word 69 too? (Deterministic read after TRIM). */
     3104        p[169] = RT_H2LE_U16(1); /* DATA SET MANAGEMENT command supported. */
    30813105
    30823106    /* The following are SATA specific */
     
    59165940}
    59175941
    5918 
    59195942/**
    59205943 * Copy the content of a buffer to a scatter gather list.
     
    62046227        PDMDevHlpAsyncNotificationCompleted(pAhciPort->pDevInsR3);
    62056228    return rc;
     6229}
     6230
     6231/**
     6232 * Creates the array of ranges to trim.
     6233 *
     6234 * @returns VBox status code.
     6235 * @param   pAhciPort             AHCI port state.
     6236 * @param   pAhciportTaskState    The task state handling the TRIM request.
     6237 */
     6238static int ahciTrimRangesCreate(PAHCIPort pAhciPort, PAHCIPORTTASKSTATE pAhciPortTaskState)
     6239{
     6240    RTSGBUF SgBuf;
     6241    uint64_t aRanges[64];
     6242    unsigned cRangesMax;
     6243    unsigned cRanges = 0;
     6244    int rc = VINF_SUCCESS;
     6245
     6246    /* First check that the trim bit is set and all other bits are 0. */
     6247    if (   !(pAhciPortTaskState->cmdFis[AHCI_CMDFIS_FET] & UINT16_C(0x01))
     6248        || (pAhciPortTaskState->cmdFis[AHCI_CMDFIS_FET] & ~UINT16_C(0x1)))
     6249        return VERR_INVALID_PARAMETER;
     6250
     6251    /* The data buffer contains LBA range entries. Each range is 8 bytes big. */
     6252    cRangesMax = pAhciPortTaskState->cbSGBuffers / 8;
     6253
     6254    RTSgBufInit(&SgBuf, pAhciPortTaskState->pSGListHead, pAhciPortTaskState->cSGListUsed);
     6255
     6256    do
     6257    {
     6258        size_t cbCopied = RTSgBufCopyToBuf(&SgBuf, &aRanges[0], sizeof(aRanges));
     6259        Assert(cbCopied == sizeof(aRanges));
     6260
     6261        /*
     6262         * Count the number of valid ranges in the buffer.
     6263         * A length of 0 is invalid and is only used for padding
     6264         */
     6265        for (unsigned i = 0; i < RT_ELEMENTS(aRanges); i++)
     6266        {
     6267            aRanges[i] = RT_H2LE_U64(aRanges[i]);
     6268            if (AHCI_RANGE_LENGTH_GET(aRanges[i]) != 0)
     6269                cRanges++;
     6270            else
     6271                break;
     6272        }
     6273
     6274        cRangesMax -= 64;
     6275    } while (cRangesMax);
     6276
     6277    AssertReturn(cRanges != 0, VERR_INVALID_PARAMETER);
     6278
     6279    pAhciPortTaskState->paRanges = (PPDMRANGE)RTMemAllocZ(sizeof(PDMRANGE) * cRanges);
     6280    if (pAhciPortTaskState->paRanges)
     6281    {
     6282        uint32_t idxRange = 0;
     6283
     6284        pAhciPortTaskState->cRanges = cRanges;
     6285        RTSgBufReset(&SgBuf);
     6286
     6287        /* Convert the ranges from the guest to our format. */
     6288        do
     6289        {
     6290            size_t cbCopied = RTSgBufCopyToBuf(&SgBuf, &aRanges[0], sizeof(aRanges));
     6291            Assert(cbCopied == sizeof(aRanges));
     6292
     6293            for (unsigned i = 0; i < RT_ELEMENTS(aRanges); i++)
     6294            {
     6295                aRanges[i] = RT_H2LE_U64(aRanges[i]);
     6296                if (AHCI_RANGE_LENGTH_GET(aRanges[i]) != 0)
     6297                {
     6298                    pAhciPortTaskState->paRanges[idxRange].offStart = (aRanges[i] & AHCI_RANGE_LBA_MASK) * 512;
     6299                    pAhciPortTaskState->paRanges[idxRange].cbRange = AHCI_RANGE_LENGTH_GET(aRanges[i]) * 512;
     6300                    idxRange++;
     6301                }
     6302                else
     6303                    break;
     6304            }
     6305        } while (idxRange < cRanges);
     6306    }
     6307
     6308    return rc;
     6309}
     6310
     6311static void ahciTrimRangesDestroy(PAHCIPORTTASKSTATE pAhciPortTaskState)
     6312{
     6313    RTMemFree(pAhciPortTaskState->paRanges);
    62066314}
    62076315
     
    64806588            break;
    64816589        }
     6590        case ATA_DATA_SET_MANAGEMENT:
     6591        {
     6592            if (pAhciPort->pDrvBlock->pfnDiscard)
     6593            {
     6594                rc = AHCITXDIR_TRIM;
     6595                break;
     6596            }
     6597            /* else: fall through and report error to the guest. */
     6598        }
    64826599        /* All not implemented commands go below. */
    64836600        case ATA_SECURITY_FREEZE_LOCK:
     
    68596976                    {
    68606977                        LogRel(("AHCI#%u: Flush returned rc=%Rrc\n",
     6978                                pAhciPort->iLUN, rc));
     6979                    }
     6980
     6981                    if (RT_FAILURE(rc))
     6982                    {
     6983                        if (!ahciIsRedoSetWarning(pAhciPort, rc))
     6984                        {
     6985                            pAhciPortTaskState->uATARegError = ID_ERR;
     6986                            pAhciPortTaskState->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR;
     6987                        }
     6988                        else
     6989                        {
     6990                            /* Add the task to the mask again. */
     6991                            ASMAtomicOrU32(&pAhciPort->u32TasksNew, (1 << pAhciPortTaskState->uTag));
     6992                        }
     6993                    }
     6994                    else
     6995                    {
     6996                        pAhciPortTaskState->uATARegError = 0;
     6997                        pAhciPortTaskState->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK;
     6998                    }
     6999
     7000                    if (!pAhciPort->fRedo)
     7001                    {
     7002                        if (pAhciPortTaskState->fQueued)
     7003                            ASMAtomicOrU32(&pAhciPort->u32QueuedTasksFinished, (1 << pAhciPortTaskState->uTag));
     7004                        else
     7005                        {
     7006                            /* Task is not queued send D2H FIS */
     7007                            ahciSendD2HFis(pAhciPort, pAhciPortTaskState, &pAhciPortTaskState->cmdFis[0], true);
     7008                        }
     7009                    }
     7010                }
     7011                else if (enmTxDir == AHCITXDIR_TRIM)
     7012                {
     7013                    rc = ahciScatterGatherListCreate(pAhciPort, pAhciPortTaskState, (enmTxDir == AHCITXDIR_READ) ? false : true);
     7014                    if (RT_FAILURE(rc))
     7015                        AssertMsgFailed(("%s: Failed to get number of list elments %Rrc\n", __FUNCTION__, rc));
     7016
     7017                    rc = ahciTrimRangesCreate(pAhciPort, pAhciPortTaskState);
     7018                    if (RT_SUCCESS(rc))
     7019                    {
     7020                        pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
     7021                        rc = pAhciPort->pDrvBlock->pfnDiscard(pAhciPort->pDrvBlock, pAhciPortTaskState->paRanges, pAhciPortTaskState->cRanges);
     7022                        pAhciPort->Led.Actual.s.fWriting = 0;
     7023                    }
     7024
     7025                    /* Cleanup. */
     7026                    ahciTrimRangesDestroy(pAhciPortTaskState);
     7027
     7028                    int rc2 = ahciScatterGatherListDestroy(pAhciPort, pAhciPortTaskState);
     7029                    if (RT_FAILURE(rc2))
     7030                        AssertMsgFailed(("Destroying task list failed rc=%Rrc\n", rc));
     7031
     7032                    /* Log the error. */
     7033                    if (   RT_FAILURE(rc)
     7034                        && pAhciPort->cErrors++ < MAX_LOG_REL_ERRORS)
     7035                    {
     7036                        LogRel(("AHCI#%u: Trim returned rc=%Rrc\n",
    68617037                                pAhciPort->iLUN, rc));
    68627038                    }
  • trunk/src/VBox/Devices/Storage/DrvBlock.cpp

    r35560 r38622  
    286286}
    287287
     288static DECLCALLBACK(int) drvblockDiscard(PPDMIBLOCK pInterface, PPDMRANGE paRanges, unsigned cRanges)
     289{
     290    PDRVBLOCK pThis = PDMIBLOCK_2_DRVBLOCK(pInterface);
     291
     292    return pThis->pDrvMedia->pfnDiscard(pThis->pDrvMedia, paRanges, cRanges);
     293}
     294
    288295/* -=-=-=-=- IBlockAsync -=-=-=-=- */
    289296
     
    968975        return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
    969976                                N_("No media or async media interface below"));
     977
     978    if (pThis->pDrvMedia->pfnDiscard)
     979        pThis->IBlock.pfnDiscard = drvblockDiscard;
    970980
    971981    /* Try to get the optional async interface. */
  • trunk/src/VBox/Devices/Storage/DrvVD.cpp

    r38469 r38622  
    16741674}
    16751675
     1676static DECLCALLBACK(int) drvvdDiscard(PPDMIMEDIA pInterface, PPDMRANGE paRanges, unsigned cRanges)
     1677{
     1678    LogFlowFunc(("\n"));
     1679    PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
     1680
     1681    /** @todo: Fix the cast properly without allocating temporary memory (maybe move the type to IPRT). */
     1682    int rc = VDDiscardRanges(pThis->pDisk, (PVDRANGE)paRanges, cRanges);
     1683    LogFlowFunc(("returns %Rrc\n", rc));
     1684    return rc;
     1685}
     1686
    16761687/*******************************************************************************
    16771688*   Async Media interface methods                                              *
     
    20582069    pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
    20592070    pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
    2060     pThis->IMedia.pfnGetUuid            = drvvdGetUuid;
     2071    pThis->IMedia.pfnGetUuid             = drvvdGetUuid;
     2072    pThis->IMedia.pfnDiscard             = drvvdDiscard;
    20612073
    20622074    /* IMediaAsync */
     
    20922104    bool        fUseNewIo = false;
    20932105    bool        fUseBlockCache = false;
     2106    bool        fDiscard = false;
    20942107    unsigned    iLevel = 0;
    20952108    PCFGMNODE   pCurNode = pCfg;
     
    21092122                                          "HostIPStack\0UseNewIo\0BootAcceleration\0BootAccelerationBuffer\0"
    21102123                                          "SetupMerge\0MergeSource\0MergeTarget\0BwGroup\0Type\0BlockCache\0"
    2111                                           "CachePath\0CacheFormat\0");
     2124                                          "CachePath\0CacheFormat\0Discard\0");
    21122125        }
    21132126        else
     
    22312244            else
    22322245                rc = VINF_SUCCESS;
     2246            rc = CFGMR3QueryBoolDef(pCurNode, "Discard", &fDiscard, false);
     2247            if (RT_FAILURE(rc))
     2248            {
     2249                rc = PDMDRV_SET_ERROR(pDrvIns, rc,
     2250                                      N_("DrvVD: Configuration error: Querying \"Discard\" as boolean failed"));
     2251                break;
     2252            }
     2253            if (fReadOnly && fDiscard)
     2254            {
     2255                rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
     2256                                      N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"Discard\" are set"));
     2257                break;
     2258            }
    22332259
    22342260            char *psz;
     
    25292555        if (pThis->fShareable)
    25302556            uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
     2557        if (fDiscard && iLevel == 0)
     2558            uOpenFlags |= VD_OPEN_FLAGS_DISCARD;
    25312559
    25322560        /* Try to open backend in async I/O mode first. */
     
    25382566            rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
    25392567        }
     2568
     2569        if (rc == VERR_VD_DISCARD_NOT_SUPPORTED)
     2570        {
     2571            fDiscard = false;
     2572            uOpenFlags &= ~VD_OPEN_FLAGS_DISCARD;
     2573            rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
     2574        }
     2575
     2576        if (!fDiscard)
     2577            pThis->IMedia.pfnDiscard = NULL;
    25402578
    25412579        if (RT_SUCCESS(rc))
  • trunk/src/VBox/Devices/Storage/ide.h

    r37264 r38622  
    5454    ATA_NOP                                 = 0x00,
    5555    ATA_CFA_REQUEST_EXTENDED_ERROR_CODE     = 0x03,
     56    ATA_DATA_SET_MANAGEMENT                 = 0x06,
    5657    ATA_DEVICE_RESET                        = 0x08,
    5758    ATA_RECALIBRATE                         = 0x10,
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