From d078eae3ade297434429b86e4eb44f8f66aef712 Mon Sep 17 00:00:00 2001 From: Johannes Stezenbach Date: Mon, 13 Dec 2010 12:32:49 +0100 Subject: [PATCH] rt2x00: fix hang when unplugging USB device in use commit d7bb5f845f437662296adbfeaab8fbfce1c32289 upstream. When an rt2x00 USB device is unplugged while in use, it reliably hangs the whole system. After some time the watchdog prints: BUG: soft lockup - CPU#0 stuck for 64s! [kworker/u:0:5] ... [] (usb_submit_urb+0x0/0x2ac) from [] (rt2x00usb_kick_rx_entry+0xb4/0xe8 [rt2x00usb]) [] (rt2x00usb_kick_rx_entry+0x0/0xe8 [rt2x00usb]) from [] (rt2x00usb_clear_entry+x28/0x2c [rt2x00usb]) [] (rt2x00usb_clear_entry+0x0/0x2c [rt2x00usb]) from [] (rt2x00lib_rxdone+0x2e0/0x2f8 [rt2x00lib]) [] (rt2x00lib_rxdone+0x0/0x2f8 [rt2x00lib]) from [] (rt2x00usb_work_rxdone+0x54/0x74 [rt2x00usb]) [] (rt2x00usb_work_rxdone+0x0/0x74 [rt2x00usb]) from [] (process_one_work+0x20c/0x35c) Clear the DEVICE_STATE_PRESENT flag when usb_submit_urb() returns -ENODEV to fix this. Signed-off-by: Johannes Stezenbach Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville Cc: Stanislaw Gruszka Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rt2x00/rt2x00usb.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index a1084914706..051758fbfc3 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -235,6 +235,7 @@ static void rt2x00usb_kick_tx_entry(struct queue_entry *entry) struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); struct queue_entry_priv_usb *entry_priv = entry->priv_data; u32 length; + int status; if (!test_and_clear_bit(ENTRY_DATA_PENDING, &entry->flags)) return; @@ -251,7 +252,10 @@ static void rt2x00usb_kick_tx_entry(struct queue_entry *entry) entry->skb->data, length, rt2x00usb_interrupt_txdone, entry); - if (usb_submit_urb(entry_priv->urb, GFP_ATOMIC)) { + status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); + if (status) { + if (status == -ENODEV) + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); rt2x00lib_dmadone(entry); } @@ -450,6 +454,7 @@ void rt2x00usb_clear_entry(struct queue_entry *entry) to_usb_device_intf(entry->queue->rt2x00dev->dev); struct queue_entry_priv_usb *entry_priv = entry->priv_data; int pipe; + int status; entry->flags = 0; @@ -460,7 +465,12 @@ void rt2x00usb_clear_entry(struct queue_entry *entry) rt2x00usb_interrupt_rxdone, entry); set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); - if (usb_submit_urb(entry_priv->urb, GFP_ATOMIC)) { + + status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); + if (status) { + if (status == -ENODEV) + clear_bit(DEVICE_STATE_PRESENT, + &entry->queue->rt2x00dev->flags); set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); rt2x00lib_dmadone(entry); } -- 2.34.1