Commit 80826240 authored by Hans de Goede's avatar Hans de Goede Committed by Gerd Hoffmann
Browse files

ehci: Lower timer freq when the periodic schedule is idle



Lower the timer freq if no iso schedule packets complete for 64 frames in
a row.

We can safely do this, without adding latency, because:
1) If there is isoc traffic this will never trigger
2) For async handled interrupt packets (only usb-host), the completion handler
   will immediately schedule the frame_timer from a bh
3) All devices using NAK to signal no data for interrupt endpoints now use
   wakeup, which will immediately schedule the frame_timer from a bh

The advantage of this is that when we only have interrupt packets in the
periodic schedule, async_stepdown can do its work and significantly lower
the frequency at which the frame_timer runs.

Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarGerd Hoffmann <kraxel@redhat.com>
parent 386ab487
Loading
Loading
Loading
Loading
+33 −6
Original line number Diff line number Diff line
@@ -114,6 +114,7 @@
#define BUFF_SIZE        5*4096   // Max bytes to transfer per transaction
#define MAX_QH           100      // Max allowable queue heads in a chain
#define MIN_FR_PER_TICK  3        // Min frames to process when catching up
#define PERIODIC_ACTIVE  64

/*  Internal periodic / asynchronous schedule state machine states
 */
@@ -738,6 +739,19 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[],
    return 0;
}

static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
{
    EHCIState *s = container_of(bus, EHCIState, bus);
    uint32_t portsc = s->portsc[ep->dev->port->index];

    if (portsc & PORTSC_POWNER) {
        return;
    }

    s->periodic_sched_active = PERIODIC_ACTIVE;
    qemu_bh_schedule(s->async_bh);
}

static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr)
{
    USBDevice *dev;
@@ -1188,9 +1202,10 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
    trace_usb_ehci_packet_action(p->queue, p, "wakeup");
    p->async = EHCI_ASYNC_FINISHED;

    if (p->queue->async) {
        qemu_bh_schedule(p->queue->ehci->async_bh);
    if (!p->queue->async) {
        s->periodic_sched_active = PERIODIC_ACTIVE;
    }
    qemu_bh_schedule(s->async_bh);
}

static void ehci_execute_complete(EHCIQueue *q)
@@ -1344,6 +1359,8 @@ static int ehci_process_itd(EHCIState *ehci,
    uint32_t i, len, pid, dir, devaddr, endp;
    uint32_t pg, off, ptr1, ptr2, max, mult;

    ehci->periodic_sched_active = PERIODIC_ACTIVE;

    dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
    devaddr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR);
    endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP);
@@ -2033,6 +2050,9 @@ static void ehci_advance_state(EHCIState *ehci, int async)
        case EST_WRITEBACK:
            assert(q != NULL);
            again = ehci_state_writeback(q);
            if (!async) {
                ehci->periodic_sched_active = PERIODIC_ACTIVE;
            }
            break;

        default:
@@ -2198,7 +2218,6 @@ static void ehci_frame_timer(void *opaque)

    if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) {
        need_timer++;
        ehci->async_stepdown = 0;

        if (frames > ehci->maxframes) {
            skipped_frames = frames - ehci->maxframes;
@@ -2222,18 +2241,25 @@ static void ehci_frame_timer(void *opaque)
                    break;
                }
            }
            if (ehci->periodic_sched_active) {
                ehci->periodic_sched_active--;
            }
            ehci_update_frindex(ehci, 1);
            ehci_advance_periodic_state(ehci);
            ehci->last_run_ns += FRAME_TIMER_NS;
        }
    } else {
        if (ehci->async_stepdown < ehci->maxframes / 2) {
            ehci->async_stepdown++;
        }
        ehci->periodic_sched_active = 0;
        ehci_update_frindex(ehci, frames);
        ehci->last_run_ns += FRAME_TIMER_NS * frames;
    }

    if (ehci->periodic_sched_active) {
        ehci->async_stepdown = 0;
    } else if (ehci->async_stepdown < ehci->maxframes / 2) {
        ehci->async_stepdown++;
    }

    /*  Async is not inside loop since it executes everything it can once
     *  called
     */
@@ -2301,6 +2327,7 @@ static USBPortOps ehci_port_ops = {

static USBBusOps ehci_bus_ops = {
    .register_companion = ehci_register_companion,
    .wakeup_endpoint = ehci_wakeup_endpoint,
};

static int usb_ehci_post_load(void *opaque, int version_id)
+1 −0
Original line number Diff line number Diff line
@@ -311,6 +311,7 @@ struct EHCIState {

    uint64_t last_run_ns;
    uint32_t async_stepdown;
    uint32_t periodic_sched_active;
    bool int_req_by_async;
};