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: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
386ab487eb
commit
8082624099
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user