summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2012-05-24 13:34:02 +0200
committerGerd Hoffmann <kraxel@redhat.com>2012-06-07 10:02:23 +0200
commit3a21532626bb5c3ecb0f916843f75ccce6501a9d (patch)
tree0f659b8f070d029c5ff1535a9b9e3fc74213a00e
parent6ceced0b2ed52cdfb474d144dce54bf9afa613ce (diff)
downloadqemu-3a21532626bb5c3ecb0f916843f75ccce6501a9d.tar.gz
ehci: adaptive wakeup rate.
Adapt the frame timer sleeps according to the actual needs. With the periodic schedule being active we'll have to wakeup 1000 times per second and go check for work. In case only the async schedule is active we can be more lazy though. When idle ehci will increate the sleep time step by step, so qemu has to wake up less frequently. When we'll see transactions on the bus or the guest fiddles with the schedule enable/disable bits we'll return to a 1000 Hz wakeup rate and full speed. With both schedules disabled we stop wakeups altogether. This patch also drops the freq property (configures wakeup rate manually) which is obsoleted by this patch. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-rw-r--r--hw/usb/hcd-ehci.c57
1 files changed, 39 insertions, 18 deletions
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index c15dbee2c7..d97c5396d1 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -386,7 +386,6 @@ struct EHCIState {
int companion_count;
/* properties */
- uint32_t freq;
uint32_t maxframes;
/*
@@ -430,6 +429,7 @@ struct EHCIState {
QEMUSGList isgl;
uint64_t last_run_ns;
+ uint32_t async_stepdown;
};
#define SET_LAST_RUN_CLOCK(s) \
@@ -776,6 +776,7 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
{
EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+ uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4;
EHCIQueue *q, *tmp;
QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
@@ -784,8 +785,7 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
q->ts = ehci->last_run_ns;
continue;
}
- if (!flush && ehci->last_run_ns < q->ts + 250000000) {
- /* allow 0.25 sec idle */
+ if (!flush && ehci->last_run_ns < q->ts + maxage) {
continue;
}
ehci_free_queue(q);
@@ -1151,11 +1151,12 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) !=
((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & s->usbcmd)) {
- if (!ehci_enabled(s)) {
- qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
+ if (s->pstate == EST_INACTIVE) {
SET_LAST_RUN_CLOCK(s);
}
ehci_update_halt(s);
+ s->async_stepdown = 0;
+ qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
}
/* not supporting dynamic frame list size at the moment */
@@ -2146,10 +2147,16 @@ static void ehci_advance_state(EHCIState *ehci, int async)
case EST_EXECUTE:
again = ehci_state_execute(q);
+ if (async) {
+ ehci->async_stepdown = 0;
+ }
break;
case EST_EXECUTING:
assert(q != NULL);
+ if (async) {
+ ehci->async_stepdown = 0;
+ }
again = ehci_state_executing(q);
break;
@@ -2305,6 +2312,7 @@ static void ehci_update_frindex(EHCIState *ehci, int frames)
static void ehci_frame_timer(void *opaque)
{
EHCIState *ehci = opaque;
+ int schedules = 0;
int64_t expire_time, t_now;
uint64_t ns_elapsed;
int frames;
@@ -2312,21 +2320,32 @@ static void ehci_frame_timer(void *opaque)
int skipped_frames = 0;
t_now = qemu_get_clock_ns(vm_clock);
- expire_time = t_now + (get_ticks_per_sec() / ehci->freq);
-
ns_elapsed = t_now - ehci->last_run_ns;
frames = ns_elapsed / FRAME_TIMER_NS;
- for (i = 0; i < frames; i++) {
- ehci_update_frindex(ehci, 1);
+ if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) {
+ schedules++;
+ expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ);
- if (frames - i > ehci->maxframes) {
- skipped_frames++;
- } else {
- ehci_advance_periodic_state(ehci);
- }
+ for (i = 0; i < frames; i++) {
+ ehci_update_frindex(ehci, 1);
- ehci->last_run_ns += FRAME_TIMER_NS;
+ if (frames - i > ehci->maxframes) {
+ skipped_frames++;
+ } else {
+ ehci_advance_periodic_state(ehci);
+ }
+
+ ehci->last_run_ns += FRAME_TIMER_NS;
+ }
+ } else {
+ if (ehci->async_stepdown < ehci->maxframes / 2) {
+ ehci->async_stepdown++;
+ }
+ expire_time = t_now + (get_ticks_per_sec()
+ * ehci->async_stepdown / FRAME_TIMER_FREQ);
+ ehci_update_frindex(ehci, frames);
+ ehci->last_run_ns += FRAME_TIMER_NS * frames;
}
#if 0
@@ -2338,9 +2357,12 @@ static void ehci_frame_timer(void *opaque)
/* Async is not inside loop since it executes everything it can once
* called
*/
- qemu_bh_schedule(ehci->async_bh);
+ if (ehci_async_enabled(ehci) || ehci->astate != EST_INACTIVE) {
+ schedules++;
+ qemu_bh_schedule(ehci->async_bh);
+ }
- if (ehci_enabled(ehci)) {
+ if (schedules) {
qemu_mod_timer(ehci->frame_timer, expire_time);
}
}
@@ -2379,7 +2401,6 @@ static const VMStateDescription vmstate_ehci = {
};
static Property ehci_properties[] = {
- DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ),
DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128),
DEFINE_PROP_END_OF_LIST(),
};