diff mbox

[1/7] wireless: Change single cqm_config to rssi config list

Message ID 1528886747-26342-2-git-send-email-tamizhr@codeaurora.org (mailing list archive)
State Changes Requested
Delegated to: Johannes Berg
Headers show

Commit Message

Tamizh chelvam June 13, 2018, 10:45 a.m. UTC
This patch changes single cqm_config into mac address based
rssi config list. This way the same structure can be
utilized by AP mode as well.

Signed-off-by: Tamizh chelvam <tamizhr@codeaurora.org>
---
 include/net/cfg80211.h |    8 ++++--
 net/wireless/core.c    |   29 +++++++++++++++----
 net/wireless/core.h    |    6 ++--
 net/wireless/nl80211.c |   74 ++++++++++++++++++++++++++++++++----------------
 4 files changed, 82 insertions(+), 35 deletions(-)

Comments

Johannes Berg June 29, 2018, 9:26 a.m. UTC | #1
On Wed, 2018-06-13 at 16:15 +0530, Tamizh chelvam wrote:
> 
> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
> index 5fbfe61..3e123a3 100644
> --- a/include/net/cfg80211.h
> +++ b/include/net/cfg80211.h
> @@ -4139,7 +4139,7 @@ static inline struct wiphy *wiphy_new(const struct cfg80211_ops *ops,
>  struct cfg80211_conn;
>  struct cfg80211_internal_bss;
>  struct cfg80211_cached_keys;
> -struct cfg80211_cqm_config;
> +struct cfg80211_rssi_config;

I think something like rssi_mon_config would be better? Just _rssi_
could be interpreted in many ways.
 
> -	struct cfg80211_cqm_config *cqm_config;
> +	struct cfg80211_rssi_config *rssi_config;
> +	struct list_head rssi_config_list;

Why do you need both now? Perhaps instead you should allow a NULL/all-
ones MAC address for where you have the direct pointer now, and remove
that? You anyway need to pass something to the peer argument in

> -void cfg80211_cqm_config_free(struct wireless_dev *wdev)
> +void cfg80211_rssi_config_free(struct wireless_dev *wdev, const u8 *peer)

even when the pointer is used.

> -	kfree(wdev->cqm_config);
> -	wdev->cqm_config = NULL;
> +	struct cfg80211_rssi_config *rssi_config, *tmp;
> +
> +	if (list_empty(&wdev->rssi_config_list))
> +		goto free;
> +
> +	list_for_each_entry_safe(rssi_config, tmp, &wdev->rssi_config_list,
> +				 list) {
> +		if (peer && memcmp(rssi_config->addr, peer, ETH_ALEN))
> +			continue;
> +
> +		list_del(&rssi_config->list);
> +		kfree(rssi_config);
> +		if (list_empty(&wdev->rssi_config_list) || peer)
> +			goto out;
> +	}

That logic could use some comments if we even decide to keep it this
way.

NULL means "free all"?

What's the locking scheme for this? It's way more complex now so perhaps
stick ASSERT_RTNL() in there or so?

> @@ -10140,7 +10141,7 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
>  	int err;
>  
>  	/* RSSI reporting disabled? */
> -	if (!wdev->cqm_config)
> +	if (!wdev->rssi_config)
>  		return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0);


So I guess the wdev->rssi_config is used for the case of "always use
this config for any AP you're connected to" or so - but maybe it'd be
better to track the AP as a station? Then again, I guess we can't force
userspace to change that.

> +static struct cfg80211_rssi_config *
> +cfg80211_get_rssi_config(struct wireless_dev *wdev, const s32 *thresholds,
> +			 int n_thresholds, u32 hysteresis, const u8 *peer)
> +{
> +	struct cfg80211_rssi_config *rssi_config;
> +
> +	if (!peer)
> +		return NULL;
> +
> +	if (list_empty(&wdev->rssi_config_list))
> +		goto new;
> +
> +	list_for_each_entry(rssi_config, &wdev->rssi_config_list, list) {
> +		if (!memcmp(rssi_config->addr, peer, ETH_ALEN))
> +			goto found;
> +	}
> +
> +new:
> +	rssi_config = kzalloc(sizeof(struct cfg80211_rssi_config) +
> +			      n_thresholds * sizeof(s32), GFP_KERNEL);
> +	list_add(&rssi_config->list, &wdev->rssi_config_list);

Why does "get" always imply "create"?

>  	wdev_lock(wdev);
>  	if (n_thresholds) {
> -		struct cfg80211_cqm_config *cqm_config;
> -
> -		cqm_config = kzalloc(sizeof(struct cfg80211_cqm_config) +
> -				     n_thresholds * sizeof(s32), GFP_KERNEL);
> -		if (!cqm_config) {
> +		wdev->rssi_config = cfg80211_get_rssi_config(
> +						wdev, thresholds,
> +						n_thresholds, hysteresis,
> +						wdev->current_bss->pub.bssid);

Here you link it to the BSSID anyway, but do we always have a
current_bss at this point?

johannes
Tamizh chelvam July 4, 2018, 6:16 p.m. UTC | #2
On 2018-06-29 14:56, Johannes Berg wrote:
> On Wed, 2018-06-13 at 16:15 +0530, Tamizh chelvam wrote:
>> 
>> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
>> index 5fbfe61..3e123a3 100644
>> --- a/include/net/cfg80211.h
>> +++ b/include/net/cfg80211.h
>> @@ -4139,7 +4139,7 @@ static inline struct wiphy *wiphy_new(const 
>> struct cfg80211_ops *ops,
>>  struct cfg80211_conn;
>>  struct cfg80211_internal_bss;
>>  struct cfg80211_cached_keys;
>> -struct cfg80211_cqm_config;
>> +struct cfg80211_rssi_config;
> 
> I think something like rssi_mon_config would be better? Just _rssi_
> could be interpreted in many ways.
> 
Okay sure
>> -	struct cfg80211_cqm_config *cqm_config;
>> +	struct cfg80211_rssi_config *rssi_config;
>> +	struct list_head rssi_config_list;
> 
> Why do you need both now? Perhaps instead you should allow a NULL/all-
> ones MAC address for where you have the direct pointer now, and remove
> that? You anyway need to pass something to the peer argument in
> 
In the current cqm/sta_mon implementation the range_config will be 
updated before notify event posted to userspace.
To update the range config for a specific station we need to have this 
peer addr based configuration list which holds the thresholds info
to choose the next rssi range.
Or do you want me to handle and store the rssi_config structure in 
mac80211 and update it in mac80211 itself ? And simply pass the 
notification event to userspace application ?

>> -void cfg80211_cqm_config_free(struct wireless_dev *wdev)
>> +void cfg80211_rssi_config_free(struct wireless_dev *wdev, const u8 
>> *peer)
> 
> even when the pointer is used.
> 
>> -	kfree(wdev->cqm_config);
>> -	wdev->cqm_config = NULL;
>> +	struct cfg80211_rssi_config *rssi_config, *tmp;
>> +
>> +	if (list_empty(&wdev->rssi_config_list))
>> +		goto free;
>> +
>> +	list_for_each_entry_safe(rssi_config, tmp, &wdev->rssi_config_list,
>> +				 list) {
>> +		if (peer && memcmp(rssi_config->addr, peer, ETH_ALEN))
>> +			continue;
>> +
>> +		list_del(&rssi_config->list);
>> +		kfree(rssi_config);
>> +		if (list_empty(&wdev->rssi_config_list) || peer)
>> +			goto out;
>> +	}
> 
> That logic could use some comments if we even decide to keep it this
> way.
> 
> NULL means "free all"?
> 
Yes.
> What's the locking scheme for this? It's way more complex now so 
> perhaps
> stick ASSERT_RTNL() in there or so?
> 
Isn't wdev_lock enough ?
>> @@ -10140,7 +10141,7 @@ static int cfg80211_cqm_rssi_update(struct 
>> cfg80211_registered_device *rdev,
>>  	int err;
>> 
>>  	/* RSSI reporting disabled? */
>> -	if (!wdev->cqm_config)
>> +	if (!wdev->rssi_config)
>>  		return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0);
> 
> 
> So I guess the wdev->rssi_config is used for the case of "always use
> this config for any AP you're connected to" or so - but maybe it'd be
> better to track the AP as a station? Then again, I guess we can't force
> userspace to change that.
> 
Sorry I didn't get your point:( I didn't change anything in the station 
mode. Functionality remains
same for the station mode.

>> +static struct cfg80211_rssi_config *
>> +cfg80211_get_rssi_config(struct wireless_dev *wdev, const s32 
>> *thresholds,
>> +			 int n_thresholds, u32 hysteresis, const u8 *peer)
>> +{
>> +	struct cfg80211_rssi_config *rssi_config;
>> +
>> +	if (!peer)
>> +		return NULL;
>> +
>> +	if (list_empty(&wdev->rssi_config_list))
>> +		goto new;
>> +
>> +	list_for_each_entry(rssi_config, &wdev->rssi_config_list, list) {
>> +		if (!memcmp(rssi_config->addr, peer, ETH_ALEN))
>> +			goto found;
>> +	}
>> +
>> +new:
>> +	rssi_config = kzalloc(sizeof(struct cfg80211_rssi_config) +
>> +			      n_thresholds * sizeof(s32), GFP_KERNEL);
>> +	list_add(&rssi_config->list, &wdev->rssi_config_list);
> 
> Why does "get" always imply "create"?
> 
I'll rename the function.

>>  	wdev_lock(wdev);
>>  	if (n_thresholds) {
>> -		struct cfg80211_cqm_config *cqm_config;
>> -
>> -		cqm_config = kzalloc(sizeof(struct cfg80211_cqm_config) +
>> -				     n_thresholds * sizeof(s32), GFP_KERNEL);
>> -		if (!cqm_config) {
>> +		wdev->rssi_config = cfg80211_get_rssi_config(
>> +						wdev, thresholds,
>> +						n_thresholds, hysteresis,
>> +						wdev->current_bss->pub.bssid);
> 
> Here you link it to the BSSID anyway, but do we always have a
> current_bss at this point?
> 
Oops! I didn't think about that condition. I'll fix that in the next 
version.
Johannes Berg July 6, 2018, 11:46 a.m. UTC | #3
On Wed, 2018-07-04 at 23:46 +0530, Tamizh chelvam wrote:

> > > -	struct cfg80211_cqm_config *cqm_config;
> > > +	struct cfg80211_rssi_config *rssi_config;
> > > +	struct list_head rssi_config_list;
> > 
> > Why do you need both now? Perhaps instead you should allow a NULL/all-
> > ones MAC address for where you have the direct pointer now, and remove
> > that? You anyway need to pass something to the peer argument in
> > 
> 
> In the current cqm/sta_mon implementation the range_config will be 
> updated before notify event posted to userspace.
> To update the range config for a specific station we need to have this 
> peer addr based configuration list which holds the thresholds info
> to choose the next rssi range.
> Or do you want me to handle and store the rssi_config structure in 
> mac80211 and update it in mac80211 itself ? And simply pass the 
> notification event to userspace application ?

I don't have any issues with storing it in mac80211 - could attach it to
the station entry there?

Come to think of it, that might also clarify the lifetime rules. As it
is now, what if the station is removed? What are the lifetime rules for
a per-station configuration?

It would almost look like it stays here (or maybe I missed when you
remove it from the list when a station is removed), but that doesn't
seem like a good idea.

Please document the lifetime rules also in the API, and make sure you
implement them properly.

> > What's the locking scheme for this? It's way more complex now so 
> > perhaps stick ASSERT_RTNL() in there or so?
> > 
> 
> Isn't wdev_lock enough ?

I guess it should be :-) Maybe assert on that, also to document the
locking rules.

> > >  	/* RSSI reporting disabled? */
> > > -	if (!wdev->cqm_config)
> > > +	if (!wdev->rssi_config)
> > >  		return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0);
> > 
> > 
> > So I guess the wdev->rssi_config is used for the case of "always use
> > this config for any AP you're connected to" or so - but maybe it'd be
> > better to track the AP as a station? Then again, I guess we can't force
> > userspace to change that.

> Sorry I didn't get your point:( I didn't change anything in the station 
> mode. Functionality remains same for the station mode.

Right, that sort of goes back to my thoughts about lifetime rules too.
The rule for stations would be to have it deleted when the station is
deleted, but I guess the rule for client-mode is to keep the config
active even when roaming?

I was just thinking that you don't need to *store* it like that, you
could still - in client mode - store the configuration treating the AP
as a (peer) station, perhaps with an ff:ff:ff:ff:ff:ff MAC address or
something (instead of using the BSSID, to get lifetime rules adjusted
right).

But now that I'm thinking about how the lifetime rules differ ... that's
probably not worth it.

> > >  	wdev_lock(wdev);
> > >  	if (n_thresholds) {
> > > -		struct cfg80211_cqm_config *cqm_config;
> > > -
> > > -		cqm_config = kzalloc(sizeof(struct cfg80211_cqm_config) +
> > > -				     n_thresholds * sizeof(s32), GFP_KERNEL);
> > > -		if (!cqm_config) {
> > > +		wdev->rssi_config = cfg80211_get_rssi_config(
> > > +						wdev, thresholds,
> > > +						n_thresholds, hysteresis,
> > > +						wdev->current_bss->pub.bssid);
> > 
> > Here you link it to the BSSID anyway, but do we always have a
> > current_bss at this point?
> > 
> 
> Oops! I didn't think about that condition. I'll fix that in the next 
> version.

Again though - see above. I'm not sure it's even correct to link it to
the BSSID since until now, I think the configuration would remain across
roaming?

johannes
Tamizh chelvam July 11, 2018, 6:54 a.m. UTC | #4
On 2018-07-06 17:16, Johannes Berg wrote:
> On Wed, 2018-07-04 at 23:46 +0530, Tamizh chelvam wrote:
> 
>> > > -	struct cfg80211_cqm_config *cqm_config;
>> > > +	struct cfg80211_rssi_config *rssi_config;
>> > > +	struct list_head rssi_config_list;
>> >
>> > Why do you need both now? Perhaps instead you should allow a NULL/all-
>> > ones MAC address for where you have the direct pointer now, and remove
>> > that? You anyway need to pass something to the peer argument in
>> >
>> 
>> In the current cqm/sta_mon implementation the range_config will be
>> updated before notify event posted to userspace.
>> To update the range config for a specific station we need to have this
>> peer addr based configuration list which holds the thresholds info
>> to choose the next rssi range.
>> Or do you want me to handle and store the rssi_config structure in
>> mac80211 and update it in mac80211 itself ? And simply pass the
>> notification event to userspace application ?
> 
> I don't have any issues with storing it in mac80211 - could attach it 
> to
> the station entry there?
yes.
> 
> Come to think of it, that might also clarify the lifetime rules. As it
> is now, what if the station is removed? What are the lifetime rules for
> a per-station configuration?
If we go with mac80211 based approach then the lifetime of the 
configuration would be for the current connection.
> 
> It would almost look like it stays here (or maybe I missed when you
> remove it from the list when a station is removed), but that doesn't
> seem like a good idea.
> 
> Please document the lifetime rules also in the API, and make sure you
> implement them properly.
In AP mode, the lifetime would be for the station's current connection. 
It will be removed in case of roaming/disconnected.
> 
>> > What's the locking scheme for this? It's way more complex now so
>> > perhaps stick ASSERT_RTNL() in there or so?
>> >
>> 
>> Isn't wdev_lock enough ?
> 
> I guess it should be :-) Maybe assert on that, also to document the
> locking rules.
Sure
> 
>> > >  	/* RSSI reporting disabled? */
>> > > -	if (!wdev->cqm_config)
>> > > +	if (!wdev->rssi_config)
>> > >  		return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0);
>> >
>> >
>> > So I guess the wdev->rssi_config is used for the case of "always use
>> > this config for any AP you're connected to" or so - but maybe it'd be
>> > better to track the AP as a station? Then again, I guess we can't force
>> > userspace to change that.
> 
>> Sorry I didn't get your point:( I didn't change anything in the 
>> station
>> mode. Functionality remains same for the station mode.
> 
> Right, that sort of goes back to my thoughts about lifetime rules too.
> The rule for stations would be to have it deleted when the station is
> deleted, but I guess the rule for client-mode is to keep the config
> active even when roaming?
> 
> I was just thinking that you don't need to *store* it like that, you
> could still - in client mode - store the configuration treating the AP
> as a (peer) station, perhaps with an ff:ff:ff:ff:ff:ff MAC address or
> something (instead of using the BSSID, to get lifetime rules adjusted
> right).
> 
Going with mac80211 based storing configuration will remove these list 
implementation in the cfg80211.
So, there won't be any MAC address stored for client-mode in cqm_config 
structure.

> But now that I'm thinking about how the lifetime rules differ ... 
> that's
> probably not worth it.
> 
>> > >  	wdev_lock(wdev);
>> > >  	if (n_thresholds) {
>> > > -		struct cfg80211_cqm_config *cqm_config;
>> > > -
>> > > -		cqm_config = kzalloc(sizeof(struct cfg80211_cqm_config) +
>> > > -				     n_thresholds * sizeof(s32), GFP_KERNEL);
>> > > -		if (!cqm_config) {
>> > > +		wdev->rssi_config = cfg80211_get_rssi_config(
>> > > +						wdev, thresholds,
>> > > +						n_thresholds, hysteresis,
>> > > +						wdev->current_bss->pub.bssid);
>> >
>> > Here you link it to the BSSID anyway, but do we always have a
>> > current_bss at this point?
>> >
>> 
>> Oops! I didn't think about that condition. I'll fix that in the next
>> version.
> 
> Again though - see above. I'm not sure it's even correct to link it to
> the BSSID since until now, I think the configuration would remain 
> across
> roaming?
> 
As mentioned above storing configuration parameters in mac80211 will 
avoid this list implementation here.
So, no need of worrying about those roaming scenario know ?

Thanks,
Tamizh.
Johannes Berg Aug. 28, 2018, 9:02 a.m. UTC | #5
On Wed, 2018-07-11 at 12:24 +0530, Tamizh chelvam wrote:
> 
> > Again though - see above. I'm not sure it's even correct to link it to
> > the BSSID since until now, I think the configuration would remain 
> > across
> > roaming?
> > 
> 
> As mentioned above storing configuration parameters in mac80211 will 
> avoid this list implementation here.
> So, no need of worrying about those roaming scenario know ?

I think we should tie it to the station lifetime and have it
reconfigured on roaming etc., and if that means it's easier by storing
in mac80211 I have no objection.

johannes
diff mbox

Patch

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 5fbfe61..3e123a3 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4139,7 +4139,7 @@  static inline struct wiphy *wiphy_new(const struct cfg80211_ops *ops,
 struct cfg80211_conn;
 struct cfg80211_internal_bss;
 struct cfg80211_cached_keys;
-struct cfg80211_cqm_config;
+struct cfg80211_rssi_config;
 
 /**
  * struct wireless_dev - wireless device state
@@ -4204,7 +4204,8 @@  static inline struct wiphy *wiphy_new(const struct cfg80211_ops *ops,
  * @event_lock: (private) lock for event list
  * @owner_nlportid: (private) owner socket port ID
  * @nl_owner_dead: (private) owner socket went away
- * @cqm_config: (private) nl80211 RSSI monitor state
+ * @rssi_config: (private) nl80211 RSSI monitor state
+ * @rssi_config_list: (private) peer based list for rssi config
  */
 struct wireless_dev {
 	struct wiphy *wiphy;
@@ -4275,7 +4276,8 @@  struct wireless_dev {
 	} wext;
 #endif
 
-	struct cfg80211_cqm_config *cqm_config;
+	struct cfg80211_rssi_config *rssi_config;
+	struct list_head rssi_config_list;
 };
 
 static inline u8 *wdev_address(struct wireless_dev *wdev)
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 5fe35aa..62e496e 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -994,10 +994,28 @@  void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)
 }
 EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);
 
-void cfg80211_cqm_config_free(struct wireless_dev *wdev)
+void cfg80211_rssi_config_free(struct wireless_dev *wdev, const u8 *peer)
 {
-	kfree(wdev->cqm_config);
-	wdev->cqm_config = NULL;
+	struct cfg80211_rssi_config *rssi_config, *tmp;
+
+	if (list_empty(&wdev->rssi_config_list))
+		goto free;
+
+	list_for_each_entry_safe(rssi_config, tmp, &wdev->rssi_config_list,
+				 list) {
+		if (peer && memcmp(rssi_config->addr, peer, ETH_ALEN))
+			continue;
+
+		list_del(&rssi_config->list);
+		kfree(rssi_config);
+		if (list_empty(&wdev->rssi_config_list) || peer)
+			goto out;
+	}
+
+free:
+	kfree(wdev->rssi_config);
+out:
+	wdev->rssi_config = NULL;
 }
 
 void cfg80211_unregister_wdev(struct wireless_dev *wdev)
@@ -1027,7 +1045,7 @@  void cfg80211_unregister_wdev(struct wireless_dev *wdev)
 		break;
 	}
 
-	cfg80211_cqm_config_free(wdev);
+	cfg80211_rssi_config_free(wdev, NULL);
 }
 EXPORT_SYMBOL(cfg80211_unregister_wdev);
 
@@ -1163,6 +1181,7 @@  static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
 		spin_lock_init(&wdev->event_lock);
 		INIT_LIST_HEAD(&wdev->mgmt_registrations);
 		spin_lock_init(&wdev->mgmt_registrations_lock);
+		INIT_LIST_HEAD(&wdev->rssi_config_list);
 
 		/*
 		 * We get here also when the interface changes network namespaces,
@@ -1292,7 +1311,7 @@  static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
 			kzfree(wdev->wext.keys);
 #endif
 			flush_work(&wdev->disconnect_wk);
-			cfg80211_cqm_config_free(wdev);
+			cfg80211_rssi_config_free(wdev, NULL);
 		}
 		/*
 		 * synchronise (so that we won't find this netdev
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 63eb1b5..170a31b 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -260,10 +260,12 @@  struct cfg80211_beacon_registration {
 	u32 nlportid;
 };
 
-struct cfg80211_cqm_config {
+struct cfg80211_rssi_config {
+	struct list_head list;
 	u32 rssi_hyst;
 	s32 last_rssi_event_value;
 	int n_rssi_thresholds;
+	u8 addr[ETH_ALEN];
 	s32 rssi_thresholds[0];
 };
 
@@ -514,6 +516,6 @@  void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
 #define CFG80211_DEV_WARN_ON(cond)	({bool __r = (cond); __r; })
 #endif
 
-void cfg80211_cqm_config_free(struct wireless_dev *wdev);
+void cfg80211_rssi_config_free(struct wireless_dev *wdev, const u8 *peer);
 
 #endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 07514ca..2fb2e97 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3200,6 +3200,7 @@  static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
 		spin_lock_init(&wdev->event_lock);
 		INIT_LIST_HEAD(&wdev->mgmt_registrations);
 		spin_lock_init(&wdev->mgmt_registrations_lock);
+		INIT_LIST_HEAD(&wdev->rssi_config_list);
 
 		wdev->identifier = ++rdev->wdev_id;
 		list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list);
@@ -10140,7 +10141,7 @@  static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
 	int err;
 
 	/* RSSI reporting disabled? */
-	if (!wdev->cqm_config)
+	if (!wdev->rssi_config)
 		return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0);
 
 	/*
@@ -10149,7 +10150,7 @@  static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
 	 * connection is established and enough beacons received to calculate
 	 * the average.
 	 */
-	if (!wdev->cqm_config->last_rssi_event_value && wdev->current_bss &&
+	if (!wdev->rssi_config->last_rssi_event_value && wdev->current_bss &&
 	    rdev->ops->get_station) {
 		struct station_info sinfo = {};
 		u8 *mac_addr;
@@ -10161,26 +10162,56 @@  static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
 			return err;
 
 		if (sinfo.filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG))
-			wdev->cqm_config->last_rssi_event_value =
+			wdev->rssi_config->last_rssi_event_value =
 				(s8) sinfo.rx_beacon_signal_avg;
 	}
 
-	last = wdev->cqm_config->last_rssi_event_value;
-	hyst = wdev->cqm_config->rssi_hyst;
-	n = wdev->cqm_config->n_rssi_thresholds;
+	last = wdev->rssi_config->last_rssi_event_value;
+	hyst = wdev->rssi_config->rssi_hyst;
+	n = wdev->rssi_config->n_rssi_thresholds;
 
 	for (i = 0; i < n; i++)
-		if (last < wdev->cqm_config->rssi_thresholds[i])
+		if (last < wdev->rssi_config->rssi_thresholds[i])
 			break;
 
 	low = i > 0 ?
-		(wdev->cqm_config->rssi_thresholds[i - 1] - hyst) : S32_MIN;
+		(wdev->rssi_config->rssi_thresholds[i - 1] - hyst) : S32_MIN;
 	high = i < n ?
-		(wdev->cqm_config->rssi_thresholds[i] + hyst - 1) : S32_MAX;
+		(wdev->rssi_config->rssi_thresholds[i] + hyst - 1) : S32_MAX;
 
 	return rdev_set_cqm_rssi_range_config(rdev, dev, low, high);
 }
 
+static struct cfg80211_rssi_config *
+cfg80211_get_rssi_config(struct wireless_dev *wdev, const s32 *thresholds,
+			 int n_thresholds, u32 hysteresis, const u8 *peer)
+{
+	struct cfg80211_rssi_config *rssi_config;
+
+	if (!peer)
+		return NULL;
+
+	if (list_empty(&wdev->rssi_config_list))
+		goto new;
+
+	list_for_each_entry(rssi_config, &wdev->rssi_config_list, list) {
+		if (!memcmp(rssi_config->addr, peer, ETH_ALEN))
+			goto found;
+	}
+
+new:
+	rssi_config = kzalloc(sizeof(struct cfg80211_rssi_config) +
+			      n_thresholds * sizeof(s32), GFP_KERNEL);
+	list_add(&rssi_config->list, &wdev->rssi_config_list);
+found:
+	rssi_config->rssi_hyst = hysteresis;
+	rssi_config->n_rssi_thresholds = n_thresholds;
+	memcpy(rssi_config->addr, peer, ETH_ALEN);
+	memcpy(rssi_config->rssi_thresholds, thresholds,
+	       n_thresholds * sizeof(s32));
+	return rssi_config;
+}
+
 static int nl80211_set_cqm_rssi(struct genl_info *info,
 				const s32 *thresholds, int n_thresholds,
 				u32 hysteresis)
@@ -10204,7 +10235,7 @@  static int nl80211_set_cqm_rssi(struct genl_info *info,
 		return -EOPNOTSUPP;
 
 	wdev_lock(wdev);
-	cfg80211_cqm_config_free(wdev);
+	cfg80211_rssi_config_free(wdev, NULL);
 	wdev_unlock(wdev);
 
 	if (n_thresholds <= 1 && rdev->ops->set_cqm_rssi_config) {
@@ -10224,21 +10255,14 @@  static int nl80211_set_cqm_rssi(struct genl_info *info,
 
 	wdev_lock(wdev);
 	if (n_thresholds) {
-		struct cfg80211_cqm_config *cqm_config;
-
-		cqm_config = kzalloc(sizeof(struct cfg80211_cqm_config) +
-				     n_thresholds * sizeof(s32), GFP_KERNEL);
-		if (!cqm_config) {
+		wdev->rssi_config = cfg80211_get_rssi_config(
+						wdev, thresholds,
+						n_thresholds, hysteresis,
+						wdev->current_bss->pub.bssid);
+		if (!wdev->rssi_config) {
 			err = -ENOMEM;
 			goto unlock;
 		}
-
-		cqm_config->rssi_hyst = hysteresis;
-		cqm_config->n_rssi_thresholds = n_thresholds;
-		memcpy(cqm_config->rssi_thresholds, thresholds,
-		       n_thresholds * sizeof(s32));
-
-		wdev->cqm_config = cqm_config;
 	}
 
 	err = cfg80211_cqm_rssi_update(rdev, dev);
@@ -15053,13 +15077,13 @@  void cfg80211_cqm_rssi_notify(struct net_device *dev,
 		    rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
 		return;
 
-	if (wdev->cqm_config) {
-		wdev->cqm_config->last_rssi_event_value = rssi_level;
+	if (wdev->rssi_config) {
+		wdev->rssi_config->last_rssi_event_value = rssi_level;
 
 		cfg80211_cqm_rssi_update(rdev, dev);
 
 		if (rssi_level == 0)
-			rssi_level = wdev->cqm_config->last_rssi_event_value;
+			rssi_level = wdev->rssi_config->last_rssi_event_value;
 	}
 
 	msg = cfg80211_prepare_cqm(dev, NULL, gfp);