VirtualBox

Changeset 39893 in vbox


Ignore:
Timestamp:
Jan 26, 2012 9:24:35 PM (13 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
75943
Message:

VD: Optimization, reduce locking contention between EMT and the I/O thread by only trying to enter the lock and deferring the request from EMT if the lock is held already. Should allow EMT to resume guest execution earlier

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Storage/VD.cpp

    r39798 r39893  
    230230    /** Critical section protecting the disk against concurrent access. */
    231231    RTCRITSECT             CritSect;
     232    /** Head of queued I/O contexts - LIFO order. */
     233    volatile PVDIOCTX      pIoCtxHead;
    232234    /** Flag whether the disk is currently locked by growing write or a flush
    233235     * request. Other flush or growing write requests need to wait until
     
    291293typedef struct VDIOCTX
    292294{
     295    /** Pointer to the next I/O context. */
     296    struct VDIOCTX * volatile    pIoCtxNext;
    293297    /** Disk this is request is for. */
    294298    PVBOXHDD                     pDisk;
     
    14231427    if (RT_LIKELY(pIoCtx))
    14241428    {
     1429        pIoCtx->pIoCtxNext                = NULL;
    14251430        pIoCtx->pDisk                     = pDisk;
    14261431        pIoCtx->enmTxDir                  = VDIOCTXTXDIR_DISCARD;
     
    15971602}
    15981603
    1599 static int vdIoCtxProcess(PVDIOCTX pIoCtx)
     1604/**
     1605 * Process the I/O context, core method which assumes that the critsect is acquired
     1606 * by the calling thread.
     1607 *
     1608 * @returns VBox status code.
     1609 * @param   pIoCtx    I/O context to process.
     1610 */
     1611static int vdIoCtxProcessLocked(PVDIOCTX pIoCtx)
    16001612{
    16011613    int rc = VINF_SUCCESS;
    1602     PVBOXHDD pDisk = pIoCtx->pDisk;
     1614
     1615    VD_THREAD_IS_CRITSECT_OWNER(pIoCtx->pDisk);
    16031616
    16041617    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
    1605 
    1606     RTCritSectEnter(&pDisk->CritSect);
    16071618
    16081619    if (   !pIoCtx->cMetaTransfersPending
     
    16761687
    16771688out:
    1678     RTCritSectLeave(&pDisk->CritSect);
    1679 
    16801689    LogFlowFunc(("pIoCtx=%#p rc=%Rrc cDataTransfersPending=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
    16811690                 pIoCtx, rc, pIoCtx->cDataTransfersPending, pIoCtx->cMetaTransfersPending,
    16821691                 pIoCtx->fComplete));
     1692
     1693    return rc;
     1694}
     1695
     1696/**
     1697 * Processes the list of waiting I/O contexts.
     1698 *
     1699 * @returns VBox status code.
     1700 * @param   pDisk    The disk structure.
     1701 * @param   pIoCtxRc An I/O context handle which waits on the list. When processed
     1702 *                   The status code is returned. NULL if there is no I/O context
     1703 *                   to return the status code for.
     1704 */
     1705static int vdDiskProcessWaitingIoCtx(PVBOXHDD pDisk, PVDIOCTX pIoCtxRc)
     1706{
     1707    int rc = VINF_SUCCESS;
     1708
     1709    LogFlowFunc(("pDisk=%#p pIoCtxRc=%#p\n", pDisk, pIoCtxRc));
     1710
     1711    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
     1712
     1713    /* Get the waiting list and process it in FIFO order. */
     1714    PVDIOCTX pIoCtxHead = ASMAtomicXchgPtrT(&pDisk->pIoCtxHead, NULL, PVDIOCTX);
     1715
     1716    /* Reverse it. */
     1717    PVDIOCTX pCur = pIoCtxHead;
     1718    pIoCtxHead = NULL;
     1719    while (pCur)
     1720    {
     1721        PVDIOCTX pInsert = pCur;
     1722        pCur = pCur->pIoCtxNext;
     1723        pInsert->pIoCtxNext = pIoCtxHead;
     1724        pIoCtxHead = pInsert;
     1725    }
     1726
     1727    /* Process now. */
     1728    pCur = pIoCtxHead;
     1729    while (pCur)
     1730    {
     1731        int rcTmp;
     1732        PVDIOCTX pTmp = pCur;
     1733
     1734        pCur = pCur->pIoCtxNext;
     1735        pTmp->pIoCtxNext = NULL;
     1736
     1737        rcTmp = vdIoCtxProcessLocked(pTmp);
     1738        if (pTmp == pIoCtxRc)
     1739        {
     1740            /* The given I/O context was processed, pass the return code to the caller. */
     1741            rc = rcTmp;
     1742        }
     1743        else if (   rcTmp == VINF_VD_ASYNC_IO_FINISHED
     1744                 && ASMAtomicCmpXchgBool(&pTmp->fComplete, true, false))
     1745        {
     1746            LogFlowFunc(("Waiting I/O context completed pTmp=%#p\n", pTmp));
     1747            vdThreadFinishWrite(pDisk);
     1748            pTmp->Type.Root.pfnComplete(pTmp->Type.Root.pvUser1,
     1749                                        pTmp->Type.Root.pvUser2,
     1750                                        pTmp->rcReq);
     1751            vdIoCtxFree(pDisk, pTmp);
     1752        }
     1753    }
     1754
     1755    LogFlowFunc(("returns rc=%Rrc\n", rc));
     1756    return rc;
     1757}
     1758
     1759/**
     1760 * Leaves the critical section of the disk processing waiting I/O contexts.
     1761 *
     1762 * @returns VBox status code.
     1763 * @param   pDisk    The disk to unlock.
     1764 * @param   pIoCtxRc An I/O context handle which waits on the list. When processed
     1765 *                   The status code is returned. NULL if there is no I/O context
     1766 *                   to return the status code for.
     1767 */
     1768static int vdDiskCritSectLeave(PVBOXHDD pDisk, PVDIOCTX pIoCtxRc)
     1769{
     1770    int rc = VINF_SUCCESS;
     1771
     1772    LogFlowFunc(("pDisk=%#p pIoCtxRc=%#p\n", pDisk, pIoCtxRc));
     1773
     1774    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
     1775
     1776    rc = vdDiskProcessWaitingIoCtx(pDisk, pIoCtxRc);
     1777    RTCritSectLeave(&pDisk->CritSect);
     1778
     1779    /*
     1780     * We have to check for new waiting contexts here. It is possible that
     1781     * another thread has queued another one while process waiting contexts
     1782     * and because we still held the lock it was appended to the waiting list.
     1783     *
     1784     * @note Don't overwrite rc here because this might result in loosing
     1785     *       the status code of the given I/O context.
     1786     */
     1787    while (ASMAtomicReadPtrT(&pDisk->pIoCtxHead, PVDIOCTX) != NULL)
     1788    {
     1789        int rc2 = RTCritSectTryEnter(&pDisk->CritSect);
     1790
     1791        if (RT_SUCCESS(rc2))
     1792        {
     1793            /*
     1794             * Don't pass status codes for any I/O context here. The context must hae been
     1795             * in the first run.
     1796             */
     1797            vdDiskProcessWaitingIoCtx(pDisk, NULL);
     1798            RTCritSectLeave(&pDisk->CritSect);
     1799        }
     1800        else
     1801        {
     1802            /*
     1803             * Another thread is holding the lock already and will process the list
     1804             * whewn leaving the lock, nothing left to do for us.
     1805             */
     1806            Assert(rc2 == VERR_SEM_BUSY);
     1807            break;
     1808        }
     1809    }
     1810
     1811    LogFlowFunc(("returns rc=%Rrc\n", rc));
     1812    return rc;
     1813}
     1814
     1815/**
     1816 * Processes the I/O context trying to lock the criticial section.
     1817 * The context is deferred if the critical section is busy.
     1818 *
     1819 * @returns VBox status code.
     1820 * @param   pIoCtx    The I/O context to process.
     1821 */
     1822static int vdIoCtxProcessTryLockDefer(PVDIOCTX pIoCtx)
     1823{
     1824    int rc = VINF_SUCCESS;
     1825    PVBOXHDD pDisk = pIoCtx->pDisk;
     1826
     1827    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
     1828
     1829    /* Put it on the waiting list first. */
     1830    PVDIOCTX pNext = ASMAtomicUoReadPtrT(&pDisk->pIoCtxHead, PVDIOCTX);
     1831    PVDIOCTX pHeadOld;
     1832    pIoCtx->pIoCtxNext = pNext;
     1833    while (!ASMAtomicCmpXchgExPtr(&pDisk->pIoCtxHead, pIoCtx, pNext, &pHeadOld))
     1834    {
     1835        pNext = pHeadOld;
     1836        Assert(pNext != pIoCtx);
     1837        pIoCtx->pIoCtxNext = pNext;
     1838        ASMNopPause();
     1839    }
     1840
     1841    rc = RTCritSectTryEnter(&pDisk->CritSect);
     1842    if (RT_SUCCESS(rc))
     1843    {
     1844        /* Leave it again, the context will be processed just before leaving the lock. */
     1845        LogFlowFunc(("Successfully acquired the critical section\n"));
     1846        rc = vdDiskCritSectLeave(pDisk, pIoCtx);
     1847    }
     1848    else
     1849    {
     1850        AssertMsg(rc == VERR_SEM_BUSY, ("Invalid return code %Rrc\n", rc));
     1851        LogFlowFunc(("Critical section is busy\n"));
     1852        rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
     1853    }
     1854
     1855    return rc;
     1856}
     1857
     1858/**
     1859 * Wrapper for vdIoCtxProcessLocked() which acquires the lock before.
     1860 *
     1861 * @returns VBox status code.
     1862 * @param   pIoCtx    I/O context to process.
     1863 */
     1864static int vdIoCtxProcess(PVDIOCTX pIoCtx)
     1865{
     1866    int rc = VINF_SUCCESS;
     1867    PVBOXHDD pDisk = pIoCtx->pDisk;
     1868
     1869    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
     1870
     1871    RTCritSectEnter(&pDisk->CritSect);
     1872    rc = vdIoCtxProcessLocked(pIoCtx);
     1873    vdDiskCritSectLeave(pDisk, NULL);
    16831874
    16841875    return rc;
     
    17361927
    17371928            RTListMove(&ListTmp, &pDisk->ListWriteLocked);
    1738             RTCritSectLeave(&pDisk->CritSect);
     1929            vdDiskCritSectLeave(pDisk, NULL);
    17391930
    17401931            /* Process the list. */
     
    17691960        }
    17701961        else
    1771             RTCritSectLeave(&pDisk->CritSect);
     1962            vdDiskCritSectLeave(pDisk, NULL);
    17721963    }
    17731964
     
    37093900        rc = VINF_SUCCESS;
    37103901
    3711     RTCritSectLeave(&pDisk->CritSect);
     3902    vdDiskCritSectLeave(pDisk, NULL);
    37123903
    37133904    return rc;
     
    37943985        RTMemFree(pMetaXfer);
    37953986
    3796     RTCritSectLeave(&pDisk->CritSect);
     3987    vdDiskCritSectLeave(pDisk, NULL);
    37973988
    37983989    return VINF_SUCCESS;
     
    44684659    vdIoCtxContinue(pIoCtx, rcReq);
    44694660
    4470     rc = RTCritSectLeave(&pDisk->CritSect);
    4471     AssertRC(rc);
     4661    vdDiskCritSectLeave(pDisk, NULL);
    44724662}
    44734663
     
    91319321        }
    91329322
    9133         rc = vdIoCtxProcess(pIoCtx);
     9323        rc = vdIoCtxProcessTryLockDefer(pIoCtx);
    91349324        if (rc == VINF_VD_ASYNC_IO_FINISHED)
    91359325        {
     
    92029392        }
    92039393
    9204         rc = vdIoCtxProcess(pIoCtx);
     9394        rc = vdIoCtxProcessTryLockDefer(pIoCtx);
    92059395        if (rc == VINF_VD_ASYNC_IO_FINISHED)
    92069396        {
     
    92589448        }
    92599449
    9260         rc = vdIoCtxProcess(pIoCtx);
     9450        rc = vdIoCtxProcessTryLockDefer(pIoCtx);
    92619451        if (rc == VINF_VD_ASYNC_IO_FINISHED)
    92629452        {
     
    93139503        }
    93149504
    9315         rc = vdIoCtxProcess(pIoCtx);
     9505        rc = vdIoCtxProcessTryLockDefer(pIoCtx);
    93169506        if (rc == VINF_VD_ASYNC_IO_FINISHED)
    93179507        {
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