From 11381e795844e50818cb8b1fa05244d03fe5e0c6 Mon Sep 17 00:00:00 2001
From: Fam Zheng <famz@redhat.com>
Date: Thu, 21 Aug 2014 12:05:40 -0500
Subject: [CHANGE 2/3] usb: Avoid reentrancy of ehci_advance_async_state
 (downstream only)
To: rhvirt-patches@redhat.com,
    jen@redhat.com

RH-Author: Fam Zheng <famz@redhat.com>
Message-id: <1408622740-10835-3-git-send-email-famz@redhat.com>
Patchwork-id: 60662
O-Subject: [RHEL-6.6 qemu-kvm PATCH v4 2/2] usb: Avoid reentrancy of ehci_advance_async_state (downstream only)
Bugzilla: 1018537
RH-Acked-by: Markus Armbruster <armbru@redhat.com>
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>

ehci_advance_async_state can be called either by the ehci->frame_timer
or by ehci->async_bh.

It's possible that during one main loop iteration, ehci->async_bh is
scheduled by a USB command, then ehci->frame_timer is fired. In this
case ehci_advance_async_state is called twice by main loop in a row.

Another case is that ehci_advance_async_state triggers io_cancel code,
which calls qemu_aio_wait() then qemu_bh_poll().
Then, ehci_advance_async_state reenters itself! This will cause
unexpected results such as assertion failure in usb_msd_cancel_io(),
because ehci_advance_async_state is not reentrant safe.

We can avoid this by simply cancelling the BH when entering
ehci_advance_async_state.

Upstream has very different ehci async code so it will not hit this
issue.

Signed-off-by: Fam Zheng <famz@redhat.com>
---
 hw/usb-ehci.c | 1 +
 1 file changed, 1 insertion(+)

Signed-off-by: Jeff E. Nelson <jen@redhat.com>
---
 hw/usb-ehci.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index 9aaccf5..baf4084 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -2034,6 +2034,7 @@ static void ehci_advance_async_state(EHCIState *ehci)
 {
     const int async = 1;
 
+    qemu_bh_cancel(ehci->async_bh);
     switch(ehci_get_state(ehci, async)) {
     case EST_INACTIVE:
         if (!(ehci->usbcmd & USBCMD_ASE)) {
-- 
1.9.3

