diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2014-07-18 16:26:07 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-07-18 16:33:01 -0700 |
commit | cdb4dd15e62eb984d9461b520d15d00ff2b88d9d (patch) | |
tree | 2015b60c2cd3bece77023c27c2e681cc76814732 /drivers/usb/host/ohci-q.c | |
parent | c6fcb85ea22889527ee44aba42c3e3b479fd2d92 (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>
Diffstat (limited to 'drivers/usb/host/ohci-q.c')
-rw-r--r-- | drivers/usb/host/ohci-q.c | 28 |
1 files changed, 26 insertions, 2 deletions
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) /*-------------------------------------------------------------------------*/ /* there are some urbs/eds to unlink; called in_irq(), with HCD locked */ -static void -finish_unlinks (struct ohci_hcd *ohci, u16 tick) +static void finish_unlinks(struct ohci_hcd *ohci) { + unsigned tick = ohci_frame_no(ohci); struct ed *ed, **last; rescan_all: @@ -1202,3 +1202,27 @@ static void process_done_list(struct ohci_hcd *ohci) takeback_td(ohci, td); } } + +/* + * TD takeback and URB giveback must be single-threaded. + * This routine takes care of it all. + */ +static void ohci_work(struct ohci_hcd *ohci) +{ + if (ohci->working) { + ohci->restart_work = 1; + return; + } + ohci->working = 1; + + restart: + process_done_list(ohci); + if (ohci->ed_rm_list) + finish_unlinks(ohci); + + if (ohci->restart_work) { + ohci->restart_work = 0; + goto restart; + } + ohci->working = 0; +} |