VirtualBox

Changeset 52324 in vbox


Ignore:
Timestamp:
Aug 8, 2014 12:41:32 PM (10 years ago)
Author:
vboxsync
Message:

Main/Medium: fix deadlock between Medium::RefreshState and Medium::Close, lock order violation involving the event sem used in AutoCaller.

Location:
trunk/src/VBox/Main
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/idl/VirtualBox.xidl

    r52319 r52324  
    1386713867    </attribute>
    1386813868
    13869     <method name="setIds">
     13869    <method name="setIds" wrap-hint-server="passcaller">
    1387013870      <desc>
    1387113871        Changes the UUID and parent UUID for a hard disk medium.
     
    1390313903    </method>
    1390413904
    13905     <method name="refreshState">
     13905    <method name="refreshState" wrap-hint-server="passcaller">
    1390613906      <desc>
    1390713907        If the current medium state (see <link to="MediumState"/>) is one of
  • trunk/src/VBox/Main/include/MediumImpl.h

    r52096 r52324  
    230230
    231231    // wrapped IMedium methods
    232     HRESULT setIds(BOOL aSetImageId,
     232    HRESULT setIds(AutoCaller &aAutoCaller,
     233                   BOOL aSetImageId,
    233234                   const com::Guid &aImageId,
    234235                   BOOL aSetParentId,
    235236                   const com::Guid &aParentId);
    236     HRESULT refreshState(MediumState_T *aState);
     237    HRESULT refreshState(AutoCaller &aAutoCaller,
     238                         MediumState_T *aState);
    237239    HRESULT getSnapshotIds(const com::Guid &aMachineId,
    238240                           std::vector<com::Guid> &aSnapshotIds);
     
    273275
    274276    // Private internal nmethods
    275     HRESULT i_queryInfo(bool fSetImageId, bool fSetParentId);
     277    HRESULT i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller);
    276278    HRESULT i_canClose();
    277279    HRESULT i_unregisterWithVirtualBox();
  • trunk/src/VBox/Main/src-server/MediumImpl.cpp

    r52168 r52324  
    131131
    132132    /** Special synchronization for operations which must wait for
    133      * Medium::queryInfo in another thread to complete. Using a SemRW is
     133     * Medium::i_queryInfo in another thread to complete. Using a SemRW is
    134134     * not quite ideal, but at least it is subject to the lock validator,
    135135     * unlike the SemEventMulti which we had here for many years. Catching
     
    149149    bool autoReset : 1;
    150150
    151     /** New UUID to be set on the next Medium::queryInfo call. */
     151    /** New UUID to be set on the next Medium::i_queryInfo call. */
    152152    const Guid uuidImage;
    153     /** New parent UUID to be set on the next Medium::queryInfo call. */
     153    /** New parent UUID to be set on the next Medium::i_queryInfo call. */
    154154    const Guid uuidParentImage;
    155155
     
    926926    m->hostDrive = false;
    927927
    928     /* No storage unit is created yet, no need to call Medium::queryInfo */
     928    /* No storage unit is created yet, no need to call Medium::i_queryInfo */
    929929
    930930    rc = i_setFormat(aFormat);
     
    10511051        m->strLastAccessError = tr("Accessibility check was not yet performed");
    10521052
    1053         /* Confirm a successful initialization before the call to queryInfo.
     1053        /* Confirm a successful initialization before the call to i_queryInfo.
    10541054         * Otherwise we can end up with a AutoCaller deadlock because the
    10551055         * medium becomes visible but is not marked as initialized. Causes
     
    10641064        return autoCaller.rc();
    10651065
    1066     /* need to call queryInfo immediately to correctly place the medium in
     1066    /* need to call i_queryInfo immediately to correctly place the medium in
    10671067     * the respective media tree and update other information such as uuid */
    1068     rc = i_queryInfo(fForceNewUuid /* fSetImageId */, false /* fSetParentId */);
     1068    rc = i_queryInfo(fForceNewUuid /* fSetImageId */, false /* fSetParentId */,
     1069                     autoCaller);
    10691070    if (SUCCEEDED(rc))
    10701071    {
     
    10861087                       alock.release(); autoCaller.release(); uninit(); return E_FAIL);
    10871088
    1088             /* storage format must be detected by Medium::queryInfo if the
     1089            /* storage format must be detected by Medium::i_queryInfo if the
    10891090             * medium is accessible */
    10901091            AssertStmt(!m->strFormat.isEmpty(),
     
    11531154    }
    11541155
    1155     /* see below why we don't call Medium::queryInfo (and therefore treat
     1156    /* see below why we don't call Medium::i_queryInfo (and therefore treat
    11561157     * the medium as inaccessible for now */
    11571158    m->state = MediumState_Inaccessible;
     
    12601261                     m->strLocationFull.c_str(), m->strFormat.c_str(), m->id.raw()));
    12611262
    1262     /* Don't call Medium::queryInfo for registered media to prevent the calling
     1263    /* Don't call Medium::i_queryInfo for registered media to prevent the calling
    12631264     * thread (i.e. the VirtualBox server startup thread) from an unexpected
    12641265     * freeze but mark it as initially inaccessible instead. The vital UUID,
     
    18551856}
    18561857
    1857 HRESULT Medium::setIds(BOOL aSetImageId,
     1858HRESULT Medium::setIds(AutoCaller &autoCaller,
     1859                       BOOL aSetImageId,
    18581860                       const com::Guid &aImageId,
    18591861                       BOOL aSetParentId,
     
    18931895    unconst(m->uuidParentImage) = parentId;
    18941896
    1895     // must not hold any locks before calling Medium::queryInfo
     1897    // must not hold any locks before calling Medium::i_queryInfo
    18961898    alock.release();
    18971899
    18981900    HRESULT rc = i_queryInfo(!!aSetImageId /* fSetImageId */,
    1899                            !!aSetParentId /* fSetParentId */);
     1901                             !!aSetParentId /* fSetParentId */,
     1902                             autoCaller);
    19001903
    19011904    return rc;
    19021905}
    19031906
    1904 HRESULT Medium::refreshState(MediumState_T *aState)
     1907HRESULT Medium::refreshState(AutoCaller &autoCaller, MediumState_T *aState)
    19051908{
    19061909    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     
    19141917        case MediumState_LockedRead:
    19151918        {
    1916             // must not hold any locks before calling Medium::queryInfo
     1919            // must not hold any locks before calling Medium::i_queryInfo
    19171920            alock.release();
    19181921
    1919             rc = i_queryInfo(false /* fSetImageId */, false /* fSetParentId */);
     1922            rc = i_queryInfo(false /* fSetImageId */, false /* fSetParentId */,
     1923                             autoCaller);
    19201924
    19211925            alock.acquire();
     
    19691973HRESULT Medium::lockRead(ComPtr<IToken> &aToken)
    19701974{
    1971     /* Must not hold the object lock, as we need control over it below. */
    1972     Assert(!isWriteLockOnCurrentThread());
    19731975    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    19741976
    1975     /* Wait for a concurrently running Medium::queryInfo to complete. */
     1977    /* Wait for a concurrently running Medium::i_queryInfo to complete. */
    19761978    if (m->queryInfoRunning)
    19771979    {
    1978         /* Must not hold the media tree lock, as Medium::queryInfo needs this
     1980        /* Must not hold the media tree lock, as Medium::i_queryInfo needs this
    19791981         * lock and thus we would run into a deadlock here. */
    19801982        Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
     
    19821984        {
    19831985            alock.release();
     1986            /* must not hold the object lock now */
     1987            Assert(!isWriteLockOnCurrentThread());
    19841988            {
    19851989                AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
     
    20862090HRESULT Medium::lockWrite(ComPtr<IToken> &aToken)
    20872091{
    2088     /* Must not hold the object lock, as we need control over it below. */
    2089     Assert(!isWriteLockOnCurrentThread());
    20902092    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    20912093
    2092     /* Wait for a concurrently running Medium::queryInfo to complete. */
     2094    /* Wait for a concurrently running Medium::i_queryInfo to complete. */
    20932095    if (m->queryInfoRunning)
    20942096    {
    2095         /* Must not hold the media tree lock, as Medium::queryInfo needs this
     2097        /* Must not hold the media tree lock, as Medium::i_queryInfo needs this
    20962098         * lock and thus we would run into a deadlock here. */
    20972099        Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
     
    20992101        {
    21002102            alock.release();
     2103            /* must not hold the object lock now */
     2104            Assert(!isWriteLockOnCurrentThread());
    21012105            {
    21022106                AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
     
    37943798        {
    37953799            alock.release();
    3796             rc = pMedium->i_queryInfo(false /* fSetImageId */, false /* fSetParentId */);
     3800            rc = pMedium->i_queryInfo(false /* fSetImageId */, false /* fSetParentId */,
     3801                                      autoCaller);
    37973802            alock.acquire();
    37983803            if (FAILED(rc)) return rc;
     
    54115416 * @return
    54125417 */
    5413 HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId)
     5418HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller)
    54145419{
    54155420    Assert(!isWriteLockOnCurrentThread());
     
    54255430    int vrc = VINF_SUCCESS;
    54265431
    5427     /* check if a blocking queryInfo() call is in progress on some other thread,
     5432    /* check if a blocking i_queryInfo() call is in progress on some other thread,
    54285433     * and wait for it to finish if so instead of querying data ourselves */
    54295434    if (m->queryInfoRunning)
     
    54355440        {
    54365441            alock.release();
     5442            /* must not hold the object lock now */
     5443            Assert(!isWriteLockOnCurrentThread());
    54375444            {
    54385445                AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
     
    54675474        uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
    54685475
    5469     /* Lock the medium, which makes the behavior much more consistent */
    5470     alock.release();
     5476    /* Lock the medium, which makes the behavior much more consistent, must be
     5477     * done before dropping the object lock and setting queryInfoRunning. */
    54715478    ComPtr<IToken> pToken;
    54725479    if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
     
    54755482        rc = LockWrite(pToken.asOutParam());
    54765483    if (FAILED(rc)) return rc;
    5477     alock.acquire();
    54785484
    54795485    /* Copies of the input state fields which are not read-only,
     
    54955501    bool fRepairImageZeroParentUuid = false;
    54965502
    5497     /* release the object lock before a lengthy operation, and take the
    5498      * opportunity to have a media tree lock, too, which isn't held initially */
     5503    ComObjPtr<VirtualBox> pVirtualBox = m->pVirtualBox;
     5504
     5505    /* must be set before leaving the object lock the first time */
    54995506    m->queryInfoRunning = true;
     5507
     5508    /* must leave object lock now, because a lock from a higher lock class
     5509     * is needed and also a lengthy operation is coming */
    55005510    alock.release();
    5501     Assert(!isWriteLockOnCurrentThread());
    5502     Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
    5503     AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    5504     treeLock.release();
     5511    autoCaller.release();
    55055512
    55065513    /* Note that taking the queryInfoSem after leaving the object lock above
    5507      * can lead to short spinning of the loops waiting for queryInfo() to
     5514     * can lead to short spinning of the loops waiting for i_queryInfo() to
    55085515     * complete. This is unavoidable since the other order causes a lock order
    55095516     * violation: here it would be requesting the object lock (at the beginning
    55105517     * of the method), then queryInfoSem, and below the other way round. */
    55115518    AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
     5519
     5520    /* take the opportunity to have a media tree lock, released initially */
     5521    Assert(!isWriteLockOnCurrentThread());
     5522    Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
     5523    AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     5524    treeLock.release();
     5525
     5526    /* re-take the caller, but not the object lock, to keep uninit away */
     5527    autoCaller.add();
     5528    if (FAILED(autoCaller.rc()))
     5529    {
     5530        m->queryInfoRunning = false;
     5531        return autoCaller.rc();
     5532    }
    55125533
    55135534    try
     
    56015622                                location.c_str(),
    56025623                                mediumId.raw(),
    5603                                 m->pVirtualBox->i_settingsFilePath().c_str());
     5624                                pVirtualBox->i_settingsFilePath().c_str());
    56045625                        throw S_OK;
    56055626                    }
     
    56675688                        rc = VBOX_E_OBJECT_NOT_FOUND;
    56685689                    else
    5669                         rc = m->pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
     5690                        rc = pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
    56705691                    if (FAILED(rc))
    56715692                    {
     
    56895710                            lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
    56905711                                                         &parentId, location.c_str(),
    5691                                                          m->pVirtualBox->i_settingsFilePath().c_str());
     5712                                                         pVirtualBox->i_settingsFilePath().c_str());
    56925713                            throw S_OK;
    56935714                        }
    56945715                    }
    56955716
     5717                    /* must drop the caller before taking the tree lock */
     5718                    autoCaller.release();
    56965719                    /* we set mParent & children() */
    56975720                    treeLock.acquire();
     5721                    autoCaller.add();
     5722                    if (FAILED(autoCaller.rc()))
     5723                        throw autoCaller.rc();
    56985724
    56995725                    if (m->pParent)
     
    57055731                else
    57065732                {
     5733                    /* must drop the caller before taking the tree lock */
     5734                    autoCaller.release();
    57075735                    /* we access mParent */
    57085736                    treeLock.acquire();
     5737                    autoCaller.add();
     5738                    if (FAILED(autoCaller.rc()))
     5739                        throw autoCaller.rc();
    57095740
    57105741                    /* check that parent UUIDs match. Note that there's no need
     
    57285759                                tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
    57295760                                location.c_str(),
    5730                                 m->pVirtualBox->settingsFilePath().c_str());
     5761                                pVirtualBox->settingsFilePath().c_str());
    57315762                        treeLock.release();
    57325763                        throw S_OK;
     
    57355766
    57365767                    {
     5768                        autoCaller.release();
    57375769                        AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
     5770                        autoCaller.add();
     5771                        if (FAILED(autoCaller.rc()))
     5772                            throw autoCaller.rc();
     5773
    57385774                        if (   !fRepairImageZeroParentUuid
    57395775                            && m->pParent->i_getState() != MediumState_Inaccessible
     
    57455781                                    &parentId, location.c_str(),
    57465782                                    m->pParent->i_getId().raw(),
    5747                                     m->pVirtualBox->i_settingsFilePath().c_str());
     5783                                    pVirtualBox->i_settingsFilePath().c_str());
    57485784                            parentLock.release();
    57495785                            treeLock.release();
     
    57845820    }
    57855821
     5822    autoCaller.release();
    57865823    treeLock.acquire();
     5824    autoCaller.add();
     5825    if (FAILED(autoCaller.rc()))
     5826    {
     5827        m->queryInfoRunning = false;
     5828        return autoCaller.rc();
     5829    }
    57875830    alock.acquire();
    57885831
     
    58005843                        rc, vrc));
    58015844    }
    5802 
    5803     /* unblock anyone waiting for the queryInfo results */
    5804     qlock.release();
    5805     m->queryInfoRunning = false;
    58065845
    58075846    /* Set the proper state according to the result of the check */
     
    58105849    else
    58115850        m->preLockState = MediumState_Inaccessible;
     5851
     5852    /* unblock anyone waiting for the i_queryInfo results */
     5853    qlock.release();
     5854    m->queryInfoRunning = false;
    58125855
    58135856    pToken->Abandon();
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