wil6210: Fix kernel oops in reset flow
authorVladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Mon, 17 Mar 2014 13:34:18 +0000 (15:34 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 17 Mar 2014 17:44:18 +0000 (13:44 -0400)
wil_reset() removes vring's
At the same time NAPI may be active performing Rx/Tx completion.
If this happens, Rx/Tx polling functions going to access already removed vrings

Make sure NAPI is idle and won't be started prior to vring removal.
For this, track NAPI enabled state

Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/wil6210/interrupt.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/wil6210.h

index 52c40e1..201cf06 100644 (file)
@@ -195,8 +195,12 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
        if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
                wil_dbg_irq(wil, "RX done\n");
                isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
-               wil_dbg_txrx(wil, "NAPI schedule\n");
-               napi_schedule(&wil->napi_rx);
+               if (test_bit(wil_status_reset_done, &wil->status)) {
+                       wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
+                       napi_schedule(&wil->napi_rx);
+               } else {
+                       wil_err(wil, "Got Rx interrupt while in reset\n");
+               }
        }
 
        if (isr)
@@ -226,10 +230,15 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
 
        if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
                wil_dbg_irq(wil, "TX done\n");
-               napi_schedule(&wil->napi_tx);
                isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
                /* clear also all VRING interrupts */
                isr &= ~(BIT(25) - 1UL);
+               if (test_bit(wil_status_reset_done, &wil->status)) {
+                       wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");
+                       napi_schedule(&wil->napi_tx);
+               } else {
+                       wil_err(wil, "Got Tx interrupt while in reset\n");
+               }
        }
 
        if (isr)
index de952ab..0831d4c 100644 (file)
@@ -329,11 +329,16 @@ int wil_reset(struct wil6210_priv *wil)
 {
        int rc;
 
+       wil->status = 0; /* prevent NAPI from being scheduled */
+       if (test_bit(wil_status_napi_en, &wil->status)) {
+               napi_synchronize(&wil->napi_rx);
+               napi_synchronize(&wil->napi_tx);
+       }
+
        cancel_work_sync(&wil->disconnect_worker);
        wil6210_disconnect(wil, NULL);
 
        wil6210_disable_irq(wil);
-       wil->status = 0;
 
        wmi_event_flush(wil);
 
@@ -426,6 +431,7 @@ static int __wil_up(struct wil6210_priv *wil)
 
        napi_enable(&wil->napi_rx);
        napi_enable(&wil->napi_tx);
+       set_bit(wil_status_napi_en, &wil->status);
 
        return 0;
 }
@@ -443,6 +449,7 @@ int wil_up(struct wil6210_priv *wil)
 
 static int __wil_down(struct wil6210_priv *wil)
 {
+       clear_bit(wil_status_napi_en, &wil->status);
        napi_disable(&wil->napi_rx);
        napi_disable(&wil->napi_tx);
 
index f7d8f0e..89cbdc3 100644 (file)
@@ -249,6 +249,7 @@ enum { /* for wil6210_priv.status */
        wil_status_dontscan,
        wil_status_reset_done,
        wil_status_irqen, /* FIXME: interrupts enabled - for debug */
+       wil_status_napi_en, /* NAPI enabled protected by wil->mutex */
 };
 
 struct pci_dev;