wil6210: prevent parallel suspend and dump collection
authorMaya Erez <qca_merez@qca.qualcomm.com>
Thu, 14 Dec 2017 16:53:09 +0000 (18:53 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 9 Jan 2018 08:03:27 +0000 (10:03 +0200)
Suspend and crash dump operations can happen simultaneously
in case there is a FW assert during the suspend procedure
or when SSR calls all the devices crashdump callbacks.

To prevent that, a new flag is added, indicating that the
dumps collection is in progress, in order to allow the
suspend/reset decline if the dumps collection already started.

Signed-off-by: Maya Erez <qca_merez@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/pm.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wil_crash_dump.c

index 7a8f8c209af8b7ccc17f672afa34e47f61241bcc..aa6f9c4a21f1d8c479b0a767a816368f3f576e4a 100644 (file)
@@ -998,6 +998,7 @@ static void wil_pre_fw_config(struct wil6210_priv *wil)
 int wil_reset(struct wil6210_priv *wil, bool load_fw)
 {
        int rc;
+       unsigned long status_flags = BIT(wil_status_resetting);
 
        wil_dbg_misc(wil, "reset\n");
 
@@ -1037,6 +1038,14 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
        }
 
        set_bit(wil_status_resetting, wil->status);
+       if (test_bit(wil_status_collecting_dumps, wil->status)) {
+               /* Device collects crash dump, cancel the reset.
+                * following crash dump collection, reset would take place.
+                */
+               wil_dbg_misc(wil, "reject reset while collecting crash dump\n");
+               rc = -EBUSY;
+               goto out;
+       }
 
        cancel_work_sync(&wil->disconnect_worker);
        wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
@@ -1051,7 +1060,11 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
 
        /* prevent NAPI from being scheduled and prevent wmi commands */
        mutex_lock(&wil->wmi_mutex);
-       bitmap_zero(wil->status, wil_status_last);
+       if (test_bit(wil_status_suspending, wil->status))
+               status_flags |= BIT(wil_status_suspending);
+       bitmap_and(wil->status, wil->status, &status_flags,
+                  wil_status_last);
+       wil_dbg_misc(wil, "wil->status (0x%lx)\n", *wil->status);
        mutex_unlock(&wil->wmi_mutex);
 
        wil_mask_irq(wil);
@@ -1069,14 +1082,14 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
        wil_rx_fini(wil);
        if (rc) {
                wil_bl_crash_info(wil, true);
-               return rc;
+               goto out;
        }
 
        rc = wil_get_bl_info(wil);
        if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */
                rc = 0;
        if (rc)
-               return rc;
+               goto out;
 
        wil_set_oob_mode(wil, oob_mode);
        if (load_fw) {
@@ -1088,10 +1101,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
                /* Loading f/w from the file */
                rc = wil_request_firmware(wil, wil->wil_fw_name, true);
                if (rc)
-                       return rc;
+                       goto out;
                rc = wil_request_firmware(wil, WIL_BOARD_FILE_NAME, true);
                if (rc)
-                       return rc;
+                       goto out;
 
                wil_pre_fw_config(wil);
                wil_release_cpu(wil);
@@ -1103,6 +1116,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
        reinit_completion(&wil->wmi_call);
        reinit_completion(&wil->halp.comp);
 
+       clear_bit(wil_status_resetting, wil->status);
+
        if (load_fw) {
                wil_configure_interrupt_moderation(wil);
                wil_unmask_irq(wil);
@@ -1136,6 +1151,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
        }
 
        return rc;
+
+out:
+       clear_bit(wil_status_resetting, wil->status);
+       return rc;
 }
 
 void wil_fw_error_recovery(struct wil6210_priv *wil)
@@ -1241,9 +1260,7 @@ int __wil_down(struct wil6210_priv *wil)
        wil_abort_scan(wil, false);
        mutex_unlock(&wil->p2p_wdev_mutex);
 
-       wil_reset(wil, false);
-
-       return 0;
+       return wil_reset(wil, false);
 }
 
 int wil_down(struct wil6210_priv *wil)
index 056b180fad7fe477fdc7134a3fcf66cd85f04081..0a96518a566f20e6c010b7dc502545f3cc2524b3 100644 (file)
@@ -145,6 +145,13 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
 
        /* Prevent handling of new tx and wmi commands */
        set_bit(wil_status_suspending, wil->status);
+       if (test_bit(wil_status_collecting_dumps, wil->status)) {
+               /* Device collects crash dump, cancel the suspend */
+               wil_dbg_pm(wil, "reject suspend while collecting crash dump\n");
+               clear_bit(wil_status_suspending, wil->status);
+               wil->suspend_stats.rejected_by_host++;
+               return -EBUSY;
+       }
        wil_update_net_queues_bh(wil, NULL, true);
 
        if (!wil_is_tx_idle(wil)) {
@@ -255,6 +262,15 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil)
 
        wil_dbg_pm(wil, "suspend radio off\n");
 
+       set_bit(wil_status_suspending, wil->status);
+       if (test_bit(wil_status_collecting_dumps, wil->status)) {
+               /* Device collects crash dump, cancel the suspend */
+               wil_dbg_pm(wil, "reject suspend while collecting crash dump\n");
+               clear_bit(wil_status_suspending, wil->status);
+               wil->suspend_stats.rejected_by_host++;
+               return -EBUSY;
+       }
+
        /* if netif up, hardware is alive, shut it down */
        if (ndev->flags & IFF_UP) {
                rc = wil_down(wil);
@@ -281,6 +297,7 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil)
        set_bit(wil_status_suspended, wil->status);
 
 out:
+       clear_bit(wil_status_suspending, wil->status);
        wil_dbg_pm(wil, "suspend radio off: %d\n", rc);
 
        return rc;
index 346b600b27013ce090b653e8a90fba961df688e4..138df71e9ae80aba1d92ab627ed9baca30102963 100644 (file)
@@ -445,6 +445,7 @@ enum { /* for wil6210_priv.status */
        wil_status_suspending, /* suspend in progress */
        wil_status_suspended, /* suspend completed, device is suspended */
        wil_status_resuming, /* resume in progress */
+       wil_status_collecting_dumps, /* crashdump collection in progress */
        wil_status_last /* keep last */
 };
 
index e53cf0cf70315ed3b32b3851fc5724c985c00de2..1ed330674d9b8d5aa17e9647cf9a80c8787c2151 100644 (file)
@@ -72,6 +72,15 @@ int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size)
                return -EINVAL;
        }
 
+       set_bit(wil_status_collecting_dumps, wil->status);
+       if (test_bit(wil_status_suspending, wil->status) ||
+           test_bit(wil_status_suspended, wil->status) ||
+           test_bit(wil_status_resetting, wil->status)) {
+               wil_err(wil, "cannot collect fw dump during suspend/reset\n");
+               clear_bit(wil_status_collecting_dumps, wil->status);
+               return -EINVAL;
+       }
+
        /* copy to crash dump area */
        for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
                map = &fw_mapping[i];
@@ -91,6 +100,8 @@ int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size)
                                     (const void __iomem * __force)data, len);
        }
 
+       clear_bit(wil_status_collecting_dumps, wil->status);
+
        return 0;
 }