diff options
Diffstat (limited to 'drivers/usb/core/urb.c')
-rw-r--r-- | drivers/usb/core/urb.c | 50 |
1 files changed, 21 insertions, 29 deletions
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 7c0555548ac..419e6b34e2f 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c | |||
@@ -137,6 +137,16 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor) | |||
137 | } | 137 | } |
138 | EXPORT_SYMBOL_GPL(usb_anchor_urb); | 138 | EXPORT_SYMBOL_GPL(usb_anchor_urb); |
139 | 139 | ||
140 | /* Callers must hold anchor->lock */ | ||
141 | static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor) | ||
142 | { | ||
143 | urb->anchor = NULL; | ||
144 | list_del(&urb->anchor_list); | ||
145 | usb_put_urb(urb); | ||
146 | if (list_empty(&anchor->urb_list)) | ||
147 | wake_up(&anchor->wait); | ||
148 | } | ||
149 | |||
140 | /** | 150 | /** |
141 | * usb_unanchor_urb - unanchors an URB | 151 | * usb_unanchor_urb - unanchors an URB |
142 | * @urb: pointer to the urb to anchor | 152 | * @urb: pointer to the urb to anchor |
@@ -156,17 +166,14 @@ void usb_unanchor_urb(struct urb *urb) | |||
156 | return; | 166 | return; |
157 | 167 | ||
158 | spin_lock_irqsave(&anchor->lock, flags); | 168 | spin_lock_irqsave(&anchor->lock, flags); |
159 | if (unlikely(anchor != urb->anchor)) { | 169 | /* |
160 | /* we've lost the race to another thread */ | 170 | * At this point, we could be competing with another thread which |
161 | spin_unlock_irqrestore(&anchor->lock, flags); | 171 | * has the same intention. To protect the urb from being unanchored |
162 | return; | 172 | * twice, only the winner of the race gets the job. |
163 | } | 173 | */ |
164 | urb->anchor = NULL; | 174 | if (likely(anchor == urb->anchor)) |
165 | list_del(&urb->anchor_list); | 175 | __usb_unanchor_urb(urb, anchor); |
166 | spin_unlock_irqrestore(&anchor->lock, flags); | 176 | spin_unlock_irqrestore(&anchor->lock, flags); |
167 | usb_put_urb(urb); | ||
168 | if (list_empty(&anchor->urb_list)) | ||
169 | wake_up(&anchor->wait); | ||
170 | } | 177 | } |
171 | EXPORT_SYMBOL_GPL(usb_unanchor_urb); | 178 | EXPORT_SYMBOL_GPL(usb_unanchor_urb); |
172 | 179 | ||
@@ -749,20 +756,11 @@ EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs); | |||
749 | void usb_unlink_anchored_urbs(struct usb_anchor *anchor) | 756 | void usb_unlink_anchored_urbs(struct usb_anchor *anchor) |
750 | { | 757 | { |
751 | struct urb *victim; | 758 | struct urb *victim; |
752 | unsigned long flags; | ||
753 | 759 | ||
754 | spin_lock_irqsave(&anchor->lock, flags); | 760 | while ((victim = usb_get_from_anchor(anchor)) != NULL) { |
755 | while (!list_empty(&anchor->urb_list)) { | ||
756 | victim = list_entry(anchor->urb_list.prev, struct urb, | ||
757 | anchor_list); | ||
758 | usb_get_urb(victim); | ||
759 | spin_unlock_irqrestore(&anchor->lock, flags); | ||
760 | /* this will unanchor the URB */ | ||
761 | usb_unlink_urb(victim); | 761 | usb_unlink_urb(victim); |
762 | usb_put_urb(victim); | 762 | usb_put_urb(victim); |
763 | spin_lock_irqsave(&anchor->lock, flags); | ||
764 | } | 763 | } |
765 | spin_unlock_irqrestore(&anchor->lock, flags); | ||
766 | } | 764 | } |
767 | EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs); | 765 | EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs); |
768 | 766 | ||
@@ -799,12 +797,11 @@ struct urb *usb_get_from_anchor(struct usb_anchor *anchor) | |||
799 | victim = list_entry(anchor->urb_list.next, struct urb, | 797 | victim = list_entry(anchor->urb_list.next, struct urb, |
800 | anchor_list); | 798 | anchor_list); |
801 | usb_get_urb(victim); | 799 | usb_get_urb(victim); |
802 | spin_unlock_irqrestore(&anchor->lock, flags); | 800 | __usb_unanchor_urb(victim, anchor); |
803 | usb_unanchor_urb(victim); | ||
804 | } else { | 801 | } else { |
805 | spin_unlock_irqrestore(&anchor->lock, flags); | ||
806 | victim = NULL; | 802 | victim = NULL; |
807 | } | 803 | } |
804 | spin_unlock_irqrestore(&anchor->lock, flags); | ||
808 | 805 | ||
809 | return victim; | 806 | return victim; |
810 | } | 807 | } |
@@ -826,12 +823,7 @@ void usb_scuttle_anchored_urbs(struct usb_anchor *anchor) | |||
826 | while (!list_empty(&anchor->urb_list)) { | 823 | while (!list_empty(&anchor->urb_list)) { |
827 | victim = list_entry(anchor->urb_list.prev, struct urb, | 824 | victim = list_entry(anchor->urb_list.prev, struct urb, |
828 | anchor_list); | 825 | anchor_list); |
829 | usb_get_urb(victim); | 826 | __usb_unanchor_urb(victim, anchor); |
830 | spin_unlock_irqrestore(&anchor->lock, flags); | ||
831 | /* this may free the URB */ | ||
832 | usb_unanchor_urb(victim); | ||
833 | usb_put_urb(victim); | ||
834 | spin_lock_irqsave(&anchor->lock, flags); | ||
835 | } | 827 | } |
836 | spin_unlock_irqrestore(&anchor->lock, flags); | 828 | spin_unlock_irqrestore(&anchor->lock, flags); |
837 | } | 829 | } |