mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 13:14:02 +02:00
wifi: cfg80211: reduce RTNL holding in regulatory enforcement
Regulatory enforcement in reg_check_chans_work() does all work with the RTNL held, which can block the RTNL for a long time, which syzbot can hit and report hung tasks. Except for NAN, we don't need the RTNL for the enforcement, and the list iteration can be done with RCU instead. Split the enforcement off into new work structs: for NAN, we have to have the RTNL to close dependent NAN_DATA interfaces, everything else can use cfg80211_leave_locked() in a wiphy work. It'd be doable to use just a single work with RTNL, but then the RTNL would end up being used all the time, and really it only needs to be used for NAN. Assisted-by: LLM Reported-by: syzbot+adeb8550754921fece20@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=adeb8550754921fece20 Reported-by: syzbot+101224300649c3eb8af4@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=101224300649c3eb8af4 Link: https://patch.msgid.link/20260904165614.f65bd4d9fa35.I82dac71371d87f39e459fce931b0e5321e4f9767@changeid Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
17a5f8571d
commit
f4e72e3758
|
|
@ -645,6 +645,8 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
|
|||
INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
|
||||
wiphy_work_init(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk);
|
||||
INIT_WORK(&rdev->sched_scan_res_wk, cfg80211_sched_scan_results_wk);
|
||||
wiphy_work_init(&rdev->reg_check_chans_wk, reg_leave_invalid_chans_wk);
|
||||
INIT_WORK(&rdev->reg_leave_nan_wk, reg_leave_invalid_nan_wk);
|
||||
INIT_WORK(&rdev->propagate_radar_detect_wk,
|
||||
cfg80211_propagate_radar_detect_wk);
|
||||
INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk);
|
||||
|
|
@ -1344,6 +1346,7 @@ void wiphy_unregister(struct wiphy *wiphy)
|
|||
cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
|
||||
cancel_delayed_work_sync(&rdev->background_cac_done_wk);
|
||||
flush_work(&rdev->destroy_work);
|
||||
flush_work(&rdev->reg_leave_nan_wk);
|
||||
flush_work(&rdev->propagate_radar_detect_wk);
|
||||
flush_work(&rdev->propagate_cac_done_wk);
|
||||
flush_work(&rdev->mgmt_registrations_update_wk);
|
||||
|
|
|
|||
|
|
@ -114,6 +114,8 @@ struct cfg80211_registered_device {
|
|||
struct work_struct destroy_work;
|
||||
struct wiphy_work sched_scan_stop_wk;
|
||||
struct work_struct sched_scan_res_wk;
|
||||
struct wiphy_work reg_check_chans_wk;
|
||||
struct work_struct reg_leave_nan_wk;
|
||||
|
||||
struct cfg80211_chan_def radar_chandef;
|
||||
struct work_struct propagate_radar_detect_wk;
|
||||
|
|
|
|||
|
|
@ -2446,19 +2446,52 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void reg_leave_invalid_chans(struct wiphy *wiphy)
|
||||
void reg_leave_invalid_nan_wk(struct work_struct *work)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev;
|
||||
struct wireless_dev *wdev;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
|
||||
rdev = container_of(work, struct cfg80211_registered_device,
|
||||
reg_leave_nan_wk);
|
||||
|
||||
/* stopping NAN closes its data interfaces, which needs the RTNL */
|
||||
rtnl_lock();
|
||||
|
||||
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
|
||||
bool valid;
|
||||
|
||||
scoped_guard(wiphy, wiphy)
|
||||
valid = reg_wdev_chan_valid(wiphy, wdev);
|
||||
if (wdev->iftype != NL80211_IFTYPE_NAN)
|
||||
continue;
|
||||
|
||||
scoped_guard(wiphy, &rdev->wiphy)
|
||||
valid = reg_wdev_chan_valid(&rdev->wiphy, wdev);
|
||||
if (!valid)
|
||||
cfg80211_leave(rdev, wdev, -1);
|
||||
}
|
||||
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
struct wireless_dev *wdev;
|
||||
|
||||
lockdep_assert_held(&wiphy->mtx);
|
||||
|
||||
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
|
||||
if (reg_wdev_chan_valid(wiphy, wdev))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Tearing down NAN needs the RTNL for closing NAN_DATA
|
||||
* interfaces, handle that separately.
|
||||
*/
|
||||
if (wdev->iftype == NL80211_IFTYPE_NAN)
|
||||
schedule_work(&rdev->reg_leave_nan_wk);
|
||||
else
|
||||
cfg80211_leave_locked(rdev, wdev, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static void reg_check_chans_work(struct work_struct *work)
|
||||
|
|
@ -2466,12 +2499,13 @@ static void reg_check_chans_work(struct work_struct *work)
|
|||
struct cfg80211_registered_device *rdev;
|
||||
|
||||
pr_debug("Verifying active interfaces after reg change\n");
|
||||
rtnl_lock();
|
||||
|
||||
for_each_rdev(rdev)
|
||||
reg_leave_invalid_chans(&rdev->wiphy);
|
||||
rcu_read_lock();
|
||||
|
||||
rtnl_unlock();
|
||||
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
|
||||
wiphy_work_queue(&rdev->wiphy, &rdev->reg_check_chans_wk);
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
void reg_check_channels(void)
|
||||
|
|
|
|||
|
|
@ -178,6 +178,22 @@ int reg_reload_regdb(void);
|
|||
*/
|
||||
void reg_check_channels(void);
|
||||
|
||||
/**
|
||||
* reg_leave_invalid_chans_wk - check if channels are no longer usable and leave
|
||||
* @wiphy: the wiphy to check
|
||||
* @work: the work struct
|
||||
*/
|
||||
void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work);
|
||||
|
||||
/**
|
||||
* reg_leave_invalid_nan_wk - check channels and tear down NAN when unusable
|
||||
* @work: the work struct
|
||||
*
|
||||
* Stopping a NAN interface needs the RTNL, so it cannot be done from
|
||||
* reg_leave_invalid_chans_wk() which runs with the wiphy mutex held.
|
||||
*/
|
||||
void reg_leave_invalid_nan_wk(struct work_struct *work);
|
||||
|
||||
extern const u8 shipped_regdb_certs[];
|
||||
extern unsigned int shipped_regdb_certs_len;
|
||||
extern const u8 extra_regdb_certs[];
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user