From 400f6ebbc175286576c7f7fddf3c347d09d12310 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Dec 2023 11:13:34 +0100 Subject: [PATCH 1/4] wifi: iwlwifi: pcie: don't synchronize IRQs from IRQ On older devices (before unified image!) we can end up calling stop_device from an rfkill interrupt. However, in stop_device we attempt to synchronize IRQs, which then of course deadlocks. Avoid this by checking the context, if running from the IRQ thread then don't synchronize. This wouldn't be correct on a new device since RSS is supported, but older devices only have a single interrupt/queue. Fixes: 37fb29bd1f90 ("wifi: iwlwifi: pcie: synchronize IRQs before NAPI") Reviewed-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg Signed-off-by: Kalle Valo Link: https://msgid.link/20231215111335.59aab00baed7.Iadfe154d6248e7f9dfd69522e5429dbbd72925d7@changeid --- .../net/wireless/intel/iwlwifi/pcie/internal.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 8 ++++---- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 17 +++++++++-------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 56def20374f3..7805a42948af 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -770,7 +770,7 @@ static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) } } -void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans); +void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans, bool from_irq); static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) { @@ -817,7 +817,7 @@ static inline bool iwl_pcie_dbg_on(struct iwl_trans *trans) return (trans->dbg.dest_tlv || iwl_trans_dbg_ini_valid(trans)); } -void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state); +void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq); void iwl_trans_pcie_dump_regs(struct iwl_trans *trans); #ifdef CONFIG_IWLWIFI_DEBUGFS diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index bc6a9f861711..07931c2db494 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1783,7 +1783,7 @@ static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans) return inta; } -void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans) +void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans, bool from_irq) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct isr_statistics *isr_stats = &trans_pcie->isr_stats; @@ -1807,7 +1807,7 @@ void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans) isr_stats->rfkill++; if (prev != report) - iwl_trans_pcie_rf_kill(trans, report); + iwl_trans_pcie_rf_kill(trans, report, from_irq); mutex_unlock(&trans_pcie->mutex); if (hw_rfkill) { @@ -1947,7 +1947,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) /* HW RF KILL switch toggled */ if (inta & CSR_INT_BIT_RF_KILL) { - iwl_pcie_handle_rfkill_irq(trans); + iwl_pcie_handle_rfkill_irq(trans, true); handled |= CSR_INT_BIT_RF_KILL; } @@ -2370,7 +2370,7 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) /* HW RF KILL switch toggled */ if (inta_hw & MSIX_HW_INT_CAUSES_REG_RF_KILL) - iwl_pcie_handle_rfkill_irq(trans); + iwl_pcie_handle_rfkill_irq(trans, true); if (inta_hw & MSIX_HW_INT_CAUSES_REG_HW_ERR) { IWL_ERR(trans, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 92253260f568..d10208075ae5 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1082,7 +1082,7 @@ bool iwl_pcie_check_hw_rf_kill(struct iwl_trans *trans) report = test_bit(STATUS_RFKILL_OPMODE, &trans->status); if (prev != report) - iwl_trans_pcie_rf_kill(trans, report); + iwl_trans_pcie_rf_kill(trans, report, false); return hw_rfkill; } @@ -1237,7 +1237,7 @@ static void iwl_pcie_init_msix(struct iwl_trans_pcie *trans_pcie) trans_pcie->hw_mask = trans_pcie->hw_init_mask; } -static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans) +static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool from_irq) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1264,7 +1264,8 @@ 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_synchronize_irqs(trans); + if (!from_irq) + iwl_pcie_synchronize_irqs(trans); iwl_pcie_rx_napi_sync(trans); iwl_pcie_tx_stop(trans); iwl_pcie_rx_stop(trans); @@ -1454,7 +1455,7 @@ void iwl_trans_pcie_handle_stop_rfkill(struct iwl_trans *trans, clear_bit(STATUS_RFKILL_OPMODE, &trans->status); } if (hw_rfkill != was_in_rfkill) - iwl_trans_pcie_rf_kill(trans, hw_rfkill); + iwl_trans_pcie_rf_kill(trans, hw_rfkill, false); } static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) @@ -1469,12 +1470,12 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) mutex_lock(&trans_pcie->mutex); trans_pcie->opmode_down = true; was_in_rfkill = test_bit(STATUS_RFKILL_OPMODE, &trans->status); - _iwl_trans_pcie_stop_device(trans); + _iwl_trans_pcie_stop_device(trans, false); iwl_trans_pcie_handle_stop_rfkill(trans, was_in_rfkill); mutex_unlock(&trans_pcie->mutex); } -void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state) +void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq) { struct iwl_trans_pcie __maybe_unused *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1487,7 +1488,7 @@ void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state) if (trans->trans_cfg->gen2) _iwl_trans_pcie_gen2_stop_device(trans); else - _iwl_trans_pcie_stop_device(trans); + _iwl_trans_pcie_stop_device(trans, from_irq); } } @@ -2887,7 +2888,7 @@ static ssize_t iwl_dbgfs_rfkill_write(struct file *file, IWL_WARN(trans, "changing debug rfkill %d->%d\n", trans_pcie->debug_rfkill, new_value); trans_pcie->debug_rfkill = new_value; - iwl_pcie_handle_rfkill_irq(trans); + iwl_pcie_handle_rfkill_irq(trans, false); return count; } From 044879ce54069cf5f0efe7b4e67200d92e23a7da Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Mon, 18 Dec 2023 13:11:05 +0100 Subject: [PATCH 2/4] MAINTAINERS: wifi: brcm80211: remove non-existing SHA-cyfmac-dev-list@infineon.com When sending an email to SHA-cyfmac-dev-list@infineon.com, the server responds '550 #5.1.0 Address rejected.' Signed-off-by: Lukas Bulwahn Signed-off-by: Kalle Valo Link: https://msgid.link/20231218121105.23882-1-lukas.bulwahn@gmail.com --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 509281e9e169..f72e21ff40fb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4138,7 +4138,6 @@ M: Franky Lin M: Hante Meuleman L: linux-wireless@vger.kernel.org L: brcm80211-dev-list.pdl@broadcom.com -L: SHA-cyfmac-dev-list@infineon.com S: Supported F: drivers/net/wireless/broadcom/brcm80211/ From 8c917f16eb6198073ff8bc5d5c83a1bef9fae813 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 20 Dec 2023 04:38:00 +0200 Subject: [PATCH 3/4] wifi: mac80211: do not re-add debugfs entries during resume The driver debugfs entries still exist when the interface is re-added during reconfiguration. This can be either because of a HW restart (in_reconfig) or because we are resuming. Fixes: a1f5dcb1c0c1 ("wifi: mac80211: add a driver callback to add vif debugfs") Signed-off-by: Benjamin Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20231220043149.ddd48c66ec6b.Ia81080d92129ceecf462eceb4966bab80df12060@changeid Signed-off-by: Johannes Berg --- net/mac80211/driver-ops.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index d3820333cd59..5e0435db2f9c 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -77,7 +77,7 @@ int drv_add_interface(struct ieee80211_local *local, sdata->flags |= IEEE80211_SDATA_IN_DRIVER; - if (!local->in_reconfig) { + if (!local->in_reconfig && !local->resuming) { drv_vif_add_debugfs(local, sdata); /* initially vif is not MLD */ ieee80211_link_debugfs_drv_add(&sdata->deflink); @@ -534,7 +534,7 @@ int drv_change_vif_links(struct ieee80211_local *local, if (ret) return ret; - if (!local->in_reconfig) { + if (!local->in_reconfig && !local->resuming) { for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { link = rcu_access_pointer(sdata->link[link_id]); @@ -590,7 +590,7 @@ int drv_change_sta_links(struct ieee80211_local *local, return ret; /* during reconfig don't add it to debugfs again */ - if (local->in_reconfig) + if (local->in_reconfig || local->resuming) return 0; for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { From 0a3d898ee9a8303d5b3982b97ef0703919c3ea76 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 20 Dec 2023 04:38:01 +0200 Subject: [PATCH 4/4] wifi: mac80211: add/remove driver debugfs entries as appropriate When an interface is removed, we should also be deleting the driver debugfs entries (as it might still exist in DOWN state in mac80211). At the same time, when adding an interface, we can check the IEEE80211_SDATA_IN_DRIVER flag to know whether the interface was previously known to the driver and is simply being reconfigured. Fixes: a1f5dcb1c0c1 ("wifi: mac80211: add a driver callback to add vif debugfs") Signed-off-by: Benjamin Berg Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20231220043149.a9f64c359424.I7076526b5297ae8f832228079c999f7b8e147a4c@changeid Signed-off-by: Johannes Berg --- net/mac80211/debugfs_netdev.c | 9 ++++++--- net/mac80211/driver-ops.c | 10 +++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 80aeb25f1b68..dce5606ed66d 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -1043,9 +1043,12 @@ void ieee80211_debugfs_recreate_netdev(struct ieee80211_sub_if_data *sdata, { ieee80211_debugfs_remove_netdev(sdata); ieee80211_debugfs_add_netdev(sdata, mld_vif); - drv_vif_add_debugfs(sdata->local, sdata); - if (!mld_vif) - ieee80211_link_debugfs_drv_add(&sdata->deflink); + + if (sdata->flags & IEEE80211_SDATA_IN_DRIVER) { + drv_vif_add_debugfs(sdata->local, sdata); + if (!mld_vif) + ieee80211_link_debugfs_drv_add(&sdata->deflink); + } } void ieee80211_link_debugfs_add(struct ieee80211_link_data *link) diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index 5e0435db2f9c..3b7f70073fc3 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -75,9 +75,9 @@ int drv_add_interface(struct ieee80211_local *local, if (ret) return ret; - sdata->flags |= IEEE80211_SDATA_IN_DRIVER; + if (!(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) { + sdata->flags |= IEEE80211_SDATA_IN_DRIVER; - if (!local->in_reconfig && !local->resuming) { drv_vif_add_debugfs(local, sdata); /* initially vif is not MLD */ ieee80211_link_debugfs_drv_add(&sdata->deflink); @@ -113,9 +113,13 @@ void drv_remove_interface(struct ieee80211_local *local, if (!check_sdata_in_driver(sdata)) return; + sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; + + /* Remove driver debugfs entries */ + ieee80211_debugfs_recreate_netdev(sdata, sdata->vif.valid_links); + trace_drv_remove_interface(local, sdata); local->ops->remove_interface(&local->hw, &sdata->vif); - sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; trace_drv_return_void(local); }