VirtualBox

Changeset 55847 in vbox


Ignore:
Timestamp:
May 13, 2015 1:46:42 PM (10 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
100298
Message:

AHCI: Cleanup, fix a few concurrency and hopefully a double free corruption issue too

File:
1 edited

Legend:

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

    r55775 r55847  
    4747#include <iprt/asm.h>
    4848#include <iprt/string.h>
     49#include <iprt/list.h>
    4950#ifdef IN_RING3
    5051# include <iprt/param.h>
     
    279280
    280281/** Task encountered a buffer overflow. */
    281 #define AHCI_REQ_OVERFLOW   RT_BIT_32(0)
     282#define AHCI_REQ_OVERFLOW    RT_BIT_32(0)
    282283/** Request is a PIO data command, if this flag is not set it either is
    283284 * a command which does not transfer data or a DMA command based on the transfer size. */
    284 #define AHCI_REQ_PIO_DATA   RT_BIT_32(1)
     285#define AHCI_REQ_PIO_DATA    RT_BIT_32(1)
    285286/** The request has the SACT register set. */
    286 #define AHCI_REQ_CLEAR_SACT RT_BIT_32(2)
    287 /** FLag whether the request is queued. */
    288 #define AHCI_REQ_IS_QUEUED  RT_BIT_32(3)
     287#define AHCI_REQ_CLEAR_SACT  RT_BIT_32(2)
     288/** Flag whether the request is queued. */
     289#define AHCI_REQ_IS_QUEUED   RT_BIT_32(3)
     290/** Flag whether the request is stored on the stack. */
     291#define AHCI_REQ_IS_ON_STACK RT_BIT_32(4)
    289292
    290293/**
     
    293296typedef struct AHCIREQ
    294297{
     298    /** List node for the free list if the request is not in use. */
     299    RTLISTNODE                 NodeList;
    295300    /** Task state. */
    296301    volatile AHCITXSTATE       enmTxState;
     
    523528    /** Async IO Thread. */
    524529    R3PTRTYPE(PPDMTHREAD)           pAsyncIOThread;
    525     /**
    526      * Array of cached tasks. The tag number is the index value.
    527      * Only used with the async interface.
    528      */
    529     R3PTRTYPE(PAHCIREQ)             aCachedTasks[AHCI_NR_COMMAND_SLOTS];
     530    /** Array of active tasks indexed by the tag. */
     531    R3PTRTYPE(volatile PAHCIREQ)    aActiveTasks[AHCI_NR_COMMAND_SLOTS];
    530532    /** First task throwing an error. */
    531533    R3PTRTYPE(volatile PAHCIREQ)    pTaskErr;
     
    567569
    568570    uint32_t                        u32Alignment5;
     571
     572    /** Critical section protecting the global free list. */
     573    RTCRITSECT                      CritSectReqsFree;
     574    /** Head of the global free request list. */
     575    R3PTRTYPE(PRTLISTANCHOR)        pListReqsFree;
     576
    569577} AHCIPort;
    570578/** Pointer to the state of an AHCI port. */
     
    944952static bool ahciCancelActiveTasks(PAHCIPort pAhciPort, PAHCIREQ pAhciReqExcept);
    945953static void ahciReqMemFree(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, bool fForceFree);
     954static void ahciR3PortCachedReqsFree(PAHCIPort pAhciPort);
    946955#endif
    947956RT_C_DECLS_END
     
    46434652                     * is about to be destroyed.
    46444653                     */
    4645                     for (uint32_t i = 0; i < AHCI_NR_COMMAND_SLOTS; i++)
    4646                     {
    4647                         if (pAhciPort->aCachedTasks[i])
    4648                             ahciReqMemFree(pAhciPort, pAhciPort->aCachedTasks[i], true /* fForceFree */);
    4649                     }
     4654                    ahciR3PortCachedReqsFree(pAhciPort);
    46504655
    46514656                    rc = VMR3ReqPriorityCallWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
     
    56775682static void ahciR3PortCachedReqsFree(PAHCIPort pAhciPort)
    56785683{
    5679     for (uint32_t i = 0; i < AHCI_NR_COMMAND_SLOTS; i++)
    5680     {
    5681         if (pAhciPort->aCachedTasks[i])
    5682         {
    5683             ahciReqMemFree(pAhciPort, pAhciPort->aCachedTasks[i], true /* fForceFree */);
    5684             RTMemFree(pAhciPort->aCachedTasks[i]);
    5685             pAhciPort->aCachedTasks[i] = NULL;
    5686         }
     5684    if (pAhciPort->pListReqsFree)
     5685    {
     5686        PAHCIREQ pReq = NULL;
     5687        PAHCIREQ pReqNext = NULL;
     5688
     5689        RTCritSectEnter(&pAhciPort->CritSectReqsFree);
     5690        RTListForEachSafe(pAhciPort->pListReqsFree, pReq, pReqNext, AHCIREQ, NodeList)
     5691        {
     5692            RTListNodeRemove(&pReq->NodeList);
     5693            ahciReqMemFree(pAhciPort, pReq, true /* fForceFree */);
     5694            RTMemFree(pReq);
     5695        }
     5696        RTCritSectLeave(&pAhciPort->CritSectReqsFree);
    56875697    }
    56885698}
     
    56985708static bool ahciCancelActiveTasks(PAHCIPort pAhciPort, PAHCIREQ pAhciReqExcept)
    56995709{
    5700     for (unsigned i = 0; i < RT_ELEMENTS(pAhciPort->aCachedTasks); i++)
    5701     {
    5702         PAHCIREQ pAhciReq = pAhciPort->aCachedTasks[i];
     5710    for (unsigned i = 0; i < RT_ELEMENTS(pAhciPort->aActiveTasks); i++)
     5711    {
     5712        PAHCIREQ pAhciReq = (PAHCIREQ)ASMAtomicXchgPtr((void * volatile *)&pAhciPort->aActiveTasks[i], NULL);
    57035713
    57045714        if (   VALID_PTR(pAhciReq)
     
    57065716        {
    57075717            bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pAhciReq->enmTxState, AHCITXSTATE_CANCELED, AHCITXSTATE_ACTIVE);
    5708 
    57095718            if (fXchg)
    57105719            {
     
    57185727                 * a new task structure for this tag.
    57195728                 */
    5720                 ASMAtomicWriteNullPtr(&pAhciPort->aCachedTasks[i]);
     5729                ASMAtomicWriteNullPtr(&pAhciPort->aActiveTasks[i]);
    57215730                LogRel(("AHCI#%uP%u: Cancelled task %u\n", pAhciPort->CTX_SUFF(pDevIns)->iInstance,
    57225731                        pAhciPort->iLUN, pAhciReq->uTag));
     
    59325941
    59335942/**
     5943 * Allocates a new AHCI request.
     5944 *
     5945 * @returns A new AHCI request structure or NULL if out of memory.
     5946 * @param   pAhciPort    The AHCI port.
     5947 */
     5948static PAHCIREQ ahciR3ReqAlloc(PAHCIPort pAhciPort)
     5949{
     5950    PAHCIREQ pAhciReq = NULL;
     5951
     5952    /* Check the global free list first. */
     5953    RTCritSectEnter(&pAhciPort->CritSectReqsFree);
     5954    pAhciReq = RTListGetFirst(pAhciPort->pListReqsFree, AHCIREQ, NodeList);
     5955    if (pAhciReq)
     5956        RTListNodeRemove(&pAhciReq->NodeList);
     5957    RTCritSectLeave(&pAhciPort->CritSectReqsFree);
     5958
     5959    if (!pAhciReq)
     5960        pAhciReq = (PAHCIREQ)RTMemAllocZ(sizeof(AHCIREQ));
     5961
     5962    pAhciReq->enmTxState = AHCITXSTATE_ACTIVE;
     5963    return pAhciReq;
     5964}
     5965
     5966/**
     5967 * Frees a given AHCI request structure.
     5968 *
     5969 * @returns nothing.
     5970 * @param   pAhciPort    The AHCI port.
     5971 */
     5972static void ahciR3ReqFree(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
     5973{
     5974    pAhciReq->enmTxState = AHCITXSTATE_FREE;
     5975
     5976    RTCritSectEnter(&pAhciPort->CritSectReqsFree);
     5977    RTListAppend(pAhciPort->pListReqsFree, &pAhciReq->NodeList);
     5978    RTCritSectLeave(&pAhciPort->CritSectReqsFree);
     5979}
     5980
     5981/**
    59345982 * Complete a data transfer task by freeing all occupied resources
    59355983 * and notifying the guest.
     
    59405988 * @param pAhciReq     Pointer to the task which finished.
    59415989 * @param rcReq        IPRT status code of the completed request.
    5942  * @param fFreeReq     Flag whether to free the request if it was canceled.
    5943  */
    5944 static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcReq, bool fFreeReq)
    5945 {
    5946     bool fXchg = false;
     5990 */
     5991static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcReq)
     5992{
    59475993    bool fRedo = false;
    59485994    bool fCanceled = false;
     
    59505996    AHCITXSTATE enmTxState = AHCITXSTATE_INVALID;
    59515997
    5952     LogFlowFunc(("pAhciPort=%p pAhciReq=%p rcReq=%d fFreeReq=%RTbool\n",
    5953                  pAhciPort, pAhciReq, rcReq, fFreeReq));
     5998    LogFlowFunc(("pAhciPort=%p pAhciReq=%p rcReq=%d\n",
     5999                 pAhciPort, pAhciReq, rcReq));
    59546000
    59556001    enmTxState = (AHCITXSTATE)ASMAtomicReadU32((volatile uint32_t *)&pAhciReq->enmTxState);
    59566002    VBOXDD_AHCI_REQ_COMPLETED(pAhciReq, rcReq, enmTxState, pAhciReq->uOffset, pAhciReq->cbTransfer);
    59576003    VBOXDD_AHCI_REQ_COMPLETED_TIMESTAMP(pAhciReq, tsNow);
     6004
     6005    /*
     6006     * Clear the request structure from the active request list first so it doesn't get cancelled
     6007     * while we complete it. If the request is not in the active list anymore it was already canceled
     6008     * and we have to make sure to not copy anything to guest memory because the guest might use it
     6009     * for other things already.
     6010     */
     6011    bool fPortReset = ASMAtomicReadBool(&pAhciPort->fPortReset);
     6012    bool fXchg = ASMAtomicCmpXchgPtr(&pAhciPort->aActiveTasks[pAhciReq->uTag], NULL, pAhciReq);
     6013
     6014    if (fXchg)
     6015    {
     6016        AssertReleaseMsg(ASMAtomicReadU32(&pAhciPort->cTasksActive) > 0,
     6017                         ("Inconsistent request counter\n"));
     6018        ASMAtomicDecU32(&pAhciPort->cTasksActive);
     6019    }
    59586020
    59596021    /*
     
    59866048                pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, pcszReq, (tsNow - pAhciReq->tsStart) / 1000));
    59876049    }
    5988 
    5989     bool fPortReset = ASMAtomicReadBool(&pAhciPort->fPortReset);
    5990     fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE);
    59916050
    59926051    if (fXchg && !fPortReset)
     
    60466105            pAhciReq->cmdHdr.u32PRDBC = pAhciReq->cbTransfer;
    60476106
    6048             /* Status will be set by already for non I/O requests. */
     6107            /* Status will be set already for non I/O requests. */
    60496108            if (pAhciReq->enmTxDir != AHCITXDIR_NONE)
    60506109            {
     
    60696128        }
    60706129
    6071         AssertReleaseMsg(ASMAtomicReadU32(&pAhciPort->cTasksActive) > 0 ,
    6072                          ("Inconsistent request counter\n"));
    6073         ASMAtomicDecU32(&pAhciPort->cTasksActive);
    6074 
    60756130        if (!fRedo)
    60766131        {
     
    61086163                  ("Task is not active but wasn't canceled and no port reset is active!\n"));
    61096164
    6110         /*
    6111          * If this handler switched the request state from active to free the request counter
    6112          * must be decremented.
    6113          */
    6114         if (fXchg)
    6115         {
    6116             Assert(fPortReset);
    6117             AssertReleaseMsg(ASMAtomicReadU32(&pAhciPort->cTasksActive) > 0 ,
    6118                              ("Inconsistent request counter\n"));
    6119             ASMAtomicDecU32(&pAhciPort->cTasksActive);
    6120         }
    6121 
    61226165        fCanceled = true;
    6123         ASMAtomicXchgU32((volatile uint32_t *)&pAhciReq->enmTxState, AHCITXSTATE_FREE);
    61246166
    61256167        if (pAhciReq->enmTxDir == AHCITXDIR_TRIM)
     
    61466188                        pAhciReq->cbTransfer, rcReq));
    61476189         }
    6148 
    6149         /* Finally free the task state structure because it is completely unused now. */
    6150         if (fFreeReq)
    6151             RTMemFree(pAhciReq);
    61526190    }
    61536191
     
    61556193        PDMDevHlpAsyncNotificationCompleted(pAhciPort->pDevInsR3);
    61566194
     6195    if (pAhciReq && !(pAhciReq->fFlags & AHCI_REQ_IS_ON_STACK))
     6196        ahciR3ReqFree(pAhciPort, pAhciReq);
    61576197    return fCanceled;
    61586198}
     
    61746214             __FUNCTION__, pInterface, pvUser, pAhciReq->uTag));
    61756215
    6176     ahciTransferComplete(pAhciPort, pAhciReq, rcReq, true);
     6216    ahciTransferComplete(pAhciPort, pAhciReq, rcReq);
    61776217
    61786218    return VINF_SUCCESS;
     
    65776617
    65786618/**
     6619 * Submits a given request for execution.
     6620 *
     6621 * @returns Flag whether the request was canceled inbetween.
     6622 * @param   pAhciPort    The port the request is for.
     6623 * @param   pAhciReq     The request to submit.
     6624 * @param   enmTxDir     The request type.
     6625 */
     6626static bool ahciR3ReqSubmit(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, AHCITXDIR enmTxDir)
     6627{
     6628    int rc = VINF_SUCCESS;
     6629    bool fReqCanceled = false;
     6630
     6631    if (pAhciPort->fAsyncInterface)
     6632    {
     6633        VBOXDD_AHCI_REQ_SUBMIT(pAhciReq, pAhciReq->enmTxDir, pAhciReq->uOffset, pAhciReq->cbTransfer);
     6634        VBOXDD_AHCI_REQ_SUBMIT_TIMESTAMP(pAhciReq, pAhciReq->tsStart);
     6635        if (enmTxDir == AHCITXDIR_FLUSH)
     6636        {
     6637            rc = pAhciPort->pDrvBlockAsync->pfnStartFlush(pAhciPort->pDrvBlockAsync,
     6638                                                          pAhciReq);
     6639        }
     6640        else if (enmTxDir == AHCITXDIR_TRIM)
     6641        {
     6642            rc = ahciTrimRangesCreate(pAhciPort, pAhciReq);
     6643            if (RT_SUCCESS(rc))
     6644            {
     6645                pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
     6646                rc = pAhciPort->pDrvBlockAsync->pfnStartDiscard(pAhciPort->pDrvBlockAsync, pAhciReq->u.Trim.paRanges,
     6647                                                                pAhciReq->u.Trim.cRanges, pAhciReq);
     6648            }
     6649        }
     6650        else if (enmTxDir == AHCITXDIR_READ)
     6651        {
     6652            pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1;
     6653            rc = pAhciPort->pDrvBlockAsync->pfnStartRead(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset,
     6654                                                         &pAhciReq->u.Io.DataSeg, 1,
     6655                                                         pAhciReq->cbTransfer,
     6656                                                         pAhciReq);
     6657        }
     6658        else
     6659        {
     6660            pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
     6661            rc = pAhciPort->pDrvBlockAsync->pfnStartWrite(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset,
     6662                                                          &pAhciReq->u.Io.DataSeg, 1,
     6663                                                          pAhciReq->cbTransfer,
     6664                                                          pAhciReq);
     6665        }
     6666        if (rc == VINF_VD_ASYNC_IO_FINISHED)
     6667            fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS);
     6668        else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
     6669            fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc);
     6670    }
     6671    else
     6672    {
     6673        if (enmTxDir == AHCITXDIR_FLUSH)
     6674            rc = pAhciPort->pDrvBlock->pfnFlush(pAhciPort->pDrvBlock);
     6675        else if (enmTxDir == AHCITXDIR_TRIM)
     6676        {
     6677            rc = ahciTrimRangesCreate(pAhciPort, pAhciReq);
     6678            if (RT_SUCCESS(rc))
     6679            {
     6680                pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
     6681                rc = pAhciPort->pDrvBlock->pfnDiscard(pAhciPort->pDrvBlock, pAhciReq->u.Trim.paRanges,
     6682                                                      pAhciReq->u.Trim.cRanges);
     6683                pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 0;
     6684            }
     6685        }
     6686        else if (enmTxDir == AHCITXDIR_READ)
     6687        {
     6688            pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1;
     6689            rc = pAhciPort->pDrvBlock->pfnRead(pAhciPort->pDrvBlock, pAhciReq->uOffset,
     6690                                               pAhciReq->u.Io.DataSeg.pvSeg,
     6691                                               pAhciReq->cbTransfer);
     6692            pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 0;
     6693        }
     6694        else
     6695        {
     6696            pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
     6697            rc = pAhciPort->pDrvBlock->pfnWrite(pAhciPort->pDrvBlock, pAhciReq->uOffset,
     6698                                                pAhciReq->u.Io.DataSeg.pvSeg,
     6699                                                pAhciReq->cbTransfer);
     6700            pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 0;
     6701        }
     6702        fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc);
     6703    }
     6704
     6705    return fReqCanceled;
     6706}
     6707
     6708/**
     6709 * Prepares the command for execution coping it from guest memory and doing a few
     6710 * validation checks on it.
     6711 *
     6712 * @returns Whether the command was successfully fetched from guest memory and
     6713 *          can be continued.
     6714 * @param   pAhciPort    The AHCI port the request is for.
     6715 * @param   pAhciReq     Request structure to copy the command to.
     6716 */
     6717static bool ahciR3CmdPrepare(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
     6718{
     6719    pAhciReq->tsStart       = RTTimeMilliTS();
     6720    pAhciReq->uATARegStatus = 0;
     6721    pAhciReq->uATARegError  = 0;
     6722
     6723    /* Set current command slot */
     6724    ASMAtomicWriteU32(&pAhciPort->u32CurrentCommandSlot, pAhciReq->uTag);
     6725    ASMAtomicWritePtr(&pAhciPort->aActiveTasks[pAhciReq->uTag], pAhciReq);
     6726
     6727    bool fContinue = ahciPortTaskGetCommandFis(pAhciPort, pAhciReq);
     6728    if (fContinue)
     6729    {
     6730        /* Mark the task as processed by the HBA if this is a queued task so that it doesn't occur in the CI register anymore. */
     6731        if (pAhciPort->regSACT & RT_BIT_32(pAhciReq->uTag))
     6732        {
     6733            pAhciReq->fFlags |= AHCI_REQ_CLEAR_SACT;
     6734            ASMAtomicOrU32(&pAhciPort->u32TasksFinished, RT_BIT_32(pAhciReq->uTag));
     6735        }
     6736
     6737        if (pAhciReq->cmdFis[AHCI_CMDFIS_BITS] & AHCI_CMDFIS_C)
     6738        {
     6739            AssertReleaseMsg(ASMAtomicReadU32(&pAhciPort->cTasksActive) < AHCI_NR_COMMAND_SLOTS,
     6740                             ("There are more than 32 requests active"));
     6741            ASMAtomicIncU32(&pAhciPort->cTasksActive);
     6742        }
     6743        else
     6744        {
     6745            /* If the reset bit is set put the device into reset state. */
     6746            if (pAhciReq->cmdFis[AHCI_CMDFIS_CTL] & AHCI_CMDFIS_CTL_SRST)
     6747            {
     6748                ahciLog(("%s: Setting device into reset state\n", __FUNCTION__));
     6749                pAhciPort->fResetDevice = true;
     6750                ahciSendD2HFis(pAhciPort, pAhciReq, pAhciReq->cmdFis, true);
     6751            }
     6752            else if (pAhciPort->fResetDevice) /* The bit is not set and we are in a reset state. */
     6753                ahciFinishStorageDeviceReset(pAhciPort, pAhciReq);
     6754            else /* We are not in a reset state update the control registers. */
     6755                AssertMsgFailed(("%s: Update the control register\n", __FUNCTION__));
     6756
     6757            fContinue = false;
     6758        }
     6759    }
     6760    else
     6761    {
     6762        /*
     6763         * Couldn't find anything in either the AHCI or SATA spec which
     6764         * indicates what should be done if the FIS is not read successfully.
     6765         * The closest thing is in the state machine, stating that the device
     6766         * should go into idle state again (SATA spec 1.0 chapter 8.7.1).
     6767         * Do the same here and ignore any corrupt FIS types, after all
     6768         * the guest messed up everything and this behavior is undefined.
     6769         */
     6770        fContinue = false;
     6771    }
     6772
     6773    return fContinue;
     6774}
     6775
     6776/**
    65796777 * Transmit queue consumer
    65806778 * Queue a new async task.
     
    66736871            ahciLog(("%s: Processing command at slot %d\n", __FUNCTION__, idx));
    66746872
    6675             /*
    6676              * Check if there is already an allocated task struct in the cache.
    6677              * Allocate a new task otherwise.
    6678              */
    6679             if (!pAhciPort->aCachedTasks[idx])
     6873            /* Check whether the request is already active and ignore. */
     6874            if (ASMAtomicReadPtr((void * volatile *)&pAhciPort->aActiveTasks[idx]))
    66806875            {
    6681                 pAhciReq = (PAHCIREQ)RTMemAllocZ(sizeof(AHCIREQ));
    6682                 AssertMsg(pAhciReq, ("%s: Cannot allocate task state memory!\n"));
    6683                 pAhciReq->enmTxState = AHCITXSTATE_FREE;
    6684                 pAhciPort->aCachedTasks[idx] = pAhciReq;
    6685             }
    6686             else
    6687                 pAhciReq = pAhciPort->aCachedTasks[idx];
    6688 
    6689             bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pAhciReq->enmTxState, AHCITXSTATE_ACTIVE, AHCITXSTATE_FREE);
    6690             AssertMsg(fXchg, ("Task is already active\n"));
    6691 
    6692             pAhciReq->tsStart = RTTimeMilliTS();
    6693             pAhciReq->uATARegStatus = 0;
    6694             pAhciReq->uATARegError  = 0;
    6695             pAhciReq->fFlags        = 0;
    6696 
    6697             /* Set current command slot */
    6698             pAhciReq->uTag = idx;
    6699             ASMAtomicWriteU32(&pAhciPort->u32CurrentCommandSlot, pAhciReq->uTag);
    6700 
    6701             bool fFisRead = ahciPortTaskGetCommandFis(pAhciPort, pAhciReq);
    6702             if (RT_UNLIKELY(!fFisRead))
    6703             {
    6704                 /*
    6705                  * Couldn't find anything in either the AHCI or SATA spec which
    6706                  * indicates what should be done if the FIS is not read successfully.
    6707                  * The closest thing is in the state machine, stating that the device
    6708                  * should go into idle state again (SATA spec 1.0 chapter 8.7.1).
    6709                  * Do the same here and ignore any corrupt FIS types, after all
    6710                  * the guest messed up everything and this behavior is undefined.
    6711                  */
    6712                 fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE);
    6713                 Assert(fXchg);
    6714                 u32Tasks &= ~RT_BIT_32(idx); /* Clear task bit. */
    6715                 idx = ASMBitFirstSetU32(u32Tasks);
     6876                ahciLog(("%s: Ignoring command at slot %d because it is already active\n", __FUNCTION__, idx));
    67166877                continue;
    67176878            }
    67186879
    6719             /* Mark the task as processed by the HBA if this is a queued task so that it doesn't occur in the CI register anymore. */
    6720             if (pAhciPort->regSACT & (1 << idx))
     6880            pAhciReq = ahciR3ReqAlloc(pAhciPort);
     6881            if (RT_LIKELY(pAhciReq))
    67216882            {
    6722                 pAhciReq->fFlags |= AHCI_REQ_CLEAR_SACT;
    6723                 ASMAtomicOrU32(&pAhciPort->u32TasksFinished, (1 << pAhciReq->uTag));
     6883                pAhciReq->uTag          = idx;
     6884                pAhciReq->fFlags        = 0;
     6885
     6886                bool fContinue = ahciR3CmdPrepare(pAhciPort, pAhciReq);
     6887                if (fContinue)
     6888                {
     6889                    enmTxDir = ahciProcessCmd(pAhciPort, pAhciReq, pAhciReq->cmdFis);
     6890                    pAhciReq->enmTxDir = enmTxDir;
     6891
     6892                    if (enmTxDir != AHCITXDIR_NONE)
     6893                    {
     6894                        if (   enmTxDir != AHCITXDIR_FLUSH
     6895                            && enmTxDir != AHCITXDIR_TRIM)
     6896                        {
     6897                            STAM_REL_COUNTER_INC(&pAhciPort->StatDMA);
     6898
     6899                            rc = ahciIoBufAllocate(pAhciPort, pAhciReq, pAhciReq->cbTransfer);
     6900                            if (RT_FAILURE(rc))
     6901                            {
     6902                                /* In case we can't allocate enough memory fail the request with an overflow error. */
     6903                                AssertMsgFailed(("%s: Failed to process command %Rrc\n", __FUNCTION__, rc));
     6904                                pAhciReq->fFlags |= AHCI_REQ_OVERFLOW;
     6905                            }
     6906                        }
     6907
     6908                        if (!(pAhciReq->fFlags & AHCI_REQ_OVERFLOW))
     6909                            fReqCanceled = ahciR3ReqSubmit(pAhciPort, pAhciReq, enmTxDir);
     6910                        else /* Overflow is handled in completion routine. */
     6911                            fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS);
     6912                    }
     6913                    else
     6914                        fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS);
     6915                } /* Command */
     6916                else
     6917                {
     6918                    ASMAtomicWritePtr(&pAhciPort->aActiveTasks[pAhciReq->uTag], NULL);
     6919                    ahciR3ReqFree(pAhciPort, pAhciReq);
     6920                }
    67246921            }
    6725 
    6726             if (!(pAhciReq->cmdFis[AHCI_CMDFIS_BITS] & AHCI_CMDFIS_C))
     6922            else /* !Request allocated, use on stack variant to signal the error. */
    67276923            {
    6728                 /* If the reset bit is set put the device into reset state. */
    6729                 if (pAhciReq->cmdFis[AHCI_CMDFIS_CTL] & AHCI_CMDFIS_CTL_SRST)
     6924                AHCIREQ Req;
     6925                Req.uTag   = idx;
     6926                Req.fFlags = AHCI_REQ_IS_ON_STACK;
     6927
     6928                bool fContinue = ahciR3CmdPrepare(pAhciPort, &Req);
     6929                if (fContinue)
     6930                    fReqCanceled = ahciTransferComplete(pAhciPort, &Req, VERR_NO_MEMORY);
     6931                else
    67306932                {
    6731                     ahciLog(("%s: Setting device into reset state\n", __FUNCTION__));
    6732                     pAhciPort->fResetDevice = true;
    6733                     ahciSendD2HFis(pAhciPort, pAhciReq, pAhciReq->cmdFis, true);
     6933                    ASMAtomicWritePtr(&pAhciPort->aActiveTasks[pAhciReq->uTag], NULL);
     6934                    ahciR3ReqFree(pAhciPort, pAhciReq);
    67346935                }
    6735                 else if (pAhciPort->fResetDevice) /* The bit is not set and we are in a reset state. */
    6736                     ahciFinishStorageDeviceReset(pAhciPort, pAhciReq);
    6737                 else /* We are not in a reset state update the control registers. */
    6738                     AssertMsgFailed(("%s: Update the control register\n", __FUNCTION__));
    6739 
    6740                 fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE);
    6741                 AssertMsg(fXchg, ("Task is not active\n"));
    6742                 break;
    67436936            }
    6744             else
    6745             {
    6746                 AssertReleaseMsg(ASMAtomicReadU32(&pAhciPort->cTasksActive) < AHCI_NR_COMMAND_SLOTS,
    6747                                  ("There are more than 32 requests active"));
    6748                 ASMAtomicIncU32(&pAhciPort->cTasksActive);
    6749 
    6750                 enmTxDir = ahciProcessCmd(pAhciPort, pAhciReq, pAhciReq->cmdFis);
    6751                 pAhciReq->enmTxDir = enmTxDir;
    6752 
    6753                 if (enmTxDir != AHCITXDIR_NONE)
    6754                 {
    6755                     if (   enmTxDir != AHCITXDIR_FLUSH
    6756                         && enmTxDir != AHCITXDIR_TRIM)
    6757                     {
    6758                         STAM_REL_COUNTER_INC(&pAhciPort->StatDMA);
    6759 
    6760                         rc = ahciIoBufAllocate(pAhciPort, pAhciReq, pAhciReq->cbTransfer);
    6761                         if (RT_FAILURE(rc))
    6762                         {
    6763                             /* In case we can't allocate enough memory fail the request with an overflow error. */
    6764                             AssertMsgFailed(("%s: Failed to process command %Rrc\n", __FUNCTION__, rc));
    6765                             pAhciReq->fFlags |= AHCI_REQ_OVERFLOW;
    6766                         }
    6767                     }
    6768 
    6769                     if (!(pAhciReq->fFlags & AHCI_REQ_OVERFLOW))
    6770                     {
    6771                         if (pAhciPort->fAsyncInterface)
    6772                         {
    6773                             VBOXDD_AHCI_REQ_SUBMIT(pAhciReq, enmTxDir, pAhciReq->uOffset, pAhciReq->cbTransfer);
    6774                             VBOXDD_AHCI_REQ_SUBMIT_TIMESTAMP(pAhciReq, pAhciReq->tsStart);
    6775                             if (enmTxDir == AHCITXDIR_FLUSH)
    6776                             {
    6777                                 rc = pAhciPort->pDrvBlockAsync->pfnStartFlush(pAhciPort->pDrvBlockAsync,
    6778                                                                               pAhciReq);
    6779                             }
    6780                             else if (enmTxDir == AHCITXDIR_TRIM)
    6781                             {
    6782                                 rc = ahciTrimRangesCreate(pAhciPort, pAhciReq);
    6783                                 if (RT_SUCCESS(rc))
    6784                                 {
    6785                                     pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
    6786                                     rc = pAhciPort->pDrvBlockAsync->pfnStartDiscard(pAhciPort->pDrvBlockAsync, pAhciReq->u.Trim.paRanges,
    6787                                                                                     pAhciReq->u.Trim.cRanges, pAhciReq);
    6788                                 }
    6789                             }
    6790                             else if (enmTxDir == AHCITXDIR_READ)
    6791                             {
    6792                                 pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1;
    6793                                 rc = pAhciPort->pDrvBlockAsync->pfnStartRead(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset,
    6794                                                                              &pAhciReq->u.Io.DataSeg, 1,
    6795                                                                              pAhciReq->cbTransfer,
    6796                                                                              pAhciReq);
    6797                             }
    6798                             else
    6799                             {
    6800                                 pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
    6801                                 rc = pAhciPort->pDrvBlockAsync->pfnStartWrite(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset,
    6802                                                                               &pAhciReq->u.Io.DataSeg, 1,
    6803                                                                               pAhciReq->cbTransfer,
    6804                                                                               pAhciReq);
    6805                             }
    6806                             if (rc == VINF_VD_ASYNC_IO_FINISHED)
    6807                                 fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true);
    6808                             else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
    6809                                 fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc, true);
    6810                         }
    6811                         else
    6812                         {
    6813                             if (enmTxDir == AHCITXDIR_FLUSH)
    6814                                 rc = pAhciPort->pDrvBlock->pfnFlush(pAhciPort->pDrvBlock);
    6815                             else if (enmTxDir == AHCITXDIR_TRIM)
    6816                             {
    6817                                 rc = ahciTrimRangesCreate(pAhciPort, pAhciReq);
    6818                                 if (RT_SUCCESS(rc))
    6819                                 {
    6820                                     pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
    6821                                     rc = pAhciPort->pDrvBlock->pfnDiscard(pAhciPort->pDrvBlock, pAhciReq->u.Trim.paRanges,
    6822                                                                           pAhciReq->u.Trim.cRanges);
    6823                                     pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 0;
    6824                                 }
    6825                             }
    6826                             else if (enmTxDir == AHCITXDIR_READ)
    6827                             {
    6828                                 pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1;
    6829                                 rc = pAhciPort->pDrvBlock->pfnRead(pAhciPort->pDrvBlock, pAhciReq->uOffset,
    6830                                                                    pAhciReq->u.Io.DataSeg.pvSeg,
    6831                                                                    pAhciReq->cbTransfer);
    6832                                 pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 0;
    6833                             }
    6834                             else
    6835                             {
    6836                                 pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
    6837                                 rc = pAhciPort->pDrvBlock->pfnWrite(pAhciPort->pDrvBlock, pAhciReq->uOffset,
    6838                                                                     pAhciReq->u.Io.DataSeg.pvSeg,
    6839                                                                     pAhciReq->cbTransfer);
    6840                                 pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 0;
    6841                             }
    6842                             fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc, true);
    6843                         }
    6844                     }
    6845                 }
    6846                 else
    6847                     fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true);
    6848             } /* Command */
    68496937
    68506938            /*
     
    75817669    pAhciPort->fATAPIPassthrough = pAhciPort->fATAPI ? (pAhciPort->pDrvBlock->pfnSendCmd != NULL) : false;
    75827670
     7671    rc = RTCritSectInit(&pAhciPort->CritSectReqsFree);
     7672    if (RT_FAILURE(rc))
     7673        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
     7674                                   N_("AHCI initialisation error: Failed to create critical section for free request list"));
     7675
     7676    pAhciPort->pListReqsFree = (PRTLISTANCHOR)PDMDevHlpMMHeapAllocZ(pDevIns, sizeof(RTLISTANCHOR));
     7677    if (!pAhciPort->pListReqsFree)
     7678        return PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
     7679                                   N_("AHCI initialisation error: Failed to allocate memory for free request list"));
     7680
     7681    RTListInit(pAhciPort->pListReqsFree);
     7682
    75837683    if (pAhciPort->fATAPI)
    75847684    {
     
    78847984    /* Free all cached I/O tasks. */
    78857985    ahciR3PortCachedReqsFree(pAhciPort);
     7986
     7987    if (RTCritSectIsInitialized(&pAhciPort->CritSectReqsFree))
     7988        RTCritSectDelete(&pAhciPort->CritSectReqsFree);
     7989
     7990    if (pAhciPort->pListReqsFree)
     7991        MMR3HeapFree(pAhciPort->pListReqsFree);
     7992
     7993    pAhciPort->pListReqsFree = NULL;
    78867994
    78877995    if (!(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG))
     
    81308238            }
    81318239
     8240            if (RTCritSectIsInitialized(&pAhciPort->CritSectReqsFree))
     8241                RTCritSectDelete(&pAhciPort->CritSectReqsFree);
     8242
    81328243#ifdef VBOX_STRICT
    81338244            for (uint32_t i = 0; i < AHCI_NR_COMMAND_SLOTS; i++)
    8134                 Assert(!pAhciPort->aCachedTasks[i]);
     8245                Assert(!pAhciPort->aActiveTasks[i]);
    81358246#endif
    81368247        }
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