@@ -145,6 +145,19 @@ static int usb_anchor_check_wakeup(struct usb_anchor *anchor)
list_empty(&anchor->urb_list);
}
+static int usb_anchor_safe_empty(struct usb_anchor *anchor)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&anchor->lock, flags);
+ ret = atomic_read(&anchor->suspend_wakeups) == 0 &&
+ list_empty(&anchor->urb_list);
+ spin_unlock_irqrestore(&anchor->lock, flags);
+
+ return ret;
+}
+
/* Callers must hold anchor->lock */
static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
{
@@ -772,11 +785,12 @@ void usb_block_urb(struct urb *urb)
EXPORT_SYMBOL_GPL(usb_block_urb);
/**
- * usb_kill_anchored_urbs - cancel transfer requests en masse
+ * usb_kill_anchored_urbs - kill all URBs associated with an anchor
* @anchor: anchor the requests are bound to
*
- * this allows all outstanding URBs to be killed starting
- * from the back of the queue
+ * This kills all outstanding URBs starting from the back of the queue,
+ * with guarantee that no completer callbacks will take place from the
+ * anchor after this function returns.
*
* This routine should not be called by a driver after its disconnect
* method has returned.
@@ -785,19 +799,21 @@ void usb_kill_anchored_urbs(struct usb_anchor *anchor)
{
struct urb *victim;
- spin_lock_irq(&anchor->lock);
- while (!list_empty(&anchor->urb_list)) {
- victim = list_entry(anchor->urb_list.prev, struct urb,
- anchor_list);
- /* we must make sure the URB isn't freed before we kill it*/
- usb_get_urb(victim);
- spin_unlock_irq(&anchor->lock);
- /* this will unanchor the URB */
- usb_kill_urb(victim);
- usb_put_urb(victim);
+ do {
spin_lock_irq(&anchor->lock);
- }
- spin_unlock_irq(&anchor->lock);
+ while (!list_empty(&anchor->urb_list)) {
+ victim = list_entry(anchor->urb_list.prev,
+ struct urb, anchor_list);
+ /* make sure the URB isn't freed before we kill it */
+ usb_get_urb(victim);
+ spin_unlock_irq(&anchor->lock);
+ /* this will unanchor the URB */
+ usb_kill_urb(victim);
+ usb_put_urb(victim);
+ spin_lock_irq(&anchor->lock);
+ }
+ spin_unlock_irq(&anchor->lock);
+ } while (!usb_anchor_safe_empty(anchor));
}
EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs);
@@ -817,20 +833,22 @@ void usb_poison_anchored_urbs(struct usb_anchor *anchor)
{
struct urb *victim;
- spin_lock_irq(&anchor->lock);
- anchor->poisoned = 1;
- while (!list_empty(&anchor->urb_list)) {
- victim = list_entry(anchor->urb_list.prev, struct urb,
- anchor_list);
- /* we must make sure the URB isn't freed before we kill it*/
- usb_get_urb(victim);
- spin_unlock_irq(&anchor->lock);
- /* this will unanchor the URB */
- usb_poison_urb(victim);
- usb_put_urb(victim);
+ do {
spin_lock_irq(&anchor->lock);
- }
- spin_unlock_irq(&anchor->lock);
+ anchor->poisoned = 1;
+ while (!list_empty(&anchor->urb_list)) {
+ victim = list_entry(anchor->urb_list.prev,
+ struct urb, anchor_list);
+ /* make sure the URB isn't freed before we kill it */
+ usb_get_urb(victim);
+ spin_unlock_irq(&anchor->lock);
+ /* this will unanchor the URB */
+ usb_poison_urb(victim);
+ usb_put_urb(victim);
+ spin_lock_irq(&anchor->lock);
+ }
+ spin_unlock_irq(&anchor->lock);
+ } while (!usb_anchor_safe_empty(anchor));
}
EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs);
@@ -971,13 +989,15 @@ void usb_scuttle_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_unanchor_urb(victim, anchor);
- }
- spin_unlock_irqrestore(&anchor->lock, flags);
+ do {
+ spin_lock_irqsave(&anchor->lock, flags);
+ while (!list_empty(&anchor->urb_list)) {
+ victim = list_entry(anchor->urb_list.prev,
+ struct urb, anchor_list);
+ __usb_unanchor_urb(victim, anchor);
+ }
+ spin_unlock_irqrestore(&anchor->lock, flags);
+ } while (!usb_anchor_safe_empty(anchor));
}
EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs);