-: ------------ > 1: f1b5a7ec462d wifi: cfg80211: reg: protect regdb pointer with RCU  1: b6810a2f7722 = 2: 9fa23fbfdd2e wifi: cfg80211: track netdev running state under wiphy mutex  2: 3d9fa076f171 = 3: 9631f154104e wifi: nl80211: allow device lookup under RCU  3: 4458683caf61 = 4: 804ba4179191 wifi: nl80211: avoid rtnl for commands that don't want it  4: 712f2da53063 = 5: becc5fbeaab5 wifi: nl80211: don't take rtnl for most dumps  5: a1079888862a = 6: 4d6c7afe0934 wifi: cfg80211: reg: update channels under wiphy mutex  6: 7e6e9cadb108 = 7: aedd147baebf wifi: cfg80211: reg: set intersected regd under wiphy mutex  7: a40f9d402723 = 8: 555c6aaf0f5c wifi: cfg80211: update channel DFS data under wiphy mutex  8: ce3f34b45219 = 9: fae0b258e679 wifi: mac80211_hwsim: call cfg80211 event with wiphy mutex  9: 35d1269d18a1 = 10: 7104e6112462 wifi: cfg80211: document wiphy mutex for radar/CAC events 10: f5111a08ef49 ! 11: 714b93153ec3 wifi: cfg80211: add a mutex for regulatory/device list @@ net/wireless/reg.c: static void crda_timeout_work(struct work_struct *work) rtnl_unlock(); }  -@@ net/wireless/reg.c: static int query_regdb(const char *alpha2) - const struct fwdb_header *hdr = regdb; - const struct fwdb_country *country; +@@ net/wireless/reg.c: static int regdb_query_country(const struct fwdb_header *db,  -- ASSERT_RTNL(); -+ lockdep_assert_held(&cfg80211_mutex); + static int query_regdb(const char *alpha2) + { +- const struct fwdb_header *regdb = rtnl_dereference(global_regdb); + const struct fwdb_country *country; ++ const struct fwdb_header *regdb; ++ ++ regdb = rcu_dereference_protected(global_regdb, ++ lockdep_rtnl_is_held() || ++ lockdep_is_held(&cfg80211_mutex));  if (IS_ERR(regdb)) return PTR_ERR(regdb); @@ net/wireless/reg.c: static void regdb_fw_cb(const struct firmware *fw, void *con }  rtnl_lock(); +- regdb = rtnl_dereference(global_regdb);  + mutex_lock(&cfg80211_mutex); ++ regdb = rcu_dereference_protected(global_regdb, ++ lockdep_rtnl_is_held() || ++ lockdep_is_held(&cfg80211_mutex)); if (regdb && !IS_ERR(regdb)) { /* negative case - a bug * positive case - can happen due to race in case of multiple cb's in @@ net/wireless/reg.c: static int query_regdb_file(const char *alpha2)  - ASSERT_RTNL();  + lockdep_assert_held(&cfg80211_mutex);  - if (regdb) + if (rcu_access_pointer(global_regdb)) return query_regdb(alpha2);  @@ net/wireless/reg.c: int reg_reload_regdb(void) }  rtnl_lock(); +- old = rcu_replace_pointer_rtnl(global_regdb, db);  + mutex_lock(&cfg80211_mutex); - if (!IS_ERR_OR_NULL(regdb)) - kfree(regdb); - regdb = db; ++ old = rcu_replace_pointer(global_regdb, db, ++ lockdep_rtnl_is_held() || ++ lockdep_is_held(&cfg80211_mutex)); +  + /* reset regulatory domain */ + current_regdomain = get_cfg80211_regdom();  @@ net/wireless/reg.c: int reg_reload_regdb(void) reg_process_hint(request);  out_unlock:  + mutex_unlock(&cfg80211_mutex); rtnl_unlock(); - out: - release_firmware(fw); +  + if (!IS_ERR_OR_NULL(old)) {  @@ net/wireless/reg.c: static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) struct cfg80211_registered_device *rdev; struct wiphy *wiphy; 11: 2fcd04e555e4 = 12: 59e57453375f wifi: cfg80211: allow walking wiphy list under cfg80211_mutex 12: ea0da3dd9e10 = 13: b6f7f4f7162a wifi: ath: use freq_reg_info() under RCU 13: 831f8693816e = 14: 2b5f7ececab9 wifi: brcmsmac: use freq_reg_info() under RCU 14: b8a29a175fdf = 15: c7f4bc128a0f wifi: rtlwifi: use freq_reg_info() under RCU 15: c3f438a74036 = 16: 91f3a79ca267 wifi: nl80211: read WMM reg rule under RCU 16: 564c77796083 = 17: 1a4c63487a09 wifi: ath11k: read wiphy regd under RCU 17: 9c36b4d5e059 = 18: 52e4aa7910f3 wifi: ath12k: read wiphy regd under RCU 18: add069d3573d ! 19: 431346d2e0d8 wifi: cfg80211: regulatory: stop using RTNL @@ Commit message  Signed-off-by: Johannes Berg   + ## drivers/net/wireless/ath/ath12k/reg.c ## +@@ drivers/net/wireless/ath/ath12k/reg.c: ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) + "failed set INIT Country code: %d\n", ret); + } +  +- wiphy_lock(wiphy); + ath12k_mac_11d_scan_stop(ar); +- wiphy_unlock(wiphy); +  + ar->regdom_set_by_user = true; + } + + ## drivers/net/wireless/realtek/rtw89/regd.c ## +@@ drivers/net/wireless/realtek/rtw89/regd.c: void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct rtw89_dev *rtwdev = hw->priv; +  +- wiphy_lock(wiphy); + rtw89_leave_ps_mode(rtwdev); +  + if (rtwdev->regulatory.programmed) +@@ drivers/net/wireless/realtek/rtw89/regd.c: void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request + rtw89_regd_apply_policy_ant_gain(rtwdev); +  + rtw89_core_set_chip_txpwr(rtwdev); +- +- wiphy_unlock(wiphy); + } +  + /* Maximum Transmit Power field (@raw) can be EIRP or PSD. + ## include/net/cfg80211.h ## +@@ include/net/cfg80211.h: struct wiphy_nan_capa { + * @mtx: mutex for the data (structures) of this device + * @reg_notifier: the driver's regulatory notification callback, + * note that if your driver uses wiphy_apply_custom_regulatory() +- * the reg_notifier's request can be passed as NULL ++ * the reg_notifier's request can be passed as NULL. ++ * This is called under wiphy mutex. + * @regd: the driver's regulatory domain, if one was requested via + * the regulatory_hint() API. This can be used by the driver + * on the reg_notifier() if it chooses to ignore future  @@ include/net/cfg80211.h: int regulatory_set_wiphy_regd(struct wiphy *wiphy, * @wiphy: the wireless device we want to process the regulatory domain on * @rd: the regulatory domain information to use for this wiphy @@ net/wireless/reg.c: static DECLARE_DELAYED_WORK(crda_timeout, crda_timeout_work) }  static void cancel_crda_timeout(void) +@@ net/wireless/reg.c: static int query_regdb(const char *alpha2) + const struct fwdb_header *regdb; +  + regdb = rcu_dereference_protected(global_regdb, +- lockdep_rtnl_is_held() || + lockdep_is_held(&cfg80211_mutex)); +  + if (IS_ERR(regdb))  @@ net/wireless/reg.c: static void regdb_fw_cb(const struct firmware *fw, void *context) set_error = -EINVAL; }   - rtnl_lock(); mutex_lock(&cfg80211_mutex); + regdb = rcu_dereference_protected(global_regdb, +- lockdep_rtnl_is_held() || + lockdep_is_held(&cfg80211_mutex)); if (regdb && !IS_ERR(regdb)) { /* negative case - a bug  @@ net/wireless/reg.c: static void regdb_fw_cb(const struct firmware *fw, void *context) @@ net/wireless/reg.c: int reg_reload_regdb(void)   - rtnl_lock(); mutex_lock(&cfg80211_mutex); - if (!IS_ERR_OR_NULL(regdb)) - kfree(regdb); + old = rcu_replace_pointer(global_regdb, db, +- lockdep_rtnl_is_held() || + lockdep_is_held(&cfg80211_mutex)); +  + /* reset regulatory domain */  @@ net/wireless/reg.c: int reg_reload_regdb(void)  out_unlock: mutex_unlock(&cfg80211_mutex);  - rtnl_unlock(); - out: - release_firmware(fw); - return err; +  + if (!IS_ERR_OR_NULL(old)) { + synchronize_rcu(); +@@ net/wireless/reg.c: static bool reg_is_world_roaming(struct wiphy *wiphy) + static void reg_call_notifier(struct wiphy *wiphy, + struct regulatory_request *request) + { ++ lockdep_assert_wiphy(wiphy); ++ + if (wiphy->reg_notifier) + wiphy->reg_notifier(wiphy, request); + } +@@ net/wireless/reg.c: static void wiphy_update_new_beacon(struct wiphy *wiphy, +  + sband = wiphy->bands[reg_beacon->chan.band]; +  +- scoped_guard(wiphy, wiphy) { +- for (i = 0; i < sband->n_channels; i++) +- changed |= handle_reg_beacon(wiphy, i, reg_beacon); +- } ++ guard(wiphy)(wiphy); ++ ++ for (i = 0; i < sband->n_channels; i++) ++ changed |= handle_reg_beacon(wiphy, i, reg_beacon); +  + if (changed && wiphy->flags & WIPHY_FLAG_CHANNEL_CHANGE_ON_BEACON) + reg_call_notifier(wiphy, get_last_request()); +@@ net/wireless/reg.c: static void wiphy_update_regulatory(struct wiphy *wiphy, + enum nl80211_band band; + struct regulatory_request *lr = get_last_request(); +  ++ lockdep_assert_wiphy(wiphy); ++ + if (ignore_reg_update(wiphy, initiator)) { + /* + * Regulatory updates set by CORE are ignored for custom +@@ net/wireless/reg.c: static void wiphy_update_regulatory(struct wiphy *wiphy, + lr->dfs_region = get_cfg80211_regdom()->dfs_region; +  + /* the notifier is called below, so ignore beacon hint changes */ +- scoped_guard(wiphy, wiphy) { +- for (band = 0; band < NUM_NL80211_BANDS; band++) +- handle_band(wiphy, initiator, wiphy->bands[band]); ++ for (band = 0; band < NUM_NL80211_BANDS; band++) ++ handle_band(wiphy, initiator, wiphy->bands[band]); +  +- reg_process_beacons(wiphy); +- reg_process_ht_flags(wiphy); +- } ++ reg_process_beacons(wiphy); ++ reg_process_ht_flags(wiphy); +  + reg_call_notifier(wiphy, lr); + } +@@ net/wireless/reg.c: static void wiphy_update_regulatory(struct wiphy *wiphy, + static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) + { + struct cfg80211_registered_device *rdev; +- struct wiphy *wiphy; +  + lockdep_assert_held(&cfg80211_mutex); +  + for_each_rdev(rdev) { +- wiphy = &rdev->wiphy; ++ struct wiphy *wiphy = &rdev->wiphy; ++ ++ guard(wiphy)(wiphy); ++ + wiphy_update_regulatory(wiphy, initiator); + } +   @@ net/wireless/reg.c: void wiphy_apply_custom_regulatory(struct wiphy *wiphy, if (IS_ERR(new_regd)) return; @@ net/wireless/reg.c: void wiphy_apply_custom_regulatory(struct wiphy *wiphy, } EXPORT_SYMBOL(wiphy_apply_custom_regulatory);  +@@ net/wireless/reg.c: static void wiphy_all_share_dfs_chan_state(struct wiphy *wiphy) + struct cfg80211_registered_device *rdev; +  + lockdep_assert_held(&cfg80211_mutex); +- +- guard(wiphy)(wiphy); ++ lockdep_assert_wiphy(wiphy); +  + for_each_rdev(rdev) { + if (wiphy == &rdev->wiphy) +@@ net/wireless/reg.c: static void reg_process_hint(struct regulatory_request *reg_request) + */ + if (treatment == REG_REQ_ALREADY_SET && wiphy && + wiphy->regulatory_flags & REGULATORY_STRICT_REG) { ++ guard(wiphy)(wiphy); ++ + wiphy_update_regulatory(wiphy, initiator); + wiphy_all_share_dfs_chan_state(wiphy); + reg_check_channels(); +@@ net/wireless/reg.c: static void reg_process_hint(struct regulatory_request *reg_request) + static void notify_self_managed_wiphys(struct regulatory_request *request) + { + struct cfg80211_registered_device *rdev; +- struct wiphy *wiphy; +  + for_each_rdev(rdev) { +- wiphy = &rdev->wiphy; ++ struct wiphy *wiphy = &rdev->wiphy; ++ + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && +- request->initiator == NL80211_REGDOM_SET_BY_USER) ++ request->initiator == NL80211_REGDOM_SET_BY_USER) { ++ guard(wiphy)(wiphy); ++ + reg_call_notifier(wiphy, request); ++ } + } + } +   @@ net/wireless/reg.c: static void reg_process_self_managed_hint(struct wiphy *wiphy) enum nl80211_band band; struct regulatory_request request = {}; @@ net/wireless/reg.c: int regulatory_set_wiphy_regd_sync(struct wiphy *wiphy,  ret = __regulatory_set_wiphy_regd(wiphy, rd); if (ret) +@@ net/wireless/reg.c: void wiphy_regulatory_register(struct wiphy *wiphy) +  + lockdep_assert_held(&cfg80211_mutex); +  +- /* self-managed devices ignore beacon hints and country IE */ +- if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) { +- wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS | +- REGULATORY_COUNTRY_IE_IGNORE; ++ scoped_guard(wiphy, wiphy) { ++ /* self-managed devices ignore beacon hints and country IE */ ++ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) { ++ wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS | ++ REGULATORY_COUNTRY_IE_IGNORE; +  +- /* +- * The last request may have been received before this +- * registration call. Call the driver notifier if +- * initiator is USER. +- */ +- if (lr->initiator == NL80211_REGDOM_SET_BY_USER) +- reg_call_notifier(wiphy, lr); +- } ++ /* ++ * The last request may have been received before this ++ * registration call. Call the driver notifier if ++ * initiator is USER. ++ */ ++ if (lr->initiator == NL80211_REGDOM_SET_BY_USER) ++ reg_call_notifier(wiphy, lr); ++ } +  +- if (!reg_dev_ignore_cell_hint(wiphy)) +- reg_num_devs_support_basehint++; ++ if (!reg_dev_ignore_cell_hint(wiphy)) ++ reg_num_devs_support_basehint++; ++ ++ wiphy_update_regulatory(wiphy, lr->initiator); ++ wiphy_all_share_dfs_chan_state(wiphy); ++ } +  +- wiphy_update_regulatory(wiphy, lr->initiator); +- wiphy_all_share_dfs_chan_state(wiphy); + reg_process_self_managed_hints(); + } +   @@ net/wireless/reg.c: void regulatory_exit(void) cancel_delayed_work_sync(®_check_chans);