Search Linux Wireless

[RFC 1/2] mac80211: Add vif hash for multi-station RX performance.

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

 



From: Ben Greear <greearb@xxxxxxxxxxxxxxx>

When one has multiple station VIFS all connected to the same
AP, the rx logic becomes a linear walk of all stations.  To
improve performance in this case, hash the station VIFs on
the VIF MAC.  This significantly improves performance:  70Mbps
without the patch, 190Mbps with patch, when using 50 stations
receiving TCP traffic.

Probably not many people doing this, so not worth pushing upstream
at this time.

Signed-off-by: Ben Greear <greearb@xxxxxxxxxxxxxxx>
---

Think I fixed the station matching issues Johannes mentioned.

:100644 100644 b985722... 84f1b73... M	net/mac80211/ieee80211_i.h
:100644 100644 c6844ad... a23a103... M	net/mac80211/rx.c
:100644 100644 c465b1d... 34351dd... M	net/mac80211/sta_info.c
:100644 100644 4947341... 227fce7... M	net/mac80211/sta_info.h
 net/mac80211/ieee80211_i.h |    3 +-
 net/mac80211/rx.c          |   15 +++++++++++++
 net/mac80211/sta_info.c    |   51 +++++++++++++++++++++++++++++++++++++++++--
 net/mac80211/sta_info.h    |    7 +++++-
 4 files changed, 71 insertions(+), 5 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index b985722..84f1b73 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -985,7 +985,8 @@ struct ieee80211_local {
 	spinlock_t tim_lock;
 	unsigned long num_sta;
 	struct list_head sta_list;
-	struct sta_info __rcu *sta_hash[STA_HASH_SIZE];
+	struct sta_info __rcu *sta_hash[STA_HASH_SIZE]; /* By station addr */
+	struct sta_info __rcu *sta_vhash[STA_HASH_SIZE]; /* By VIF mac addr */
 	struct timer_list sta_cleanup;
 	int sta_generation;
 
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index c6844ad..a23a103 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3152,6 +3152,20 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 	if (ieee80211_is_data(fc)) {
 		prev_sta = NULL;
 
+		/* Check for Station VIFS by hashing on the destination MAC
+		 * (ie, local sdata MAC).  This changes 'promisc' behaviour,
+		 * but not sure that is a bad thing.
+		 */
+		if ((!is_multicast_ether_addr(hdr->addr1)) &&
+		    (local->monitors == 0) && (local->cooked_mntrs == 0)) {
+			sta = sta_info_get_by_vif(local, hdr->addr1, hdr->addr2);
+			if (sta) {
+				rx.sta = sta;
+				rx.sdata = sta->sdata;
+				goto rx_and_done;
+			}
+		}
+
 		for_each_sta_info(local, hdr->addr2, sta, tmp) {
 			if (!prev_sta) {
 				prev_sta = sta;
@@ -3169,6 +3183,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 			rx.sta = prev_sta;
 			rx.sdata = prev_sta->sdata;
 
+		rx_and_done:
 			if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
 				return;
 			goto out;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index c465b1d..34351dd 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -77,19 +77,42 @@ static int sta_info_hash_del(struct ieee80211_local *local,
 	s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)],
 				      lockdep_is_held(&local->sta_mtx));
 	if (!s)
-		return -ENOENT;
+		goto try_lhash;
+	
 	if (s == sta) {
 		rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)],
 				   s->hnext);
-		return 0;
+		goto try_lhash;
 	}
 
 	while (rcu_access_pointer(s->hnext) &&
 	       rcu_access_pointer(s->hnext) != sta)
 		s = rcu_dereference_protected(s->hnext,
-					lockdep_is_held(&local->sta_mtx));
+					      lockdep_is_held(&local->sta_mtx));
 	if (rcu_access_pointer(s->hnext)) {
 		rcu_assign_pointer(s->hnext, sta->hnext);
+		goto try_lhash;
+	}
+
+	/* Remove from the local VIF addr hash */
+try_lhash:
+	s = rcu_dereference_protected(local->sta_vhash[STA_HASH(sta->sdata->vif.addr)],
+				      lockdep_is_held(&local->sta_mtx));
+	if (!s)
+		return -ENONET;
+	
+	if (s == sta) {
+		rcu_assign_pointer(local->sta_vhash[STA_HASH(sta->sdata->vif.addr)],
+				   s->vnext);
+		return 0;
+	}
+
+	while (rcu_access_pointer(s->vnext) &&
+	       rcu_access_pointer(s->vnext) != sta)
+		s = rcu_dereference_protected(s->vnext,
+					      lockdep_is_held(&local->sta_mtx));
+	if (rcu_access_pointer(s->vnext)) {
+		rcu_assign_pointer(s->vnext, sta->vnext);
 		return 0;
 	}
 
@@ -251,6 +274,23 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
 	return sta;
 }
 
+struct sta_info *sta_info_get_by_vif(struct ieee80211_local *local,
+				     const u8 *vif_addr, const u8 * sta_addr) {
+	struct sta_info *sta;
+
+	sta = rcu_dereference_check(local->sta_vhash[STA_HASH(vif_addr)],
+				    lockdep_is_held(&local->sta_mtx));
+	while (sta) {
+		if (ether_addr_equal(sta->sdata->vif.addr, vif_addr) &&
+		    ether_addr_equal(sta->sta.addr, sta_addr))
+			break;
+		sta = rcu_dereference_check(sta->vnext,
+					    lockdep_is_held(&local->sta_mtx));
+	}
+	return sta;
+}
+
+
 struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
 				     int idx)
 {
@@ -300,6 +340,11 @@ static void sta_info_hash_add(struct ieee80211_local *local,
 	sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)];
 	rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
 
+	if (sta->sdata->vif.type == NL80211_IFTYPE_STATION) {
+		sta->vnext = local->sta_vhash[STA_HASH(sta->sdata->vif.addr)];
+		rcu_assign_pointer(local->sta_vhash[STA_HASH(sta->sdata->vif.addr)], sta);
+	}
+
 	rcu_assign_pointer(sta->sdata->some_sta, sta);
 }
 
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 4947341..227fce7 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -227,7 +227,8 @@ struct sta_ampdu_mlme {
  * mac80211 is communicating with.
  *
  * @list: global linked list entry
- * @hnext: hash table linked list pointer
+ * @hnext: hash table linked list pointer, by (remote) station MAC
+ * @vnext: hash table linked list pointer, by VIF MAC
  * @local: pointer to the global information
  * @sdata: virtual interface this station belongs to
  * @ptk: peer key negotiated with this station, if any
@@ -304,6 +305,7 @@ struct sta_info {
 	struct list_head list;
 	struct rcu_head rcu_head;
 	struct sta_info __rcu *hnext;
+	struct sta_info __rcu *vnext;
 	struct ieee80211_local *local;
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
@@ -507,6 +509,9 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
 struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
 				  const u8 *addr);
 
+struct sta_info *sta_info_get_by_vif(struct ieee80211_local *local,
+				     const u8 *vif_addr, const u8 *sta_addr);
+
 static inline
 void for_each_sta_info_type_check(struct ieee80211_local *local,
 				  const u8 *addr,
-- 
1.7.3.4

--
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