@@ -12815,7 +12815,8 @@ static int nl80211_set_cqm_txe(struct genl_info *info,
1281512815}
1281612816
1281712817static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
12818- struct net_device *dev)
12818+ struct net_device *dev,
12819+ struct cfg80211_cqm_config *cqm_config)
1281912820{
1282012821 struct wireless_dev *wdev = dev->ieee80211_ptr;
1282112822 s32 last, low, high;
@@ -12824,7 +12825,7 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
1282412825 int err;
1282512826
1282612827 /* RSSI reporting disabled? */
12827- if (!wdev-> cqm_config)
12828+ if (!cqm_config)
1282812829 return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0);
1282912830
1283012831 /*
@@ -12833,7 +12834,7 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
1283312834 * connection is established and enough beacons received to calculate
1283412835 * the average.
1283512836 */
12836- if (!wdev-> cqm_config->last_rssi_event_value &&
12837+ if (!cqm_config->last_rssi_event_value &&
1283712838 wdev->links[0].client.current_bss &&
1283812839 rdev->ops->get_station) {
1283912840 struct station_info sinfo = {};
@@ -12847,30 +12848,30 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
1284712848
1284812849 cfg80211_sinfo_release_content(&sinfo);
1284912850 if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG))
12850- wdev-> cqm_config->last_rssi_event_value =
12851+ cqm_config->last_rssi_event_value =
1285112852 (s8) sinfo.rx_beacon_signal_avg;
1285212853 }
1285312854
12854- last = wdev-> cqm_config->last_rssi_event_value;
12855- hyst = wdev-> cqm_config->rssi_hyst;
12856- n = wdev-> cqm_config->n_rssi_thresholds;
12855+ last = cqm_config->last_rssi_event_value;
12856+ hyst = cqm_config->rssi_hyst;
12857+ n = cqm_config->n_rssi_thresholds;
1285712858
1285812859 for (i = 0; i < n; i++) {
1285912860 i = array_index_nospec(i, n);
12860- if (last < wdev-> cqm_config->rssi_thresholds[i])
12861+ if (last < cqm_config->rssi_thresholds[i])
1286112862 break;
1286212863 }
1286312864
1286412865 low_index = i - 1;
1286512866 if (low_index >= 0) {
1286612867 low_index = array_index_nospec(low_index, n);
12867- low = wdev-> cqm_config->rssi_thresholds[low_index] - hyst;
12868+ low = cqm_config->rssi_thresholds[low_index] - hyst;
1286812869 } else {
1286912870 low = S32_MIN;
1287012871 }
1287112872 if (i < n) {
1287212873 i = array_index_nospec(i, n);
12873- high = wdev-> cqm_config->rssi_thresholds[i] + hyst - 1;
12874+ high = cqm_config->rssi_thresholds[i] + hyst - 1;
1287412875 } else {
1287512876 high = S32_MAX;
1287612877 }
@@ -12883,6 +12884,7 @@ static int nl80211_set_cqm_rssi(struct genl_info *info,
1288312884 u32 hysteresis)
1288412885{
1288512886 struct cfg80211_registered_device *rdev = info->user_ptr[0];
12887+ struct cfg80211_cqm_config *cqm_config = NULL, *old;
1288612888 struct net_device *dev = info->user_ptr[1];
1288712889 struct wireless_dev *wdev = dev->ieee80211_ptr;
1288812890 int i, err;
@@ -12900,10 +12902,6 @@ static int nl80211_set_cqm_rssi(struct genl_info *info,
1290012902 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
1290112903 return -EOPNOTSUPP;
1290212904
12903- wdev_lock(wdev);
12904- cfg80211_cqm_config_free(wdev);
12905- wdev_unlock(wdev);
12906-
1290712905 if (n_thresholds <= 1 && rdev->ops->set_cqm_rssi_config) {
1290812906 if (n_thresholds == 0 || thresholds[0] == 0) /* Disabling */
1290912907 return rdev_set_cqm_rssi_config(rdev, dev, 0, 0);
@@ -12920,9 +12918,10 @@ static int nl80211_set_cqm_rssi(struct genl_info *info,
1292012918 n_thresholds = 0;
1292112919
1292212920 wdev_lock(wdev);
12923- if (n_thresholds) {
12924- struct cfg80211_cqm_config *cqm_config ;
12921+ old = rcu_dereference_protected(wdev->cqm_config,
12922+ lockdep_is_held(&wdev->mtx)) ;
1292512923
12924+ if (n_thresholds) {
1292612925 cqm_config = kzalloc(struct_size(cqm_config, rssi_thresholds,
1292712926 n_thresholds),
1292812927 GFP_KERNEL);
@@ -12937,11 +12936,18 @@ static int nl80211_set_cqm_rssi(struct genl_info *info,
1293712936 flex_array_size(cqm_config, rssi_thresholds,
1293812937 n_thresholds));
1293912938
12940- wdev->cqm_config = cqm_config;
12939+ rcu_assign_pointer(wdev->cqm_config, cqm_config);
12940+ } else {
12941+ RCU_INIT_POINTER(wdev->cqm_config, NULL);
1294112942 }
1294212943
12943- err = cfg80211_cqm_rssi_update(rdev, dev);
12944-
12944+ err = cfg80211_cqm_rssi_update(rdev, dev, cqm_config);
12945+ if (err) {
12946+ rcu_assign_pointer(wdev->cqm_config, old);
12947+ kfree_rcu(cqm_config, rcu_head);
12948+ } else {
12949+ kfree_rcu(old, rcu_head);
12950+ }
1294512951unlock:
1294612952 wdev_unlock(wdev);
1294712953
@@ -19092,28 +19098,50 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
1909219098 enum nl80211_cqm_rssi_threshold_event rssi_event,
1909319099 s32 rssi_level, gfp_t gfp)
1909419100{
19095- struct sk_buff *msg;
1909619101 struct wireless_dev *wdev = dev->ieee80211_ptr;
19097- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy) ;
19102+ struct cfg80211_cqm_config *cqm_config ;
1909819103
1909919104 trace_cfg80211_cqm_rssi_notify(dev, rssi_event, rssi_level);
1910019105
1910119106 if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
1910219107 rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
1910319108 return;
1910419109
19105- if (wdev->cqm_config) {
19106- wdev->cqm_config->last_rssi_event_value = rssi_level;
19110+ rcu_read_lock();
19111+ cqm_config = rcu_dereference(wdev->cqm_config);
19112+ if (cqm_config) {
19113+ cqm_config->last_rssi_event_value = rssi_level;
19114+ cqm_config->last_rssi_event_type = rssi_event;
19115+ wiphy_work_queue(wdev->wiphy, &wdev->cqm_rssi_work);
19116+ }
19117+ rcu_read_unlock();
19118+ }
19119+ EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
19120+
19121+ void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy, struct wiphy_work *work)
19122+ {
19123+ struct wireless_dev *wdev = container_of(work, struct wireless_dev,
19124+ cqm_rssi_work);
19125+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
19126+ enum nl80211_cqm_rssi_threshold_event rssi_event;
19127+ struct cfg80211_cqm_config *cqm_config;
19128+ struct sk_buff *msg;
19129+ s32 rssi_level;
1910719130
19108- cfg80211_cqm_rssi_update(rdev, dev);
19131+ wdev_lock(wdev);
19132+ cqm_config = rcu_dereference_protected(wdev->cqm_config,
19133+ lockdep_is_held(&wdev->mtx));
19134+ if (!wdev->cqm_config)
19135+ goto unlock;
1910919136
19110- if (rssi_level == 0)
19111- rssi_level = wdev->cqm_config->last_rssi_event_value;
19112- }
19137+ cfg80211_cqm_rssi_update(rdev, wdev->netdev, cqm_config);
1911319138
19114- msg = cfg80211_prepare_cqm(dev, NULL, gfp);
19139+ rssi_level = cqm_config->last_rssi_event_value;
19140+ rssi_event = cqm_config->last_rssi_event_type;
19141+
19142+ msg = cfg80211_prepare_cqm(wdev->netdev, NULL, GFP_KERNEL);
1911519143 if (!msg)
19116- return ;
19144+ goto unlock ;
1911719145
1911819146 if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
1911919147 rssi_event))
@@ -19123,14 +19151,15 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
1912319151 rssi_level))
1912419152 goto nla_put_failure;
1912519153
19126- cfg80211_send_cqm(msg, gfp );
19154+ cfg80211_send_cqm(msg, GFP_KERNEL );
1912719155
19128- return ;
19156+ goto unlock ;
1912919157
1913019158 nla_put_failure:
1913119159 nlmsg_free(msg);
19160+ unlock:
19161+ wdev_unlock(wdev);
1913219162}
19133- EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
1913419163
1913519164void cfg80211_cqm_txe_notify(struct net_device *dev,
1913619165 const u8 *peer, u32 num_packets,
0 commit comments