aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2014-07-18 16:26:07 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-07-18 19:33:01 -0400
commitcdb4dd15e62eb984d9461b520d15d00ff2b88d9d (patch)
tree2015b60c2cd3bece77023c27c2e681cc76814732
parentc6fcb85ea22889527ee44aba42c3e3b479fd2d92 (diff)
USB: OHCI: make URB completions single-threaded
URBs for a particular endpoint should complete sequentially. That is, we shouldn't call the completion handler for one URB until the handler for the previous URB has returned. When the OHCI watchdog routine is added, there will be two paths for completing URBs: interrupt handler and watchdog routine. Their activities have to be synchronized so that completions don't occur in multiple threads concurrently. For that purpose, this patch creates an ohci_work() routine which will be responsible for calling process_done_list() and finish_unlinks(), the two routines that detect when an URB is complete. Everything will funnel through ohci_work(), and it will be careful not to run in more than one thread at a time. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/host/ohci-hcd.c10
-rw-r--r--drivers/usb/host/ohci-hub.c6
-rw-r--r--drivers/usb/host/ohci-q.c28
-rw-r--r--drivers/usb/host/ohci.h2
4 files changed, 34 insertions, 12 deletions
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 3112799bba7f..ad588538e2e7 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -316,7 +316,7 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
316 316
317 if (ohci->rh_state != OHCI_RH_RUNNING) { 317 if (ohci->rh_state != OHCI_RH_RUNNING) {
318 /* With HC dead, we can clean up right away */ 318 /* With HC dead, we can clean up right away */
319 finish_unlinks(ohci, 0); 319 ohci_work(ohci);
320 } 320 }
321 } 321 }
322 spin_unlock_irqrestore (&ohci->lock, flags); 322 spin_unlock_irqrestore (&ohci->lock, flags);
@@ -349,7 +349,7 @@ rescan:
349 if (ohci->rh_state != OHCI_RH_RUNNING) { 349 if (ohci->rh_state != OHCI_RH_RUNNING) {
350sanitize: 350sanitize:
351 ed->state = ED_IDLE; 351 ed->state = ED_IDLE;
352 finish_unlinks (ohci, 0); 352 ohci_work(ohci);
353 } 353 }
354 354
355 switch (ed->state) { 355 switch (ed->state) {
@@ -789,9 +789,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
789 /* handle any pending URB/ED unlinks, leaving INTR_SF enabled 789 /* handle any pending URB/ED unlinks, leaving INTR_SF enabled
790 * when there's still unlinking to be done (next frame). 790 * when there's still unlinking to be done (next frame).
791 */ 791 */
792 process_done_list(ohci); 792 ohci_work(ohci);
793 if (ohci->ed_rm_list)
794 finish_unlinks (ohci, ohci_frame_no(ohci));
795 if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list 793 if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
796 && ohci->rh_state == OHCI_RH_RUNNING) 794 && ohci->rh_state == OHCI_RH_RUNNING)
797 ohci_writel (ohci, OHCI_INTR_SF, &regs->intrdisable); 795 ohci_writel (ohci, OHCI_INTR_SF, &regs->intrdisable);
@@ -879,7 +877,7 @@ int ohci_restart(struct ohci_hcd *ohci)
879 if (!urb->unlinked) 877 if (!urb->unlinked)
880 urb->unlinked = -ESHUTDOWN; 878 urb->unlinked = -ESHUTDOWN;
881 } 879 }
882 finish_unlinks (ohci, 0); 880 ohci_work(ohci);
883 spin_unlock_irq(&ohci->lock); 881 spin_unlock_irq(&ohci->lock);
884 882
885 /* paranoia, in case that didn't work: */ 883 /* paranoia, in case that didn't work: */
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index dccb90edd66e..8991692bcfb8 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -40,8 +40,7 @@
40 (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) 40 (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
41 41
42static void update_done_list(struct ohci_hcd *); 42static void update_done_list(struct ohci_hcd *);
43static void process_done_list(struct ohci_hcd *); 43static void ohci_work(struct ohci_hcd *);
44static void finish_unlinks (struct ohci_hcd *, u16);
45 44
46#ifdef CONFIG_PM 45#ifdef CONFIG_PM
47static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop) 46static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
@@ -89,8 +88,7 @@ __acquires(ohci->lock)
89 spin_lock_irq (&ohci->lock); 88 spin_lock_irq (&ohci->lock);
90 } 89 }
91 update_done_list(ohci); 90 update_done_list(ohci);
92 process_done_list(ohci); 91 ohci_work(ohci);
93 finish_unlinks (ohci, ohci_frame_no(ohci));
94 92
95 /* 93 /*
96 * Some controllers don't handle "global" suspend properly if 94 * Some controllers don't handle "global" suspend properly if
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index f36b2fa0ee2f..1974ddc68e45 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -964,9 +964,9 @@ static void update_done_list(struct ohci_hcd *ohci)
964/*-------------------------------------------------------------------------*/ 964/*-------------------------------------------------------------------------*/
965 965
966/* there are some urbs/eds to unlink; called in_irq(), with HCD locked */ 966/* there are some urbs/eds to unlink; called in_irq(), with HCD locked */
967static void 967static void finish_unlinks(struct ohci_hcd *ohci)
968finish_unlinks (struct ohci_hcd *ohci, u16 tick)
969{ 968{
969 unsigned tick = ohci_frame_no(ohci);
970 struct ed *ed, **last; 970 struct ed *ed, **last;
971 971
972rescan_all: 972rescan_all:
@@ -1202,3 +1202,27 @@ static void process_done_list(struct ohci_hcd *ohci)
1202 takeback_td(ohci, td); 1202 takeback_td(ohci, td);
1203 } 1203 }
1204} 1204}
1205
1206/*
1207 * TD takeback and URB giveback must be single-threaded.
1208 * This routine takes care of it all.
1209 */
1210static void ohci_work(struct ohci_hcd *ohci)
1211{
1212 if (ohci->working) {
1213 ohci->restart_work = 1;
1214 return;
1215 }
1216 ohci->working = 1;
1217
1218 restart:
1219 process_done_list(ohci);
1220 if (ohci->ed_rm_list)
1221 finish_unlinks(ohci);
1222
1223 if (ohci->restart_work) {
1224 ohci->restart_work = 0;
1225 goto restart;
1226 }
1227 ohci->working = 0;
1228}
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index a8259bc6fd8b..ef348c2e1e4b 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -393,6 +393,8 @@ struct ohci_hcd {
393 unsigned long next_statechange; /* suspend/resume */ 393 unsigned long next_statechange; /* suspend/resume */
394 u32 fminterval; /* saved register */ 394 u32 fminterval; /* saved register */
395 unsigned autostop:1; /* rh auto stopping/stopped */ 395 unsigned autostop:1; /* rh auto stopping/stopped */
396 unsigned working:1;
397 unsigned restart_work:1;
396 398
397 unsigned long flags; /* for HC bugs */ 399 unsigned long flags; /* for HC bugs */
398#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ 400#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */