|
@@ -137,6 +137,16 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(usb_anchor_urb);
|
|
|
|
|
|
+/* Callers must hold anchor->lock */
|
|
|
+static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
|
|
|
+{
|
|
|
+ urb->anchor = NULL;
|
|
|
+ list_del(&urb->anchor_list);
|
|
|
+ usb_put_urb(urb);
|
|
|
+ if (list_empty(&anchor->urb_list))
|
|
|
+ wake_up(&anchor->wait);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* usb_unanchor_urb - unanchors an URB
|
|
|
* @urb: pointer to the urb to anchor
|
|
@@ -156,17 +166,14 @@ void usb_unanchor_urb(struct urb *urb)
|
|
|
return;
|
|
|
|
|
|
spin_lock_irqsave(&anchor->lock, flags);
|
|
|
- if (unlikely(anchor != urb->anchor)) {
|
|
|
- /* we've lost the race to another thread */
|
|
|
- spin_unlock_irqrestore(&anchor->lock, flags);
|
|
|
- return;
|
|
|
- }
|
|
|
- urb->anchor = NULL;
|
|
|
- list_del(&urb->anchor_list);
|
|
|
+ /*
|
|
|
+ * At this point, we could be competing with another thread which
|
|
|
+ * has the same intention. To protect the urb from being unanchored
|
|
|
+ * twice, only the winner of the race gets the job.
|
|
|
+ */
|
|
|
+ if (likely(anchor == urb->anchor))
|
|
|
+ __usb_unanchor_urb(urb, anchor);
|
|
|
spin_unlock_irqrestore(&anchor->lock, flags);
|
|
|
- usb_put_urb(urb);
|
|
|
- if (list_empty(&anchor->urb_list))
|
|
|
- wake_up(&anchor->wait);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(usb_unanchor_urb);
|
|
|
|
|
@@ -749,20 +756,11 @@ EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
|
|
|
void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
|
|
|
{
|
|
|
struct urb *victim;
|
|
|
- unsigned long flags;
|
|
|
|
|
|
- spin_lock_irqsave(&anchor->lock, flags);
|
|
|
- while (!list_empty(&anchor->urb_list)) {
|
|
|
- victim = list_entry(anchor->urb_list.prev, struct urb,
|
|
|
- anchor_list);
|
|
|
- usb_get_urb(victim);
|
|
|
- spin_unlock_irqrestore(&anchor->lock, flags);
|
|
|
- /* this will unanchor the URB */
|
|
|
+ while ((victim = usb_get_from_anchor(anchor)) != NULL) {
|
|
|
usb_unlink_urb(victim);
|
|
|
usb_put_urb(victim);
|
|
|
- spin_lock_irqsave(&anchor->lock, flags);
|
|
|
}
|
|
|
- spin_unlock_irqrestore(&anchor->lock, flags);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
|
|
|
|
|
@@ -799,12 +797,11 @@ struct urb *usb_get_from_anchor(struct usb_anchor *anchor)
|
|
|
victim = list_entry(anchor->urb_list.next, struct urb,
|
|
|
anchor_list);
|
|
|
usb_get_urb(victim);
|
|
|
- spin_unlock_irqrestore(&anchor->lock, flags);
|
|
|
- usb_unanchor_urb(victim);
|
|
|
+ __usb_unanchor_urb(victim, anchor);
|
|
|
} else {
|
|
|
- spin_unlock_irqrestore(&anchor->lock, flags);
|
|
|
victim = NULL;
|
|
|
}
|
|
|
+ spin_unlock_irqrestore(&anchor->lock, flags);
|
|
|
|
|
|
return victim;
|
|
|
}
|
|
@@ -826,12 +823,7 @@ void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
|
|
|
while (!list_empty(&anchor->urb_list)) {
|
|
|
victim = list_entry(anchor->urb_list.prev, struct urb,
|
|
|
anchor_list);
|
|
|
- usb_get_urb(victim);
|
|
|
- spin_unlock_irqrestore(&anchor->lock, flags);
|
|
|
- /* this may free the URB */
|
|
|
- usb_unanchor_urb(victim);
|
|
|
- usb_put_urb(victim);
|
|
|
- spin_lock_irqsave(&anchor->lock, flags);
|
|
|
+ __usb_unanchor_urb(victim, anchor);
|
|
|
}
|
|
|
spin_unlock_irqrestore(&anchor->lock, flags);
|
|
|
}
|