Skip to content

Commit

Permalink
wifi: cfg80211: fix CQM for non-range use
Browse files Browse the repository at this point in the history
commit 7e7efdd upstream.

[note: this is commit 4a7e925 reapplied;
that commit had been reverted in 6.6.6 because it caused regressions, see
https://lore.kernel.org/stable/2023121450-habitual-transpose-68a1@gregkh/
for details]

My prior race fix here broke CQM when ranges aren't used, as
the reporting worker now requires the cqm_config to be set in
the wdev, but isn't set when there's no range configured.

Rather than continuing to special-case the range version, set
the cqm_config always and configure accordingly, also tracking
if range was used or not to be able to clear the configuration
appropriately with the same API, which was actually not right
if both were implemented by a driver for some reason, as is
the case with mac80211 (though there the implementations are
equivalent so it doesn't matter.)

Also, the original multiple-RSSI commit lost checking for the
callback, so might have potentially crashed if a driver had
neither implementation, and userspace tried to use it despite
not being advertised as supported.

Cc: stable@vger.kernel.org
Fixes: 4a4b816 ("cfg80211: Accept multiple RSSI thresholds for CQM")
Fixes: 37c20b2 ("wifi: cfg80211: fix cqm_config access race")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Léo Lam <leo@leolam.fr>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Johannes Berg authored and Greg Kroah-Hartman committed Jan 1, 2024
1 parent 706448f commit 15577a9
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 19 deletions.
1 change: 1 addition & 0 deletions net/wireless/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ struct cfg80211_cqm_config {
u32 rssi_hyst;
s32 last_rssi_event_value;
enum nl80211_cqm_rssi_threshold_event last_rssi_event_type;
bool use_range_api;
int n_rssi_thresholds;
s32 rssi_thresholds[];
};
Expand Down
50 changes: 31 additions & 19 deletions net/wireless/nl80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -12574,10 +12574,6 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
int i, n, low_index;
int err;

/* RSSI reporting disabled? */
if (!cqm_config)
return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0);

/*
* Obtain current RSSI value if possible, if not and no RSSI threshold
* event has been received yet, we should receive an event after a
Expand Down Expand Up @@ -12652,25 +12648,27 @@ static int nl80211_set_cqm_rssi(struct genl_info *info,
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
return -EOPNOTSUPP;

if (n_thresholds <= 1 && rdev->ops->set_cqm_rssi_config) {
if (n_thresholds == 0 || thresholds[0] == 0) /* Disabling */
return rdev_set_cqm_rssi_config(rdev, dev, 0, 0);

return rdev_set_cqm_rssi_config(rdev, dev,
thresholds[0], hysteresis);
}

if (!wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_CQM_RSSI_LIST))
return -EOPNOTSUPP;

if (n_thresholds == 1 && thresholds[0] == 0) /* Disabling */
n_thresholds = 0;

wdev_lock(wdev);
old = rcu_dereference_protected(wdev->cqm_config,
lockdep_is_held(&wdev->mtx));

/* if already disabled just succeed */
if (!n_thresholds && !old)
return 0;

if (n_thresholds > 1) {
if (!wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_CQM_RSSI_LIST) ||
!rdev->ops->set_cqm_rssi_range_config)
return -EOPNOTSUPP;
} else {
if (!rdev->ops->set_cqm_rssi_config)
return -EOPNOTSUPP;
}

if (n_thresholds) {
cqm_config = kzalloc(struct_size(cqm_config, rssi_thresholds,
n_thresholds),
Expand All @@ -12685,13 +12683,26 @@ static int nl80211_set_cqm_rssi(struct genl_info *info,
memcpy(cqm_config->rssi_thresholds, thresholds,
flex_array_size(cqm_config, rssi_thresholds,
n_thresholds));
cqm_config->use_range_api = n_thresholds > 1 ||
!rdev->ops->set_cqm_rssi_config;

rcu_assign_pointer(wdev->cqm_config, cqm_config);

if (cqm_config->use_range_api)
err = cfg80211_cqm_rssi_update(rdev, dev, cqm_config);
else
err = rdev_set_cqm_rssi_config(rdev, dev,
thresholds[0],
hysteresis);
} else {
RCU_INIT_POINTER(wdev->cqm_config, NULL);
/* if enabled as range also disable via range */
if (old->use_range_api)
err = rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0);
else
err = rdev_set_cqm_rssi_config(rdev, dev, 0, 0);
}

err = cfg80211_cqm_rssi_update(rdev, dev, cqm_config);
if (err) {
rcu_assign_pointer(wdev->cqm_config, old);
kfree_rcu(cqm_config, rcu_head);
Expand Down Expand Up @@ -18758,10 +18769,11 @@ void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy, struct wiphy_work *work)
wdev_lock(wdev);
cqm_config = rcu_dereference_protected(wdev->cqm_config,
lockdep_is_held(&wdev->mtx));
if (!wdev->cqm_config)
if (!cqm_config)
goto unlock;

cfg80211_cqm_rssi_update(rdev, wdev->netdev, cqm_config);
if (cqm_config->use_range_api)
cfg80211_cqm_rssi_update(rdev, wdev->netdev, cqm_config);

rssi_level = cqm_config->last_rssi_event_value;
rssi_event = cqm_config->last_rssi_event_type;
Expand Down

0 comments on commit 15577a9

Please sign in to comment.