[USB][OHCI]BugFix:Insert new ed at end of ohci->ed_rm_list to accomdate race condition

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



There is a race condition between finish_unlinks->finish_urb() 
function and usb_kill_urb() in ohci controller case. 
finish_urb is called after calling  spin_unlock(&ohci->lock), 
then if during this time, usb_kill_urb is called for another
 endpoint, then new ed will be added to ed_rm_list at beginning 
for unlink. and ed_rm_list will point to newly added ed.

When finish_urb() is completed in finish_unlinks() and
ed->td_list becomes empty as in below code (in finish_unlinks() function)
        if (list_empty(&ed->td_list)) {
                *last = ed->ed_next;
                ed->ed_next = NULL;
        } else if (ohci->rh_state == OHCI_RH_RUNNING) {
                *last = ed->ed_next;
                ed->ed_next = NULL;
                ed_schedule(ohci, ed);
        }
*last = ed->ed_next will make ed_rm_list to point to ed->ed_next and
previously added ed by usb_kill_urb will be left unreferenced by
ed_rm_list. This causes usb_kill_urb() hang forever waiting for
finish_unlink to remove added ed from ed_rm_list.

The main reason for hang in this race condtion is addition and removal
of ed from ed_rm_list in the beginning. This patch adds new ed in
ed_rm_list at the end and it solves the usb_kill_urb hang issue for
ohci controller.

Signed-off-by: Aman Deep <aman.deep@xxxxxxxxxxx>
---
 drivers/usb/host/ohci-q.c |   23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index f7d561e..90a24ab 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -481,13 +481,28 @@ done:
  */
 static void start_ed_unlink (struct ohci_hcd *ohci, struct ed *ed)
 {
+	struct ed       *tmp, *prev;
+
 	ed->hwINFO |= cpu_to_hc32 (ohci, ED_DEQUEUE);
 	ed_deschedule (ohci, ed);
 
-	/* rm_list is just singly linked, for simplicity */
-	ed->ed_next = ohci->ed_rm_list;
-	ed->ed_prev = NULL;
-	ohci->ed_rm_list = ed;
+	/* rm_list is just singly linked, for simplicity
+	ed is now added to end of ed_rm_list for ensuring that
+	during race condition, this new ed is accessed by finish_unlinks
+	*/
+	tmp = prev = ohci->ed_rm_list;
+	while (tmp != NULL) {
+		prev = tmp;
+		tmp = tmp->ed_next;
+	}
+	if (prev != NULL) {
+		prev->ed_next = ed;
+		ed->ed_prev = NULL;
+	} else {
+		ed->ed_next = ohci->ed_rm_list;
+		ohci->ed_rm_list = ed;
+	}
+	ed->ed_next = NULL;
 
 	/* enable SOF interrupt */
 	ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrstatus);
-- 
1.7.9.5
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ¥Šwÿº{.nÇ+‰·¥Š{±þëþ)í…æèw*jg¬±¨¶‰šŽŠÝ¢jÿ¾«þG«?éÿ¢¸¢·¦j:+v‰¨ŠwèjØm¶Ÿÿþø¯ù®w¥þŠàþf£¢·hš?â?úÿ†Ù¥




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux