Search Linux Wireless

[PATCH 3/3] mac80211: compute chanctx refcount dynamically

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



It doesn't make much sense to have a manually
managed chanctx refcount. It isn't performance
critical and it can't be even used outside the
protection of chanctx_mtx.

With future enhancements to channel contenxt
(namely reservations) refcount accounting will
become more complex and having manually managed
refcount is going to be error-prone and confusing.

The refcount is now equal to the number of vifs it
is assigned to. However in the future this will
change so keep the refcount/num_assigned_vifs
functions split now.

Signed-off-by: Michal Kazior <michal.kazior@xxxxxxxxx>
---
 net/mac80211/cfg.c         |  2 +-
 net/mac80211/chan.c        | 62 +++++++++++++++++++++++++++++++++++++---------
 net/mac80211/ieee80211_i.h |  5 +++-
 net/mac80211/mlme.c        |  2 +-
 4 files changed, 56 insertions(+), 15 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index a79875c..b5c9a77 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -3242,7 +3242,7 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
 
 	/* don't handle for multi-VIF cases */
 	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
-	if (chanctx->refcount > 1) {
+	if (ieee80211_chanctx_refcount(chanctx) > 1) {
 		mutex_unlock(&local->chanctx_mtx);
 		return -EBUSY;
 	}
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 5b946e8..a320dfd 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -9,6 +9,45 @@
 #include "ieee80211_i.h"
 #include "driver-ops.h"
 
+struct ieee80211_chanctx *
+ieee80211_vif_assigned_chanctx(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_chanctx_conf *conf;
+
+	lockdep_assert_held(&local->chanctx_mtx);
+
+	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+					 lockdep_is_held(&local->chanctx_mtx));
+	if (!conf)
+		return NULL;
+
+	return container_of(conf, struct ieee80211_chanctx, conf);
+}
+
+int ieee80211_chanctx_num_assigned_vifs(struct ieee80211_chanctx *ctx)
+{
+	struct ieee80211_local *local = ctx->local;
+	struct ieee80211_sub_if_data *sdata;
+	int num = 0;
+
+	lockdep_assert_held(&local->chanctx_mtx);
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sdata, &local->interfaces, list)
+		if (ieee80211_vif_assigned_chanctx(sdata) == ctx)
+			num++;
+	rcu_read_unlock();
+
+	return num;
+}
+
+int ieee80211_chanctx_refcount(struct ieee80211_chanctx *ctx)
+{
+	lockdep_assert_held(&ctx->local->chanctx_mtx);
+	return ieee80211_chanctx_num_assigned_vifs(ctx);
+}
+
 static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
 {
 	switch (sta->bandwidth) {
@@ -266,7 +305,7 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,
 	bool check_single_channel = false;
 	lockdep_assert_held(&local->chanctx_mtx);
 
-	WARN_ON_ONCE(ctx->refcount != 0);
+	WARN_ON_ONCE(ieee80211_chanctx_refcount(ctx) != 0);
 
 	if (!local->use_chanctx) {
 		struct cfg80211_chan_def *chandef = &local->_oper_chandef;
@@ -308,7 +347,6 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
 		return ret;
 
 	rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
-	ctx->refcount++;
 
 	ieee80211_recalc_txpower(sdata);
 	ieee80211_recalc_chanctx_min_def(local, ctx);
@@ -386,7 +424,6 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
 
 	lockdep_assert_held(&local->chanctx_mtx);
 
-	ctx->refcount--;
 	rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
 
 	sdata->vif.bss_conf.idle = true;
@@ -397,12 +434,13 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
 
 	drv_unassign_vif_chanctx(local, sdata, ctx);
 
-	if (ctx->refcount > 0) {
-		ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
-		ieee80211_recalc_smps_chanctx(local, ctx);
-		ieee80211_recalc_radar_chanctx(local, ctx);
-		ieee80211_recalc_chanctx_min_def(local, ctx);
-	}
+	if (ieee80211_chanctx_num_assigned_vifs(ctx) == 0)
+		return;
+
+	ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
+	ieee80211_recalc_smps_chanctx(local, ctx);
+	ieee80211_recalc_radar_chanctx(local, ctx);
+	ieee80211_recalc_chanctx_min_def(local, ctx);
 }
 
 static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
@@ -421,7 +459,7 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
 	ctx = container_of(conf, struct ieee80211_chanctx, conf);
 
 	ieee80211_unassign_vif_chanctx(sdata, ctx);
-	if (ctx->refcount == 0)
+	if (ieee80211_chanctx_refcount(ctx) == 0)
 		ieee80211_free_chanctx(local, ctx);
 }
 
@@ -536,7 +574,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
 	ret = ieee80211_assign_vif_chanctx(sdata, ctx);
 	if (ret) {
 		/* if assign fails refcount stays the same */
-		if (ctx->refcount == 0)
+		if (ieee80211_chanctx_refcount(ctx) == 0)
 			ieee80211_free_chanctx(local, ctx);
 		goto out;
 	}
@@ -577,7 +615,7 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
 	}
 
 	ctx = container_of(conf, struct ieee80211_chanctx, conf);
-	if (ctx->refcount != 1) {
+	if (ieee80211_chanctx_refcount(ctx) != 1) {
 		ret = -EINVAL;
 		goto out;
 	}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c21b0c3..11d70b5 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -693,7 +693,6 @@ struct ieee80211_chanctx {
 
 	struct ieee80211_local *local;
 	enum ieee80211_chanctx_mode mode;
-	int refcount;
 	bool driver_present;
 
 	struct ieee80211_chanctx_conf conf;
@@ -1788,6 +1787,10 @@ void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
 void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
 void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
 					 bool clear);
+struct ieee80211_chanctx *
+ieee80211_vif_assigned_chanctx(struct ieee80211_sub_if_data *sdata);
+int ieee80211_chanctx_num_assigned_vifs(struct ieee80211_chanctx *ctx);
+int ieee80211_chanctx_refcount(struct ieee80211_chanctx *ctx);
 
 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
 				   struct ieee80211_chanctx *chanctx);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 46b62bb..c0b2fb0 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1066,7 +1066,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 	}
 	chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf),
 			       struct ieee80211_chanctx, conf);
-	if (chanctx->refcount > 1) {
+	if (ieee80211_chanctx_refcount(chanctx) > 1) {
 		sdata_info(sdata,
 			   "channel switch with multiple interfaces on the same channel, disconnecting\n");
 		ieee80211_queue_work(&local->hw,
-- 
1.8.5.3

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux