wifi: iwlwifi: call napi_synchronize() before freeing rx/tx queues
authorGregory Greenman <gregory.greenman@intel.com>
Sun, 16 Apr 2023 12:47:26 +0000 (15:47 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 17 Apr 2023 07:53:23 +0000 (09:53 +0200)
When rx/tx queues are being freed, on a different CPU there could be
still rx flow running. Call napi_synchronize() to prevent such a race.

Signed-off-by: Gregory Greenman <gregory.greenman@intel.com>
Co-developed-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Link: https://lore.kernel.org/r/20230416154301.5171ee44dcc1.Iff18718540da412e084e7d8266447d40730600ed@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/intel/iwlwifi/pcie/internal.h
drivers/net/wireless/intel/iwlwifi/pcie/rx.c
drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c
drivers/net/wireless/intel/iwlwifi/pcie/trans.c

index f7e4f868363dffea9a06e3bdacb1d06a2451ba03..69b95ad5993b0acad217a4ab752825766c3a0add 100644 (file)
@@ -497,6 +497,7 @@ int iwl_pcie_rx_stop(struct iwl_trans *trans);
 void iwl_pcie_rx_free(struct iwl_trans *trans);
 void iwl_pcie_free_rbs_pool(struct iwl_trans *trans);
 void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq);
+void iwl_pcie_rx_napi_sync(struct iwl_trans *trans);
 void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority,
                            struct iwl_rxq *rxq);
 
index 9c9f87fe8377701c9e7eb5bb9de9620153cdf1b3..0d7890f99a5fbc3232a4d7b525845a4a0e56888e 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 /*
- * Copyright (C) 2003-2014, 2018-2022 Intel Corporation
+ * Copyright (C) 2003-2014, 2018-2023 Intel Corporation
  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
  * Copyright (C) 2016-2017 Intel Deutschland GmbH
  */
@@ -1053,6 +1053,22 @@ static int iwl_pcie_napi_poll_msix(struct napi_struct *napi, int budget)
        return ret;
 }
 
+void iwl_pcie_rx_napi_sync(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       int i;
+
+       if (unlikely(!trans_pcie->rxq))
+               return;
+
+       for (i = 0; i < trans->num_rx_queues; i++) {
+               struct iwl_rxq *rxq = &trans_pcie->rxq[i];
+
+               if (rxq && rxq->napi.poll)
+                       napi_synchronize(&rxq->napi);
+       }
+}
+
 static int _iwl_pcie_rx_init(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
index 45b63e3f5df3d49b9ca4131b3434f4882b092d0c..73b395841ca8a546f3c98e3f26d07e7a6c390ea4 100644 (file)
@@ -156,6 +156,7 @@ void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
        if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
                IWL_DEBUG_INFO(trans,
                               "DEVICE_ENABLED bit was set and is now cleared\n");
+               iwl_pcie_rx_napi_sync(trans);
                iwl_txq_gen2_tx_free(trans);
                iwl_pcie_rx_stop(trans);
        }
index 1b32a4035d88a4d5da1b5d6aede6888659f9f995..c5446b82c6c71b904062e29c559226b09860577c 100644 (file)
@@ -1260,6 +1260,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans)
        if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
                IWL_DEBUG_INFO(trans,
                               "DEVICE_ENABLED bit was set and is now cleared\n");
+               iwl_pcie_rx_napi_sync(trans);
                iwl_pcie_tx_stop(trans);
                iwl_pcie_rx_stop(trans);