mwifiex: fix kernel crash after shutdown command timeout
authorBrian Norris <briannorris@chromium.org>
Thu, 16 Mar 2017 21:36:07 +0000 (14:36 -0700)
committerKalle Valo <kvalo@codeaurora.org>
Mon, 20 Mar 2017 17:09:20 +0000 (19:09 +0200)
We observed a SHUTDOWN command timeout during reboot stress test due to
a corner case firmware bug. It can lead to either a use-after-free +
OOPS (on either the adapter structure, or the 'card' structure) or an
abort (where, e.g., the PCI device is "disabled" before we're done
dumping the FW).

We can avoid this by canceling/flushing the FW dump work:

(a) after we've terminated all other work queues (e.g., for processing
    commands which could time out)
(b) after we've disabled all interrupts (which could also queue more
    work for us)
(c) after we've unregistered the netdev and wiphy structures (and
    implicitly, and debugfs entries which could manually trigger FW dumps)
(d) before we've actually disabled the device (e.g.,
    pci_device_disable())

Altogether, this means no card->work will be scheduled if we sync at
a point that satisfies the above. This can be done at the beginning of
the .cleanup_if() callback.

Signed-off-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/marvell/mwifiex/pcie.c
drivers/net/wireless/marvell/mwifiex/sdio.c

index 23e09117bf4abb55baec54b8076f3d833e9f6681..e381deff37f3a8b518336c391f64d78e90150981 100644 (file)
@@ -294,8 +294,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
        if (!adapter || !adapter->priv_num)
                return;
 
-       cancel_work_sync(&card->work);
-
        reg = card->pcie.reg;
        if (reg)
                ret = mwifiex_read_reg(adapter, reg->fw_status, &fw_status);
@@ -2854,6 +2852,8 @@ static void mwifiex_cleanup_pcie(struct mwifiex_adapter *adapter)
        int ret;
        u32 fw_status;
 
+       cancel_work_sync(&card->work);
+
        ret = mwifiex_read_reg(adapter, reg->fw_status, &fw_status);
        if (fw_status == FIRMWARE_READY_PCIE) {
                mwifiex_dbg(adapter, INFO,
index 9f7cabf0236b6216f27cae773ff2302c93c7efc7..58d3da09cfbd0bb33a4eeb142f35e3b487536979 100644 (file)
@@ -387,8 +387,6 @@ mwifiex_sdio_remove(struct sdio_func *func)
        if (!adapter || !adapter->priv_num)
                return;
 
-       cancel_work_sync(&card->work);
-
        mwifiex_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num);
 
        ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat);
@@ -2158,6 +2156,8 @@ static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter)
 {
        struct sdio_mmc_card *card = adapter->card;
 
+       cancel_work_sync(&card->work);
+
        kfree(card->mp_regs);
        kfree(card->mpa_rx.skb_arr);
        kfree(card->mpa_rx.len_arr);