VirtualBox

Changeset 59875 in vbox


Ignore:
Timestamp:
Feb 29, 2016 3:53:00 PM (9 years ago)
Author:
vboxsync
Message:

VUSB: Move the thread for periodic frame procession down to the roothub. There we don't need to replicate the code to EHCI later and it has much better overview of the amount of active URBs to schedule when to process new frames

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/VBox/vusb.h

    r59723 r59875  
    2929#include <VBox/cdefs.h>
    3030#include <VBox/types.h>
     31#include <iprt/assert.h>
    3132
    3233struct PDMLED;
     
    645646    DECLR3CALLBACKMEMBER(bool, pfnXferError,(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb));
    646647
     648    /**
     649     * Processes a new frame if periodic frame processing is enabled.
     650     *
     651     * @returns Flag whether there was activity which influences the frame rate.
     652     * @param   pInterface      Pointer to this structure.
     653     * @param   u32FrameNo      The frame number.
     654     */
     655    DECLR3CALLBACKMEMBER(bool, pfnStartFrame, (PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameNo));
     656
     657    /**
     658     * Informs the callee about a change in the frame rate due to too many idle cycles or
     659     * when seeing activity after some idle time.
     660     *
     661     * @returns nothing.
     662     * @param   pInterface      Pointer to this structure.
     663     * @param   u32Framerate    The new frame rate.
     664     */
     665    DECLR3CALLBACKMEMBER(void, pfnFrameRateChanged, (PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameRate));
     666
    647667    /** Alignment dummy. */
    648668    RTR3PTR Alignment;
     
    650670} VUSBIROOTHUBPORT;
    651671/** VUSBIROOTHUBPORT interface ID. */
    652 #define VUSBIROOTHUBPORT_IID                    "79a31188-043d-432c-82ac-9485c9ab9a49"
     672#define VUSBIROOTHUBPORT_IID                    "6571aece-6c33-4714-a8ac-9508a3b8b429"
    653673
    654674/** Pointer to a VUSB RootHub connector interface. */
     
    789809    DECLR3CALLBACKMEMBER(int, pfnDetachDevice,(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBIDEVICE pDevice));
    790810
    791     /** Alignment dummy. */
    792     RTR3PTR Alignment;
     811    /**
     812     * Sets periodic frame processing.
     813     *
     814     * @returns VBox status code.
     815     * @param   pInterface  Pointer to this struct.
     816     * @param   uFrameRate  The target frame rate in Hertz, 0 disables periodic frame processing.
     817     *                      The real frame rate might be lower if there is no activity for a certain period or
     818     *                      higher if there is a need for catching up with where the guest expects the device to be.
     819     */
     820    DECLR3CALLBACKMEMBER(int, pfnSetPeriodicFrameProcessing, (PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate));
     821
     822    /**
     823     * Returns the current frame rate for the periodic frame processing.
     824     *
     825     * @returns Frame rate for periodic frame processing.
     826     * @retval  0 if disabled.
     827     * @param   pInterface  Pointer to this struct.
     828     */
     829    DECLR3CALLBACKMEMBER(uint32_t, pfnGetPeriodicFrameRate, (PVUSBIROOTHUBCONNECTOR pInterface));
    793830
    794831} VUSBIROOTHUBCONNECTOR;
     832AssertCompileSizeAlignment(VUSBIROOTHUBCONNECTOR, 8);
    795833/** VUSBIROOTHUBCONNECTOR interface ID. */
    796 #define VUSBIROOTHUBCONNECTOR_IID               "a593cc64-a821-4e57-af2d-f86b2a052ea4"
     834#define VUSBIROOTHUBCONNECTOR_IID               "662d7822-b9c6-43b5-88b6-5d59f0106e46"
    797835
    798836
     
    845883{
    846884    return pInterface->pfnDetachDevice(pInterface, pDevice);
     885}
     886
     887/** @copydoc VUSBIROOTHUBCONNECTOR::pfnSetPeriodicFrameProcessing */
     888DECLINLINE(int) VUSBIRhSetPeriodicFrameProcessing(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate)
     889{
     890    return pInterface->pfnSetPeriodicFrameProcessing(pInterface, uFrameRate);
     891}
     892
     893/** @copydoc VUSBIROOTHUBCONNECTOR::pfnSetPeriodicFrameProcessing */
     894DECLINLINE(uint32_t) VUSBIRhGetPeriodicFrameRate(PVUSBIROOTHUBCONNECTOR pInterface)
     895{
     896    return pInterface->pfnGetPeriodicFrameRate(pInterface);
    847897}
    848898#endif /* IN_RING3 */
  • trunk/src/VBox/Devices/USB/DevOHCI.cpp

    r59704 r59875  
    223223    R3PTRTYPE(POHCI)                    pOhci;
    224224} OHCIROOTHUB;
    225 #if HC_ARCH_BITS == 64
    226 AssertCompile(sizeof(OHCIROOTHUB) == 392); /* saved state */
    227 #endif
    228225/** Pointer to the OHCI root hub. */
    229226typedef OHCIROOTHUB *POHCIROOTHUB;
     
    387384    /** VM timer frequency used for frame timer calculations. */
    388385    uint64_t            u64TimerHz;
    389     /** Number of USB work cycles with no transfers. */
    390     uint32_t            cIdleCycles;
    391     /** Current frame timer rate (default 1000). */
    392     uint32_t            uFrameRate;
    393386    /** Idle detection flag; must be cleared at start of frame */
    394387    bool                fIdle;
     
    403396    /** Critical section synchronising interrupt handling. */
    404397    PDMCRITSECT         CsIrq;
    405 
    406     /** The framer thread. */
    407     R3PTRTYPE(PPDMTHREAD)      hThreadFrame;
    408     /** Event semaphore to interact with the framer thread. */
    409     R3PTRTYPE(RTSEMEVENTMULTI) hSemEventFrame;
    410     /** Event semaphore to release the thread waiting for the framer thread to stop. */
    411     R3PTRTYPE(RTSEMEVENTMULTI) hSemEventFrameStopped;
    412     /** Flag whether the framer thread should processing frames. */
    413     volatile bool              fBusStarted;
    414     /** Alignment. */
    415     uint32_t                   Alignment5;
    416     /** How long to wait until the next frame. */
    417     uint64_t                   nsWait;
    418398    /** Critical section to synchronize the framer and URB completion handler. */
    419399    RTCRITSECT                 CritSect;
     
    21932173
    21942174/**
    2195  * Calculate frame timer variables given a frame rate (1,000 Hz is the full speed).
    2196  */
    2197 static void ohciCalcTimerIntervals(POHCI pThis, uint32_t u32FrameRate)
    2198 {
    2199     Assert(u32FrameRate <= OHCI_DEFAULT_TIMER_FREQ);
    2200 
    2201     pThis->cTicksPerFrame = pThis->u64TimerHz / u32FrameRate;
    2202     if (!pThis->cTicksPerFrame)
    2203         pThis->cTicksPerFrame = 1;
    2204     pThis->cTicksPerUsbTick   = pThis->u64TimerHz >= VUSB_BUS_HZ ? pThis->u64TimerHz / VUSB_BUS_HZ : 1;
    2205     pThis->nsWait             = RT_NS_1SEC / u32FrameRate;
    2206     pThis->uFrameRate         = u32FrameRate;
    2207 }
    2208 
    2209 
    2210 /**
    2211  * Calculates the new frame rate based on the idle detection and number of idle
    2212  * cycles.
    2213  *
    2214  * @returns nothing.
    2215  * @param   pThis    The OHCI device data.
    2216  */
    2217 static bool ohciFramerateCalcNew(POHCI pThis)
    2218 {
    2219     uint32_t uNewFrameRate = pThis->uFrameRate;
    2220 
    2221     /*
    2222      * Adjust the frame timer interval based on idle detection.
    2223      */
    2224     if (pThis->fIdle)
    2225     {
    2226         pThis->cIdleCycles++;
    2227         /* Set the new frame rate based on how long we've been idle. Tunable. */
    2228         switch (pThis->cIdleCycles)
    2229         {
    2230             case 4: uNewFrameRate = 500;    break;  /*  2ms interval */
    2231             case 16:uNewFrameRate = 125;    break;  /*  8ms interval */
    2232             case 24:uNewFrameRate = 50;     break;  /* 20ms interval */
    2233             default:    break;
    2234         }
    2235         /* Avoid overflow. */
    2236         if (pThis->cIdleCycles > 60000)
    2237             pThis->cIdleCycles = 20000;
    2238     }
    2239     else
    2240     {
    2241         if (pThis->cIdleCycles)
    2242         {
    2243             pThis->cIdleCycles = 0;
    2244             uNewFrameRate      = OHCI_DEFAULT_TIMER_FREQ;
    2245         }
    2246     }
    2247     if (uNewFrameRate != pThis->uFrameRate)
    2248     {
    2249         LogFlow(("Frame rate changed from %u to %u\n", pThis->uFrameRate, uNewFrameRate));
    2250         ohciCalcTimerIntervals(pThis, uNewFrameRate);
    2251         return true;
    2252     }
    2253     return false;
    2254 }
    2255 
    2256 
    2257 /**
    22582175 * Returns the OHCI_CC_* corresponding to the VUSB status code.
    22592176 *
     
    26402557    /* finally write back the endpoint descriptor. */
    26412558    ohciWriteEd(pThis, pUrb->pHci->EdAddr, &Ed);
    2642 
    2643     /* Calculate new frame rate and wakeup the framer thread if the rate was chnaged. */
    2644     if (ohciFramerateCalcNew(pThis))
    2645         RTSemEventMultiSignal(pThis->hSemEventFrame);
    26462559
    26472560    RTCritSectLeave(&pThis->CritSect);
     
    38743787    }
    38753788#endif
    3876 
    3877     /*
    3878      * Adjust the frame timer interval based on idle detection.
    3879      */
    3880     ohciFramerateCalcNew(pThis);
    38813789}
    38823790
     
    38913799}
    38923800
    3893 static DECLCALLBACK(int) ohciR3ThreadFrame(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
    3894 {
    3895     POHCI pThis = (POHCI)pThread->pvUser;
    3896     uint64_t tsBeginServicing = 0;
    3897     uint64_t cFramesProcessed = 0;
    3898 
    3899     if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
    3900         return VINF_SUCCESS;
    3901 
    3902     tsBeginServicing = RTTimeNanoTS();
    3903     cFramesProcessed = 0;
    3904 
    3905     while (pThread->enmState == PDMTHREADSTATE_RUNNING)
    3906     {
    3907         while (   !ASMAtomicReadBool(&pThis->fBusStarted)
    3908                && pThread->enmState == PDMTHREADSTATE_RUNNING)
    3909         {
    3910             /* Signal the waiter that we are stopped now. */
    3911             int rc = RTSemEventMultiSignal(pThis->hSemEventFrameStopped);
    3912             AssertRC(rc);
    3913             rc = RTSemEventMultiReset(pThis->hSemEventFrame);
    3914             AssertRC(rc);
    3915 
    3916             /*
    3917              * We have to check that the Bus was not started and the thread state
    3918              * did not change or otherwise we risk hanging here indefinitely
    3919              * if the signaller set the event semaphore before we reset it.
    3920              */
    3921             if (ASMAtomicReadBool(&pThis->fBusStarted) || pThread->enmState != PDMTHREADSTATE_RUNNING)
    3922                 break;
    3923 
    3924             rc = RTSemEventMultiWait(pThis->hSemEventFrame, RT_INDEFINITE_WAIT);
    3925             AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc);
    3926             tsBeginServicing = RTTimeNanoTS();
    3927             cFramesProcessed = 0;
    3928         }
    3929 
    3930         if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
    3931             break;
    3932 
    3933         RTCritSectEnter(&pThis->CritSect);
    3934 
    3935         /* Reset idle detection flag */
    3936         pThis->fIdle = true;
    3937 
    3938         /*
    3939          * Process new frames until we reached the required amount of
    3940          * frames for this service period. We might need to catch up
    3941          * here and process multiple frames at once due to scheduling
    3942          * preempting us. This is required because isochronous transfers
    3943          * have a tight timing requirement.
    3944          */
    3945         uint64_t tsNow = RTTimeNanoTS();
    3946         uint64_t nsWait = 0;
    3947         while (tsBeginServicing + (cFramesProcessed * RT_NS_1MS) < tsNow)
    3948         {
    3949             uint64_t tsNanoStart = RTTimeNanoTS();
    3950             LogFlowFunc(("Starting new frame at ts %llu\n", tsNanoStart));
    3951 
    3952             /* Frame boundary, so do EOF stuff here. */
    3953             bump_frame_number(pThis);
    3954             if ( (pThis->dqic != 0x7) && (pThis->dqic != 0))
    3955                 pThis->dqic--;
    3956 
    3957             /* Clean up any URBs that have been removed. */
    3958             ohciCancelOrphanedURBs(pThis);
    3959 
    3960             /* Start the next frame. */
    3961             ohciStartOfFrame(pThis);
    3962             cFramesProcessed++;
    3963 
    3964             tsNow = RTTimeNanoTS();
    3965             uint64_t tsFrameNext = tsNanoStart + pThis->nsWait;
    3966 
    3967             if (tsFrameNext > tsNow)
    3968             {
    3969                 nsWait = tsFrameNext - tsNow;
    3970                 LogFlowFunc(("Current frame took %llu nano seconds to finish, we can wait %llu ns for the next frame\n", tsNow - tsNanoStart, nsWait));
    3971                 break;
    3972             }
    3973             else if (tsBeginServicing + (cFramesProcessed + 100) * RT_NS_1MS < tsNow)
    3974             {
    3975                 /* If we lag to far behind stop trying to catch up. */
    3976                 LogRelMax(10, ("OHCI#%u: Lagging too far behind, not trying to catch up anymore. Expect glitches with USB devices\n",
    3977                                pThis->pDevInsR3->iInstance));
    3978                 tsBeginServicing = tsNow;
    3979                 cFramesProcessed = 0;
    3980             }
    3981         }
    3982 
    3983         RTCritSectLeave(&pThis->CritSect);
    3984 
    3985         /* Wait for the next round. */
    3986         if (nsWait >= 500 * RT_NS_1US)
    3987         {
    3988             LogFlowFunc(("Going to sleep for at least %llu ns\n", nsWait));
    3989             int rc = RTSemEventMultiWaitEx(pThis->hSemEventFrame, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE,
    3990                                            nsWait);
    3991             AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc));
    3992             RTSemEventMultiReset(pThis->hSemEventFrame);
    3993         }
    3994     }
    3995 
    3996     return VINF_SUCCESS;
    3997 }
    3998 
    3999 /**
    4000  * Unblock the framer thread so it can respond to a state change.
    4001  *
    4002  * @returns VBox status code.
    4003  * @param   pDevIns     The device instance.
    4004  * @param   pThread     The send thread.
    4005  */
    4006 static DECLCALLBACK(int) ohciR3ThreadFrameWakeup(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
    4007 {
    4008     POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
    4009     return RTSemEventMultiSignal(pThis->hSemEventFrame);
     3801/**
     3802 * Callback for periodic frame processing.
     3803 */
     3804static DECLCALLBACK(bool) ohciR3StartFrame(PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameNo)
     3805{
     3806    POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
     3807
     3808    RTCritSectEnter(&pThis->CritSect);
     3809
     3810    /* Reset idle detection flag */
     3811    pThis->fIdle = true;
     3812
     3813    /* Frame boundary, so do EOF stuff here. */
     3814    bump_frame_number(pThis);
     3815    if ( (pThis->dqic != 0x7) && (pThis->dqic != 0))
     3816        pThis->dqic--;
     3817
     3818    /* Clean up any URBs that have been removed. */
     3819    ohciCancelOrphanedURBs(pThis);
     3820
     3821    /* Start the next frame. */
     3822    ohciStartOfFrame(pThis);
     3823
     3824    RTCritSectLeave(&pThis->CritSect);
     3825
     3826    return pThis->fIdle;
     3827}
     3828
     3829/**
     3830 * @copydoc VUSBIROOTHUBPORT::pfnFramerateChanged.
     3831 */
     3832static DECLCALLBACK(void) ohciR3FrameRateChanged(PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameRate)
     3833{
     3834    POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
     3835
     3836    Assert(u32FrameRate <= OHCI_DEFAULT_TIMER_FREQ);
     3837
     3838    pThis->cTicksPerFrame = pThis->u64TimerHz / u32FrameRate;
     3839    if (!pThis->cTicksPerFrame)
     3840        pThis->cTicksPerFrame = 1;
     3841    pThis->cTicksPerUsbTick = pThis->u64TimerHz >= VUSB_BUS_HZ ? pThis->u64TimerHz / VUSB_BUS_HZ : 1;
    40103842}
    40113843
     
    40293861
    40303862    pThis->SofTime = PDMDevHlpTMTimeVirtGet(pThis->CTX_SUFF(pDevIns));
    4031     bool fBusActive = ASMAtomicXchgBool(&pThis->fBusStarted, true);
    4032     if (!fBusActive)
    4033         RTSemEventMultiSignal(pThis->hSemEventFrame);
     3863    int rc = pThis->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThis->RootHub.pIRhConn, OHCI_DEFAULT_TIMER_FREQ);
     3864    AssertRC(rc);
    40343865}
    40353866
     
    40393870static void ohciBusStop(POHCI pThis)
    40403871{
    4041     bool fBusActive = ASMAtomicXchgBool(&pThis->fBusStarted, false);
    4042     if (fBusActive)
    4043     {
    4044         int rc = RTSemEventMultiReset(pThis->hSemEventFrameStopped);
    4045         AssertRC(rc);
    4046 
    4047         /* Signal the frame thread to stop. */
    4048         RTSemEventMultiSignal(pThis->hSemEventFrame);
    4049 
    4050         /* Wait for signal from the thrad that it stopped. */
    4051         rc = RTSemEventMultiWait(pThis->hSemEventFrameStopped, RT_INDEFINITE_WAIT);
    4052         AssertRC(rc);
    4053     }
     3872    int rc = pThis->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThis->RootHub.pIRhConn, 0);
     3873    AssertRC(rc);
    40543874    VUSBIDevPowerOff(pThis->RootHub.pIDev);
    40553875}
     
    52685088     */
    52695089    /** @todo: Do it properly for 4.4 by changing the saved state. */
    5270     if (pThis->fBusStarted)
     5090    if (VUSBIRhGetPeriodicFrameRate(pRh->pIRhConn) != 0)
    52715091    {
    52725092        /* Calculate a new timer expiration so this saved state works with older releases. */
     
    56805500        AssertRC(rc);
    56815501
    5682         LogFlowFunc(("Bus was active, restart frame thread\n"));
    5683         ASMAtomicXchgBool(&pThis->fBusStarted, true);
    5684         RTSemEventMultiSignal(pThis->hSemEventFrame);
     5502        LogFlowFunc(("Bus was active, enable periodic frame processing\n"));
     5503        rc = pThis->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThis->RootHub.pIRhConn, OHCI_DEFAULT_TIMER_FREQ);
     5504        AssertRC(rc);
    56855505    }
    56865506}
     
    57725592    PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
    57735593
    5774     /*
    5775      * Destroy event sempahores.
    5776      */
    5777     if (pThis->hSemEventFrame)
    5778         RTSemEventMultiDestroy(pThis->hSemEventFrame);
    5779     if (pThis->hSemEventFrameStopped)
    5780         RTSemEventMultiDestroy(pThis->hSemEventFrameStopped);
    57815594    if (RTCritSectIsInitialized(&pThis->CritSect))
    57825595        RTCritSectDelete(&pThis->CritSect);
     
    58275640    pThis->RootHub.IRhPort.pfnXferCompletion     = ohciRhXferCompletion;
    58285641    pThis->RootHub.IRhPort.pfnXferError          = ohciRhXferError;
     5642    pThis->RootHub.IRhPort.pfnStartFrame         = ohciR3StartFrame;
     5643    pThis->RootHub.IRhPort.pfnFrameRateChanged   = ohciR3FrameRateChanged;
    58295644
    58305645    /* USB LED */
     
    59425757     */
    59435758    pThis->u64TimerHz = TMTimerGetFreq(pThis->CTX_SUFF(pEndOfFrameTimer));
    5944     ohciCalcTimerIntervals(pThis, OHCI_DEFAULT_TIMER_FREQ);
    5945     Log(("ohci: cTicksPerFrame=%RU64 cTicksPerUsbTick=%RU64\n",
    5946          pThis->cTicksPerFrame, pThis->cTicksPerUsbTick));
    5947 
    5948     pThis->fBusStarted = false;
    59495759
    59505760    rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CsIrq, RT_SRC_POS, "OHCI#%uIrq", iInstance);
     
    59535763                                   N_("OHCI: Failed to create critical section"));
    59545764
    5955     rc = RTSemEventMultiCreate(&pThis->hSemEventFrame);
    5956     AssertRCReturn(rc, rc);
    5957 
    5958     rc = RTSemEventMultiCreate(&pThis->hSemEventFrameStopped);
    5959     AssertRCReturn(rc, rc);
    5960 
    59615765    rc = RTCritSectInit(&pThis->CritSect);
    59625766    if (RT_FAILURE(rc))
    59635767        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
    59645768                                   N_("OHCI: Failed to create critical section"));
    5965 
    5966     rc = PDMDevHlpThreadCreate(pDevIns, &pThis->hThreadFrame, pThis, ohciR3ThreadFrame,
    5967                                ohciR3ThreadFrameWakeup, 0, RTTHREADTYPE_TIMER, "OhciFramer");
    5968     if (RT_FAILURE(rc))
    5969         return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
    5970                                    N_("OHCI: Failed to create worker thread"));
    59715769
    59725770    /*
  • trunk/src/VBox/Devices/USB/DrvVUSBRootHub.cpp

    r59775 r59875  
    415415
    416416
     417/**
     418 * Calculate frame timer variables given a frame rate.
     419 */
     420static void vusbRhR3CalcTimerIntervals(PVUSBROOTHUB pThis, uint32_t u32FrameRate)
     421{
     422    pThis->nsWait     = RT_NS_1SEC / u32FrameRate;
     423    pThis->uFrameRate = u32FrameRate;
     424    /* Inform the HCD about the new frame rate. */
     425    pThis->pIRhPort->pfnFrameRateChanged(pThis->pIRhPort, u32FrameRate);
     426}
     427
     428
     429/**
     430 * Calculates the new frame rate based on the idle detection and number of idle
     431 * cycles.
     432 *
     433 * @returns nothing.
     434 * @param   pThis    The roothub instance data.
     435 * @param   fIdle    Flag whether the last frame didn't produce any activity.
     436 */
     437static void vusbRhR3FrameRateCalcNew(PVUSBROOTHUB pThis, bool fIdle)
     438{
     439    uint32_t uNewFrameRate = pThis->uFrameRate;
     440
     441    /*
     442     * Adjust the frame timer interval based on idle detection.
     443     */
     444    if (fIdle)
     445    {
     446        pThis->cIdleCycles++;
     447        /* Set the new frame rate based on how long we've been idle. Tunable. */
     448        switch (pThis->cIdleCycles)
     449        {
     450            case 4: uNewFrameRate = 500;    break;  /*  2ms interval */
     451            case 16:uNewFrameRate = 125;    break;  /*  8ms interval */
     452            case 24:uNewFrameRate = 50;     break;  /* 20ms interval */
     453            default:    break;
     454        }
     455        /* Avoid overflow. */
     456        if (pThis->cIdleCycles > 60000)
     457            pThis->cIdleCycles = 20000;
     458    }
     459    else
     460    {
     461        if (pThis->cIdleCycles)
     462        {
     463            pThis->cIdleCycles = 0;
     464            uNewFrameRate      = pThis->uFrameRateDefault;
     465        }
     466    }
     467
     468    if (uNewFrameRate != pThis->uFrameRate)
     469    {
     470        LogFlow(("Frame rate changed from %u to %u\n", pThis->uFrameRate, uNewFrameRate));
     471        vusbRhR3CalcTimerIntervals(pThis, uNewFrameRate);
     472    }
     473}
     474
     475
     476/**
     477 * The core frame processing routine keeping track of the elapsed time and calling into
     478 * the device emulation above us to do the work.
     479 *
     480 * @returns Relative timespan when to process the next frame.
     481 * @param   pThis     The roothub instance data.
     482 * @param   fCallback Flag whether this method is called from the URB completion callback or
     483 *                    from the worker thread (only used for statistics).
     484 */
     485DECLHIDDEN(uint64_t) vusbRhR3ProcessFrame(PVUSBROOTHUB pThis, bool fCallback)
     486{
     487    uint64_t tsNext = 0;
     488    uint64_t tsNanoStart = RTTimeNanoTS();
     489
     490    /* Don't do anything if we are not supposed to process anything (EHCI and XHCI). */
     491    if (!pThis->uFrameRateDefault)
     492        return 0;
     493
     494    if (ASMAtomicXchgBool(&pThis->fFrameProcessing, true))
     495        return pThis->nsWait;
     496
     497    if (   tsNanoStart > pThis->tsFrameProcessed
     498        && tsNanoStart - pThis->tsFrameProcessed >= 750 * RT_NS_1US)
     499    {
     500        LogFlowFunc(("Starting new frame at ts %llu\n", tsNanoStart));
     501
     502        bool fIdle = pThis->pIRhPort->pfnStartFrame(pThis->pIRhPort, 0 /* u32FrameNo */);
     503        vusbRhR3FrameRateCalcNew(pThis, fIdle);
     504
     505        uint64_t tsNow = RTTimeNanoTS();
     506        tsNext = (tsNanoStart + pThis->nsWait) > tsNow ? (tsNanoStart + pThis->nsWait) - tsNow : 0;
     507        pThis->tsFrameProcessed = tsNanoStart;
     508        LogFlowFunc(("Current frame took %llu nano seconds to process, next frame in %llu ns\n", tsNow - tsNanoStart, tsNext));
     509        if (fCallback)
     510            STAM_COUNTER_INC(&pThis->StatFramesProcessedClbk);
     511        else
     512            STAM_COUNTER_INC(&pThis->StatFramesProcessedThread);
     513    }
     514    else
     515    {
     516        tsNext = (pThis->tsFrameProcessed + pThis->nsWait) > tsNanoStart ? (pThis->tsFrameProcessed + pThis->nsWait) - tsNanoStart : 0;
     517        LogFlowFunc(("Next frame is too far away in the future, waiting... (tsNanoStart=%llu tsFrameProcessed=%llu)\n",
     518                     tsNanoStart, pThis->tsFrameProcessed));
     519    }
     520
     521    ASMAtomicXchgBool(&pThis->fFrameProcessing, false);
     522    LogFlowFunc(("returns %llu\n", tsNext));
     523    return tsNext;
     524}
     525
     526
     527/**
     528 * Worker for processing frames periodically.
     529 *
     530 * @returns VBox status code.
     531 * @param   pDrvIns     The driver instance.
     532 * @param   pThread     The PDM thread structure for the thread this worker runs on.
     533 */
     534static DECLCALLBACK(int) vusbRhR3PeriodFrameWorker(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     535{
     536    int rc = VINF_SUCCESS;
     537    PVUSBROOTHUB pThis = (PVUSBROOTHUB)pThread->pvUser;
     538
     539    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
     540        return VINF_SUCCESS;
     541
     542    while (pThread->enmState == PDMTHREADSTATE_RUNNING)
     543    {
     544        while (   !ASMAtomicReadU32(&pThis->uFrameRateDefault)
     545               && pThread->enmState == PDMTHREADSTATE_RUNNING)
     546        {
     547            /* Signal the waiter that we are stopped now. */
     548            rc = RTSemEventMultiSignal(pThis->hSemEventPeriodFrameStopped);
     549            AssertRC(rc);
     550
     551            rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrame, RT_INDEFINITE_WAIT);
     552            RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
     553        }
     554
     555        AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc);
     556        if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
     557            break;
     558
     559        uint64_t tsNext = vusbRhR3ProcessFrame(pThis, false /* fCallback */);
     560
     561        if (tsNext >= 250 * RT_NS_1US)
     562        {
     563            rc = RTSemEventMultiWaitEx(pThis->hSemEventPeriodFrame, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE,
     564                                       tsNext);
     565            AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc));
     566            RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
     567        }
     568    }
     569
     570    return VINF_SUCCESS;
     571}
     572
     573
     574/**
     575 * Unblock the periodic frame thread so it can respond to a state change.
     576 *
     577 * @returns VBox status code.
     578 * @param   pDrvIns     The driver instance.
     579 * @param   pThread     The send thread.
     580 */
     581static DECLCALLBACK(int) vusbRhR3PeriodFrameWorkerWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     582{
     583    PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
     584    return RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
     585}
     586
     587
    417588/** @copydoc VUSBIROOTHUBCONNECTOR::pfnSetUrbParams */
    418589static DECLCALLBACK(int) vusbRhSetUrbParams(PVUSBIROOTHUBCONNECTOR pInterface, size_t cbHci, size_t cbHciTd)
     
    703874
    704875
     876/** @copydoc VUSBIROOTHUBCONNECTOR::pfnSetFrameProcessing */
     877static DECLCALLBACK(int) vusbRhSetFrameProcessing(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate)
     878{
     879    int rc = VINF_SUCCESS;
     880    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     881
     882    /* Create the frame thread lazily. */
     883    if (   !pThis->hThreadPeriodFrame
     884        && uFrameRate)
     885    {
     886        ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
     887        pThis->uFrameRate = uFrameRate;
     888        vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
     889
     890        rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrame);
     891        AssertRCReturn(rc, rc);
     892
     893        rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrameStopped);
     894        AssertRCReturn(rc, rc);
     895
     896        rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->hThreadPeriodFrame, pThis, vusbRhR3PeriodFrameWorker,
     897                                   vusbRhR3PeriodFrameWorkerWakeup, 0, RTTHREADTYPE_IO, "VUsbPeriodFrm");
     898        AssertRCReturn(rc, rc);
     899
     900        rc = PDMR3ThreadResume(pThis->hThreadPeriodFrame);
     901        AssertRCReturn(rc, rc);
     902    }
     903    else if (   pThis->hThreadPeriodFrame
     904             && !uFrameRate)
     905    {
     906        /* Stop processing. */
     907        uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
     908        if (uFrameRateOld)
     909        {
     910            rc = RTSemEventMultiReset(pThis->hSemEventPeriodFrameStopped);
     911            AssertRC(rc);
     912
     913            /* Signal the frame thread to stop. */
     914            RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
     915
     916            /* Wait for signal from the thread that it stopped. */
     917            rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrameStopped, RT_INDEFINITE_WAIT);
     918            AssertRC(rc);
     919        }
     920    }
     921    else if (   pThis->hThreadPeriodFrame
     922             && uFrameRate)
     923    {
     924        /* Just switch to the new frame rate and let the periodic frame thread pick it up. */
     925        ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
     926    }
     927
     928    return rc;
     929}
     930
     931
    705932/* -=-=-=-=-=- VUSB Device methods (for the root hub) -=-=-=-=-=- */
    706933
     
    9201147    if (pRh->hSniffer != VUSBSNIFFER_NIL)
    9211148        VUSBSnifferDestroy(pRh->hSniffer);
     1149
     1150    if (pRh->hSemEventPeriodFrame)
     1151        RTSemEventMultiDestroy(pRh->hSemEventPeriodFrame);
     1152
     1153    if (pRh->hSemEventPeriodFrameStopped)
     1154        RTSemEventMultiDestroy(pRh->hSemEventPeriodFrameStopped);
     1155
    9221156    RTCritSectDelete(&pRh->CritSectDevices);
    9231157}
     
    9851219    pThis->pDrvIns                      = pDrvIns;
    9861220    /* the connector */
    987     pThis->IRhConnector.pfnSetUrbParams = vusbRhSetUrbParams;
    988     pThis->IRhConnector.pfnNewUrb       = vusbRhConnNewUrb;
    989     pThis->IRhConnector.pfnFreeUrb      = vusbRhConnFreeUrb;
    990     pThis->IRhConnector.pfnSubmitUrb    = vusbRhSubmitUrb;
    991     pThis->IRhConnector.pfnReapAsyncUrbs= vusbRhReapAsyncUrbs;
    992     pThis->IRhConnector.pfnCancelUrbsEp = vusbRhCancelUrbsEp;
    993     pThis->IRhConnector.pfnCancelAllUrbs= vusbRhCancelAllUrbs;
    994     pThis->IRhConnector.pfnAbortEp      = vusbRhAbortEp;
    995     pThis->IRhConnector.pfnAttachDevice = vusbRhAttachDevice;
    996     pThis->IRhConnector.pfnDetachDevice = vusbRhDetachDevice;
    997     pThis->hSniffer                     = VUSBSNIFFER_NIL;
    998     pThis->cbHci                        = 0;
    999     pThis->cbHciTd                      = 0;
     1221    pThis->IRhConnector.pfnSetUrbParams               = vusbRhSetUrbParams;
     1222    pThis->IRhConnector.pfnNewUrb                     = vusbRhConnNewUrb;
     1223    pThis->IRhConnector.pfnFreeUrb                    = vusbRhConnFreeUrb;
     1224    pThis->IRhConnector.pfnSubmitUrb                  = vusbRhSubmitUrb;
     1225    pThis->IRhConnector.pfnReapAsyncUrbs              = vusbRhReapAsyncUrbs;
     1226    pThis->IRhConnector.pfnCancelUrbsEp               = vusbRhCancelUrbsEp;
     1227    pThis->IRhConnector.pfnCancelAllUrbs              = vusbRhCancelAllUrbs;
     1228    pThis->IRhConnector.pfnAbortEp                    = vusbRhAbortEp;
     1229    pThis->IRhConnector.pfnAttachDevice               = vusbRhAttachDevice;
     1230    pThis->IRhConnector.pfnDetachDevice               = vusbRhDetachDevice;
     1231    pThis->IRhConnector.pfnSetPeriodicFrameProcessing = vusbRhSetFrameProcessing;
     1232    pThis->hSniffer                                   = VUSBSNIFFER_NIL;
     1233    pThis->cbHci                                      = 0;
     1234    pThis->cbHciTd                                    = 0;
     1235    pThis->fFrameProcessing                           = false;
    10001236#ifdef LOG_ENABLED
    1001     pThis->iSerial                      = 0;
     1237    pThis->iSerial                                    = 0;
    10021238#endif
    10031239    /*
     
    11681404    PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReapAsyncUrbs, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Profiling the vusbRhReapAsyncUrbs body (omitting calls when nothing is in-flight).",  "/VUSB/%d/ReapAsyncUrbs", pDrvIns->iInstance);
    11691405    PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatSubmitUrb,     STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Profiling the vusbRhSubmitUrb body.",                                 "/VUSB/%d/SubmitUrb",                 pDrvIns->iInstance);
     1406    PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatFramesProcessedThread, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Processed frames in the dedicated thread", "/VUSB/%d/FramesProcessedThread",       pDrvIns->iInstance);
     1407    PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatFramesProcessedClbk,   STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Processed frames in the URB completion callback", "/VUSB/%d/FramesProcessedClbk",  pDrvIns->iInstance);
    11701408#endif
    11711409    PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->Hub.Dev.UrbPool.cUrbsInPool, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "The number of URBs in the pool.",                                 "/VUSB/%d/cUrbsInPool",               pDrvIns->iInstance);
  • trunk/src/VBox/Devices/USB/VUSBInternal.h

    r59775 r59875  
    386386    /** The HUB.
    387387     * @todo remove this? */
    388     VUSBHUB                 Hub;
     388    VUSBHUB                    Hub;
    389389    /** Address hash table. */
    390     PVUSBDEV                apAddrHash[VUSB_ADDR_HASHSZ];
     390    PVUSBDEV                   apAddrHash[VUSB_ADDR_HASHSZ];
    391391    /** The default address. */
    392     PVUSBDEV                pDefaultAddress;
     392    PVUSBDEV                   pDefaultAddress;
    393393
    394394    /** Pointer to the driver instance. */
    395     PPDMDRVINS              pDrvIns;
     395    PPDMDRVINS                 pDrvIns;
    396396    /** Pointer to the root hub port interface we're attached to. */
    397     PVUSBIROOTHUBPORT       pIRhPort;
     397    PVUSBIROOTHUBPORT          pIRhPort;
    398398    /** Connector interface exposed upwards. */
    399     VUSBIROOTHUBCONNECTOR   IRhConnector;
     399    VUSBIROOTHUBCONNECTOR      IRhConnector;
     400
     401    /** Critical section protecting the device list. */
     402    RTCRITSECT                 CritSectDevices;
     403    /** Chain of devices attached to this hub. */
     404    PVUSBDEV                   pDevices;
    400405
    401406#if HC_ARCH_BITS == 32
    402     uint32_t                Alignment0;
    403 #endif
    404 
    405     /** Critical section protecting the device list. */
    406     RTCRITSECT              CritSectDevices;
    407     /** Chain of devices attached to this hub. */
    408     PVUSBDEV                pDevices;
     407    uint32_t                   Alignment0;
     408#endif
     409
     410    /** Availability Bitmap. */
     411    VUSBPORTBITMAP             Bitmap;
     412
     413    /** Sniffer instance for the root hub. */
     414    VUSBSNIFFER                hSniffer;
     415    /** Version of the attached Host Controller. */
     416    uint32_t                   fHcVersions;
     417    /** Size of the HCI specific data for each URB. */
     418    size_t                     cbHci;
     419    /** Size of the HCI specific TD. */
     420    size_t                     cbHciTd;
     421
     422    /** The periodic frame processing thread. */
     423    R3PTRTYPE(PPDMTHREAD)      hThreadPeriodFrame;
     424    /** Event semaphore to interact with the periodic frame processing thread. */
     425    R3PTRTYPE(RTSEMEVENTMULTI) hSemEventPeriodFrame;
     426    /** Event semaphore to release the thread waiting for the periodic frame processing thread to stop. */
     427    R3PTRTYPE(RTSEMEVENTMULTI) hSemEventPeriodFrameStopped;
     428    /** Current default frame rate for periodic frame processing thread. */
     429    volatile uint32_t          uFrameRateDefault;
     430    /** Current frame rate (can be lower than the default frame rate if there is no activity). */
     431    uint32_t                   uFrameRate;
     432    /** How long to wait until the next frame. */
     433    uint64_t                   nsWait;
     434    /** Timestamp when the last frame was processed. */
     435    uint64_t                   tsFrameProcessed;
     436    /** Number of USB work cycles with no transfers. */
     437    uint32_t                   cIdleCycles;
     438
     439    /** Flag whether a frame is currently being processed. */
     440    volatile bool              fFrameProcessing;
    409441
    410442#if HC_ARCH_BITS == 32
    411     uint32_t                Alignment1;
    412 #endif
    413 
    414     /** Availability Bitmap. */
    415     VUSBPORTBITMAP          Bitmap;
    416 
    417     /** Sniffer instance for the root hub. */
    418     VUSBSNIFFER             hSniffer;
    419     /** Version of the attached Host Controller. */
    420     uint32_t                fHcVersions;
    421     /** Size of the HCI specific data for each URB. */
    422     size_t                  cbHci;
    423     /** Size of the HCI specific TD. */
    424     size_t                  cbHciTd;
     443    uint32_t                   Alignment1;
     444#endif
     445
    425446#ifdef LOG_ENABLED
    426447    /** A serial number for URBs submitted on the roothub instance.
    427448     * Only logging builds. */
    428     uint32_t                iSerial;
     449    uint32_t                   iSerial;
    429450    /** Alignment */
    430     uint32_t                Alignment2;
     451    uint32_t                   Alignment2;
    431452#endif
    432453#ifdef VBOX_WITH_STATISTICS
     
    454475    STAMPROFILE             StatReapAsyncUrbs;
    455476    STAMPROFILE             StatSubmitUrb;
     477    STAMCOUNTER             StatFramesProcessedClbk;
     478    STAMCOUNTER             StatFramesProcessedThread;
    456479#endif
    457480} VUSBROOTHUB;
     
    501524DECLHIDDEN(int) vusbUrbCancelWorker(PVUSBURB pUrb, CANCELMODE enmMode);
    502525
     526DECLHIDDEN(uint64_t) vusbRhR3ProcessFrame(PVUSBROOTHUB pThis, bool fCallback);
     527
    503528int  vusbUrbQueueAsyncRh(PVUSBURB pUrb);
    504529
  • trunk/src/VBox/Devices/USB/VUSBUrb.cpp

    r59796 r59875  
    376376        pUrb->pVUsb->pfnFree(pUrb);
    377377    }
     378
     379    vusbRhR3ProcessFrame(pRh, true /* fCallback */);
    378380}
    379381
  • trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp

    r59540 r59875  
    10511051    GEN_CHECK_OFF(OHCI, StatTimer);
    10521052# endif
    1053     GEN_CHECK_OFF(OHCI, hThreadFrame);
    1054     GEN_CHECK_OFF(OHCI, hSemEventFrame);
    1055     GEN_CHECK_OFF(OHCI, fBusStarted);
    10561053    GEN_CHECK_OFF(OHCI, CsIrq);
    1057     GEN_CHECK_OFF(OHCI, nsWait);
    10581054    GEN_CHECK_OFF(OHCI, CritSect);
    10591055
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