VirtualBox

Changeset 80683 in vbox for trunk/src


Ignore:
Timestamp:
Sep 9, 2019 7:57:50 PM (5 years ago)
Author:
vboxsync
Message:

Storage:DevVirtioSCSI.cpp: suspend/resume/reset implemented and seems to be working. See bugref:9440, Comment #84

Location:
trunk/src/VBox/Devices
Files:
4 edited

Legend:

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

    r80658 r80683  
    515515    /** True if in the process of resetting */
    516516    bool                            fResetting;
    517 
    518     /** True if in the process of suspending  */
    519     bool                            fSuspending;
    520 
    521     /** True if in the process of powering off */
    522     bool                            fPoweringOff;
    523517
    524518    /** True if in the process of quiescing I/O */
     
    16731667}
    16741668
    1675 /** Let guest to use the queues */
    1676 static void enableQueues(PVIRTIOSCSI pThis)
    1677 {
    1678     for (int qIdx = 0; qIdx < VIRTIOSCSI_QUEUE_CNT; qIdx++)
    1679         virtioQueueEnable(pThis->hVirtio, qIdx, true);
    1680 }
    1681 
    1682 /** Tell guest to stop using the queues */
    1683 static void disableQueues(PVIRTIOSCSI pThis)
    1684 {
    1685     /** Tell guest to stop sending stuff */
    1686     for (int qIdx = 0; qIdx < VIRTIOSCSI_QUEUE_CNT; qIdx++)
    1687         virtioQueueEnable(pThis->hVirtio, qIdx, false);
    1688 }
    1689 
    1690 /**
    1691  * Handle callback from PDM's async notification mechanism.
    1692  * In this case, the callback is triggered when I/O activity is quiesced.
    1693  *
    1694  * @returns true if we've quiesced, false if we're still working.
    1695  * @param   pDevIns     The device instance.
     1669/**
     1670 * Is asynchronous handling of suspend or power off notification completed?
     1671 *
     1672 * This is called to check whether the device has quiesced.  Don't deadlock.
     1673 * Avoid blocking.  Do NOT wait for anything.
     1674 *
     1675 * @returns true if done, false if more work to be done.
     1676 *
     1677 * @param   pDevIns             The device instance.
     1678 * @remarks The caller will enter the device critical section.
     1679 * @thread  EMT(0)
    16961680 */
    16971681static DECLCALLBACK(bool) virtioScsiDeviceQuiesced(PPDMDEVINS pDevIns)
    16981682{
    1699     LogFunc(("\n"));
     1683    LogFunc(("Device I/O activity quiesced.\n"));
    17001684    PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
     1685
     1686    pThis->fQuiescing = false;
    17011687
    17021688    if (pThis->fResetting)
     
    17071693        virtioResetAll(pThis->hVirtio);
    17081694
    1709         /** Reset negotiable device-specific config parameters to VirtIO-specified default values */
     1695        /** Reset locally-owned negotiable device-specific config parameters
     1696         *  to VirtIO spec-mandated default values */
    17101697        pThis->virtioScsiConfig.uSenseSize = VIRTIOSCSI_SENSE_SIZE_DEFAULT;
    17111698        pThis->virtioScsiConfig.uCdbSize   = VIRTIOSCSI_CDB_SIZE_DEFAULT;
    1712 
    1713         pThis->fQuiescing = false;
    1714 
    1715         /** fQuiescing and fReset flags get cleared during [re-]initialization */
    1716     }
    1717     else if (pThis->fSuspending || pThis->fPoweringOff)
    1718     {
    1719         /* Lower-level driver (DrvVD) has already suspended any I/O on wait queue */
    1720     }
    1721     return false;
     1699    }
     1700    return true;
    17221701}
    17231702
     
    17261705    PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
    17271706
    1728     pThis->fQuiescing = true;  /* inhibit worker thread's de-queing */
    1729     disableQueues(pThis);      /* inhibit guest use of virtq's */
     1707    /** Prevent worker threads from removing/processing elements from virtq's */
     1708    pThis->fQuiescing = true;
    17301709
    17311710    PDMDevHlpSetAsyncNotification(pDevIns, virtioScsiDeviceQuiesced);
     
    17631742
    17641743/**
    1765  * @interface_method_impl{PDMDEVREG,pfnResume}
    1766  */
    1767 static DECLCALLBACK(void) virtioScsiResume(PPDMDEVINS pDevIns)
     1744 * @copydoc FNPDMDEVRESET
     1745 */
     1746static DECLCALLBACK(void) virtioScsiReset(PPDMDEVINS pDevIns)
    17681747{
    17691748    LogFunc(("\n"));
    17701749    PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
    1771     pThis->fSuspending = pThis->fQuiescing = false;
    1772     enableQueues(pThis);
     1750    pThis->fResetting = true;
     1751    virtioScsiQuiesceDevice(pDevIns);
     1752}
     1753
     1754/**
     1755 * @interface_method_impl{PDMDEVREG,pfnResume}
     1756 */
     1757static DECLCALLBACK(void) virtioScsiResume(PPDMDEVINS pDevIns)
     1758{
     1759    LogFunc(("\n"));
     1760
     1761    PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
     1762
     1763    pThis->fQuiescing = false;
     1764
     1765    /** Wake worker threads flagged to skip pulling queue entries during quiesce
     1766     *  to ensure they re-check their queues. Active request queues may already
     1767     *  be awake due to new reqs coming in.
     1768     */
     1769     for (uint16_t qIdx = 0; qIdx < VIRTIOSCSI_REQ_QUEUE_CNT; qIdx++)
     1770    {
     1771        PWORKER pWorker = &pThis->aWorker[qIdx];
     1772
     1773        if (ASMAtomicReadBool(&pWorker->fSleeping))
     1774        {
     1775            Log6Func(("waking %s worker.\n", QUEUENAME(qIdx)));
     1776            int rc = SUPSemEventSignal(pThis->pSupDrvSession, pWorker->hEvtProcess);
     1777            AssertRC(rc);
     1778        }
     1779    }
     1780
     1781    /** Ensure guest is working the queues too. */
     1782    virtioPropagateResumeNotification(pThis->hVirtio);
    17731783}
    17741784
     
    17761786 * @interface_method_impl{PDMDEVREG,pfnSuspend}
    17771787 */
    1778 static DECLCALLBACK(void) virtioScsiSuspend(PPDMDEVINS pDevIns)
     1788static DECLCALLBACK(void) virtioScsiSuspendOrPoweroff(PPDMDEVINS pDevIns)
    17791789{
    17801790    LogFunc(("\n"));
    17811791
     1792    virtioScsiQuiesceDevice(pDevIns);
     1793
    17821794    PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
    17831795
    1784     pThis->fSuspending = true;
    1785 
    1786     virtioScsiQuiesceDevice(pDevIns);
    1787 
     1796    /** VM is halted, thus no new I/O being dumped into queues by the guest.
     1797     *  Workers have been flagged to stop pulling stuff already queued-up by the guest.
     1798     *  Now tell lower-level to to suspend reqs (for example, DrvVD suspends all reqs
     1799     *  on its wait queue, and we will get a callback as the state changes to
     1800     *  suspended (and later, resumed) for each).
     1801     */
    17881802    for (uint32_t i = 0; i < pThis->cTargets; i++)
    17891803    {
     
    17931807                pTarget->pDrvMediaEx->pfnNotifySuspend(pTarget->pDrvMediaEx);
    17941808    }
    1795 }
    1796 
    1797  /**
    1798   * Common worker for virtioScsiSuspend and virtioScsiPowerOff.
    1799   */
    1800 static void virtioScsiPowerOn(PPDMDEVINS pDevIns)
    1801 {
    1802     LogFunc(("\n"));
    1803     PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
    1804     pThis->fPoweringOff = pThis->fQuiescing = false;
    1805     enableQueues(pThis);
    1806 }
    1807 
    1808  /**
    1809   * Common worker for virtioScsiSuspend and virtioScsiPowerOff.
    1810   */
    1811 static void virtioScsiPowerOff(PPDMDEVINS pDevIns)
    1812 {
    1813     LogFunc(("\n"));
    1814 
    1815     PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
    1816 
    1817     pThis->fPoweringOff = true;
    1818     virtioScsiQuiesceDevice(pDevIns);
    1819 
    1820     for (uint32_t i = 0; i < pThis->cTargets; i++)
    1821     {
    1822         PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[i];
    1823         if (pTarget->pDrvBase)
    1824             if (pTarget->pDrvMediaEx)
    1825                 pTarget->pDrvMediaEx->pfnNotifySuspend(pTarget->pDrvMediaEx);
    1826     }
    1827 }
    1828 
    1829 /**
    1830  * @copydoc FNPDMDEVRESET
    1831  */
    1832 static DECLCALLBACK(void) virtioScsiReset(PPDMDEVINS pDevIns)
    1833 {
    1834     LogFunc(("\n"));
    1835 
    1836     PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
    1837     pThis->fResetting = true;
    1838 
    1839     virtioScsiQuiesceDevice(pDevIns);
    18401809}
    18411810
     
    22792248            if (RT_FAILURE(rc))
    22802249                return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
    2281                                      N_("LsiLogic: Failed to create SUP event semaphore"));
     2250                                     N_("DevVirtioSCSI: Failed to create SUP event semaphore"));
    22822251         }
    22832252    }
     
    24212390    /* .pfnRelocate = */            virtioScsiRelocate,
    24222391    /* .pfnMemSetup = */            NULL,
    2423     /* .pfnPowerOn = */             virtioScsiPowerOn,
     2392    /* .pfnPowerOn = */             NULL,
    24242393    /* .pfnReset = */               virtioScsiReset,
    2425     /* .pfnSuspend = */             virtioScsiSuspend,
     2394    /* .pfnSuspend = */             virtioScsiSuspendOrPoweroff,
    24262395    /* .pfnResume = */              virtioScsiResume,
    24272396    /* .pfnAttach = */              virtioScsiAttach,
     
    24292398    /* .pfnQueryInterface = */      NULL,
    24302399    /* .pfnInitComplete = */        NULL,
    2431     /* .pfnPowerOff = */            virtioScsiPowerOff,
     2400    /* .pfnPowerOff = */            virtioScsiSuspendOrPoweroff,
    24322401    /* .pfnSoftReset = */           NULL,
    24332402    /* .pfnReserved0 = */           NULL,
  • trunk/src/VBox/Devices/VirtIO/Virtio_1_0.cpp

    r80657 r80683  
    8888    return VINF_SUCCESS;
    8989}
     90
    9091
    9192/**
     
    294295
    295296    virtioWriteUsedRingIdx(pVirtio, qIdx, pVirtqProxy->uUsedIdx);
    296     virtioNotifyGuestDriver(pVirtio, qIdx);
     297    virtioNotifyGuestDriver(pVirtio, qIdx, false);
    297298
    298299    return VINF_SUCCESS;
     
    309310    Log6Func(("%s\n", pVirtqProxy->szVirtqName));
    310311
    311 
    312312    /** Inform client */
    313313    pVirtio->virtioCallbacks.pfnVirtioQueueNotified((VIRTIOHANDLE)pVirtio, pVirtio->pClientContext, qIdx);
     314}
     315
     316/**
     317 * See API comments in header file for description
     318 */
     319void virtioPropagateResumeNotification(VIRTIOHANDLE hVirtio)
     320{
     321    virtioNotifyGuestDriver((PVIRTIOSTATE)hVirtio, (uint16_t)NULL /* qIdx */, true /* fForce */);
    314322}
    315323
     
    319327 * and depending on negotiated and realtime constraints flagged by the guest driver.
    320328 * See VirtIO 1.0 specification (section 2.4.7).
    321  */
    322 static void virtioNotifyGuestDriver(PVIRTIOSTATE pVirtio, uint16_t qIdx)
     329 *
     330 * @param pVirtio       - Instance state
     331 * @param qIdx          - Queue to check for guest interrupt handling preference
     332 * @param fForce        - Overrides qIdx, forcing notification, regardless of driver's
     333 *                        notification preferences. This is a safeguard to prevent
     334 *                        stalls upon resuming the VM. VirtIO 1.0 specification Section 4.1.5.5
     335 *                        indicates spurious interrupts are harmless to guest driver's state,
     336 *                        as they only cause the guest driver to scan queues for work to do.
     337 */
     338static void virtioNotifyGuestDriver(PVIRTIOSTATE pVirtio, uint16_t qIdx, bool fForce)
    323339{
    324340    PVIRTQ_PROXY_T pVirtqProxy = &pVirtio->virtqProxy[qIdx];
    325341
    326     AssertMsgReturnVoid(DRIVER_OK(pVirtio) && pVirtio->uQueueEnable[qIdx],
    327                     ("Guest driver not in ready state.\n"));
    328 
    329     if (pVirtio->uQueueMsixVector[qIdx] == VIRTIO_MSI_NO_VECTOR)
     342    AssertMsgReturnVoid(DRIVER_OK(pVirtio), ("Guest driver not in ready state.\n"));
     343
     344    if (pVirtio->uMsixConfig == VIRTIO_MSI_NO_VECTOR)
    330345    {
    331346        if (pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
     
    333348            if (pVirtqProxy->fEventThresholdReached)
    334349            {
    335                 virtioRaiseInterrupt(pVirtio, VIRTIO_ISR_VIRTQ_INTERRUPT);
     350                virtioRaiseInterrupt(pVirtio, VIRTIO_ISR_VIRTQ_INTERRUPT, fForce);
    336351                pVirtqProxy->fEventThresholdReached = false;
    337352                return;
     
    342357        {
    343358            /** If guest driver hasn't suppressed interrupts, interrupt  */
    344             if (!(virtioReadUsedFlags(pVirtio, qIdx) & VIRTQ_AVAIL_F_NO_INTERRUPT))
     359            if (fForce || !(virtioReadUsedFlags(pVirtio, qIdx) & VIRTQ_AVAIL_F_NO_INTERRUPT))
    345360            {
    346                 virtioRaiseInterrupt(pVirtio, VIRTIO_ISR_VIRTQ_INTERRUPT);
     361                virtioRaiseInterrupt(pVirtio, VIRTIO_ISR_VIRTQ_INTERRUPT, fForce);
    347362                return;
    348363            }
    349364            Log6Func(("...skipping interrupt. Guest flagged VIRTQ_AVAIL_F_NO_INTERRUPT for queue\n"));
    350 
    351365        }
    352366    }
     
    392406 * @param   uCause          Interrupt cause bit mask to set in PCI ISR port.
    393407 */
    394 static int virtioRaiseInterrupt(PVIRTIOSTATE pVirtio, uint8_t uCause)
    395 {
     408static int virtioRaiseInterrupt(PVIRTIOSTATE pVirtio, uint8_t uCause, bool fForce)
     409{
     410
     411   if (fForce)
     412       Log6Func(("reason: resumed after suspend\n"));
     413   else
    396414   if (uCause == VIRTIO_ISR_VIRTQ_INTERRUPT)
    397415       Log6Func(("reason: buffer added to 'used' ring.\n"));
     
    480498    {
    481499        pVirtio->fGenUpdatePending = true;
    482         virtioRaiseInterrupt(pVirtio, VIRTIO_ISR_DEVICE_CONFIG);
     500        virtioRaiseInterrupt(pVirtio, VIRTIO_ISR_DEVICE_CONFIG, false /* fForce */);
    483501    }
    484502}
  • trunk/src/VBox/Devices/VirtIO/Virtio_1_0.h

    r80657 r80683  
    289289void virtioResetAll(VIRTIOHANDLE hVirtio);
    290290
     291/**
     292 * This sends notification ('kicks') guest driver to check queues for any new
     293 * elements in the used queue to process.  It should be called after resuming
     294 * in case anything was added to the queues during suspend/quiescing and a
     295 * notification was missed, to prevent the guest from stalling after suspend.
     296 */
     297void virtioPropagateResumeNotification(VIRTIOHANDLE hVirtio);
     298
    291299
    292300
  • trunk/src/VBox/Devices/VirtIO/Virtio_1_0_impl.h

    r80647 r80683  
    535535
    536536static void virtioResetQueue        (PVIRTIOSTATE pVirtio, uint16_t qIdx);
    537 static void virtioNotifyGuestDriver (PVIRTIOSTATE pVirtio, uint16_t qIdx);
    538 static int  virtioRaiseInterrupt    (PVIRTIOSTATE pVirtio, uint8_t uCause);
     537static void virtioNotifyGuestDriver (PVIRTIOSTATE pVirtio, uint16_t qIdx, bool fForce);
     538static int  virtioRaiseInterrupt    (PVIRTIOSTATE pVirtio, uint8_t uCause, bool fForce);
    539539static void virtioLowerInterrupt    (PVIRTIOSTATE pVirtio);
    540540static void virtioQueueNotified     (PVIRTIOSTATE pVirtio, uint16_t qidx, uint16_t uDescIdx);
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