From patchwork Mon Oct 8 07:51:35 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 189935 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id CFDD02C0233 for ; Mon, 8 Oct 2012 19:12:21 +1100 (EST) Received: from localhost ([::1]:50465 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TL87i-0004Z7-GY for incoming@patchwork.ozlabs.org; Mon, 08 Oct 2012 03:51:34 -0400 Received: from eggs.gnu.org ([208.118.235.92]:48456) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TL86c-0001zA-4L for qemu-devel@nongnu.org; Mon, 08 Oct 2012 03:50:30 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TL86Z-0006Ok-8r for qemu-devel@nongnu.org; Mon, 08 Oct 2012 03:50:26 -0400 Received: from mx1.redhat.com ([209.132.183.28]:42223) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TL86Z-0006Nx-0J for qemu-devel@nongnu.org; Mon, 08 Oct 2012 03:50:23 -0400 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q987oMja000850 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 8 Oct 2012 03:50:22 -0400 Received: from shalem.localdomain.com (vpn1-7-79.ams2.redhat.com [10.36.7.79]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q987o59n012329; Mon, 8 Oct 2012 03:50:21 -0400 From: Hans de Goede To: Gerd Hoffmann Date: Mon, 8 Oct 2012 09:51:35 +0200 Message-Id: <1349682696-3046-12-git-send-email-hdegoede@redhat.com> In-Reply-To: <1349682696-3046-1-git-send-email-hdegoede@redhat.com> References: <1349682696-3046-1-git-send-email-hdegoede@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.132.183.28 Cc: Hans de Goede , qemu-devel@nongnu.org Subject: [Qemu-devel] [PATCH 11/12] ehci: Improve latency of interrupt delivery and async schedule scanning X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org While doing various performance tests of reading from USB mass storage devices I noticed the following:: 1) When an async handled packet completes, we don't immediately report an interrupt to the guest, instead we wait for the frame-timer to run and report it from there 2) If 1) has been fixed and an async handled packet takes a while to complete, then async_stepdown will become a high value, which means that there will be a large latency before any new packets queued by the guest in response to the interrupt get seen 1) was done deliberately as part of commit f0ad01f92: http://www.kraxel.org/cgit/qemu/commit/?h=usb.57&id=f0ad01f92ca02eee7cadbfd225c5de753ebd5fce Since setting reporting the interrupt immediately on async packet completion was causing issues with Linux guests, I believe this recently fixed Linux bug explains why this is happening: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=361aabf395e4a23cf554cf4ec0c0c6963b8beb01 Note that we can *not* count on this fix being present in all Linux guests! I was hoping that the recently added support for Interrupt Threshold Control would fix the issues with Linux guests, but adding a simple ehci_commit_irq() call to ehci_async_bh() still caused problems with Linux guests. The problem is, that when doing ehci_commit_irq() from ehci_async_bh(), the "old" frindex value is used to calculate usbsts_frindex, and when the frame-timer then runs possibly very shortly after ehci_async_bh(), it increases the frame-timer, and thus any interrupts raised from that frame-timer run, will also get reported to the guest immediately, rather then being delayed to the next frame-timer run. Luckily the solution for this is simple, this means that we need to increase frindex before calling ehci_commit_irq() from ehci_async_bh(), which in the end boils down to simple calling ehci_frame_timer() instead of ehci_async_bh() from the bh. This may seem like it causes a lot of extra work to be done, but this is not true. Any work done from the frame-timer processing the periodic schedule is work which then does not need to be done the next time the frame timer runs, also the frame-timer will re-arm itself at (possibly) a later time then it was armed for saving a vmexit at that time. As an additional advantage moving to simply calling the frame-timer also fixes 2) as the packet completion will have set async_stepdown to 0, and the re-arming of the timer with an async_stepdown of 0 ensures that any newly queued up packets get seen in a reasonable amount of time. This improves the speed (MB/s) of a Linux guest reading from a USB mass storage device by a factor of 1.5 - 1.7 with input pipelining disabled, and by a factor of 1.8 with input pipelining enabled. Signed-off-by: Hans de Goede --- hw/usb/hcd-ehci.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 444db15..fb3547d 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1261,7 +1261,7 @@ static void ehci_opreg_write(void *ptr, target_phys_addr_t addr, s->usbcmd = val; /* Set usbcmd for ehci_update_halt() */ ehci_update_halt(s); s->async_stepdown = 0; - qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); + qemu_bh_schedule(s->async_bh); } break; @@ -2534,12 +2534,6 @@ static void ehci_frame_timer(void *opaque) } } -static void ehci_async_bh(void *opaque) -{ - EHCIState *ehci = opaque; - ehci_advance_async_state(ehci); -} - static const MemoryRegionOps ehci_mmio_caps_ops = { .read = ehci_caps_read, .valid.min_access_size = 1, @@ -2770,7 +2764,7 @@ static int usb_ehci_initfn(PCIDevice *dev) } s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s); - s->async_bh = qemu_bh_new(ehci_async_bh, s); + s->async_bh = qemu_bh_new(ehci_frame_timer, s); QTAILQ_INIT(&s->aqueues); QTAILQ_INIT(&s->pqueues); usb_packet_init(&s->ipacket);