aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2013-10-09 11:01:41 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-10-11 19:33:58 -0400
commit6ec4147e7bdbde168f5bce30de5984aa4f971b22 (patch)
tree0c06a5ac384ea390883f085de37992f0dbfedf3f /include
parent9ef73dbdd0fc292d183e93cd1d4b21d1a66040d7 (diff)
usb-anchor: Delay usb_wait_anchor_empty_timeout wake up till completion is done
usb_wait_anchor_empty_timeout() should wait till the completion handler has run. Both the zd1211rw driver and the uas driver (in its task mgmt) depend on the completion handler having completed when usb_wait_anchor_empty_timeout() returns, as they read state set by the completion handler after an usb_wait_anchor_empty_timeout() call. But __usb_hcd_giveback_urb() calls usb_unanchor_urb before calling the completion handler. This is necessary as the completion handler may re-submit and re-anchor the urb. But this introduces a race where the state these drivers want to read has not been set yet by the completion handler (this race is easily triggered with the uas task mgmt code). I've considered adding an anchor_count to struct urb, which would be incremented on anchor and decremented on unanchor, and then only actually do the anchor / unanchor on 0 -> 1 and 1 -> 0 transtions, combined with moving the unanchor call in hcd_giveback_urb to after calling the completion handler. But this will only work if urb's are only re-anchored to the same anchor as they were anchored to before the completion handler ran. And at least one driver re-anchors to another anchor from the completion handler (rtlwifi). So I have come up with this patch instead, which adds the ability to suspend wakeups of usb_wait_anchor_empty_timeout() waiters to the usb_anchor functionality, and uses this in __usb_hcd_giveback_urb() to delay wake-ups until the completion handler has run. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Acked-by: Oliver Neukum <oliver@neukum.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'include')
-rw-r--r--include/linux/usb.h3
1 files changed, 3 insertions, 0 deletions
diff --git a/include/linux/usb.h b/include/linux/usb.h
index fa8bedf06c6e..055ba74bee80 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1209,6 +1209,7 @@ struct usb_anchor {
1209 struct list_head urb_list; 1209 struct list_head urb_list;
1210 wait_queue_head_t wait; 1210 wait_queue_head_t wait;
1211 spinlock_t lock; 1211 spinlock_t lock;
1212 atomic_t suspend_wakeups;
1212 unsigned int poisoned:1; 1213 unsigned int poisoned:1;
1213}; 1214};
1214 1215
@@ -1575,6 +1576,8 @@ extern void usb_kill_anchored_urbs(struct usb_anchor *anchor);
1575extern void usb_poison_anchored_urbs(struct usb_anchor *anchor); 1576extern void usb_poison_anchored_urbs(struct usb_anchor *anchor);
1576extern void usb_unpoison_anchored_urbs(struct usb_anchor *anchor); 1577extern void usb_unpoison_anchored_urbs(struct usb_anchor *anchor);
1577extern void usb_unlink_anchored_urbs(struct usb_anchor *anchor); 1578extern void usb_unlink_anchored_urbs(struct usb_anchor *anchor);
1579extern void usb_anchor_suspend_wakeups(struct usb_anchor *anchor);
1580extern void usb_anchor_resume_wakeups(struct usb_anchor *anchor);
1578extern void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor); 1581extern void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor);
1579extern void usb_unanchor_urb(struct urb *urb); 1582extern void usb_unanchor_urb(struct urb *urb);
1580extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, 1583extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,