Return-path: Received: from mail.candelatech.com ([208.74.158.172]:54205 "EHLO ns3.lanforge.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753495Ab3C2QA3 (ORCPT ); Fri, 29 Mar 2013 12:00:29 -0400 From: greearb@candelatech.com To: linux-wireless@vger.kernel.org Cc: Ben Greear Subject: [PATCH v4] mac80211: Optimize sta lookup for many VIFs Date: Fri, 29 Mar 2013 09:00:23 -0700 Message-Id: <1364572823-28071-1-git-send-email-greearb@candelatech.com> (sfid-20130329_170033_026763_9F6C74DD) Sender: linux-wireless-owner@vger.kernel.org List-ID: From: Ben Greear The sta_info hash is designed to deal with an AP with lots of stations associated, or a station interface connected to a single AP. However, when you have lots of station VIFs connected to the same AP, the sta_info hash becomes worthless as there is a single hash bucket that contains all the entries in a linked list. So, have the sdata object cache one of its station interfaces. If we are a station VIF with a single sta_info, then this means we can ignore the sta_info hash entirely. On a test case with 128 stations and 50 TCP streams, tx performance went from around 80Mbps to 124Mbps. Signed-off-by: Ben Greear --- v4: Use rcu_dereference_protected as designed, passing a mutex as the second argument instead of hard-coding it to 1. :100644 100644 a618bda... a7e7f5d... M net/mac80211/cfg.c :100644 100644 493e2e8... c6386c7... M net/mac80211/ieee80211_i.h :100644 100644 415f9c6... d87a76d... M net/mac80211/sta_info.c net/mac80211/cfg.c | 6 ++++++ net/mac80211/ieee80211_i.h | 6 ++++++ net/mac80211/sta_info.c | 23 ++++++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a618bda..a7e7f5d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1287,6 +1287,7 @@ static int ieee80211_change_station(struct wiphy *wiphy, if (params->vlan && params->vlan != sta->sdata->dev) { bool prev_4addr = false; bool new_4addr = false; + struct sta_info *some_sta; vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); @@ -1312,7 +1313,12 @@ static int ieee80211_change_station(struct wiphy *wiphy, prev_4addr = true; } + some_sta = rcu_dereference_protected(sta->sdata->some_sta, + lockdep_is_held(&local->sta_mtx)); + if (some_sta == sta) + rcu_assign_pointer(sta->sdata->some_sta, NULL); sta->sdata = vlansdata; + rcu_assign_pointer(sta->sdata->some_sta, sta); if (sta->sta_state == IEEE80211_STA_AUTHORIZED && prev_4addr != new_4addr) { diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 493e2e8..c6386c7 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -669,6 +669,12 @@ struct ieee80211_sub_if_data { /* count for keys needing tailroom space allocation */ int crypto_tx_tailroom_needed_cnt; + /* A pointer to some station associated with this interface, or + * NULL. This allows opportunistic lookup for sta_info objects when + * sdata is a station with a single sta_info. + */ + struct sta_info __rcu *some_sta; + struct net_device *dev; struct ieee80211_local *local; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 415f9c6..d87a76d 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -67,7 +67,12 @@ static int sta_info_hash_del(struct ieee80211_local *local, struct sta_info *sta) { - struct sta_info *s; + struct sta_info *s, *some_sta; + + some_sta = rcu_dereference_protected(sta->sdata->some_sta, + lockdep_is_held(&local->sta_mtx)); + if (some_sta == sta) + rcu_assign_pointer(sta->sdata->some_sta, NULL); s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)], lockdep_is_held(&local->sta_mtx)); @@ -195,6 +200,20 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sta_info *sta; + if (sdata->vif.type == NL80211_IFTYPE_STATION) { + struct sta_info *some_sta; + + /* Shortcut for finding station entries for STATION VIFs */ + some_sta = rcu_dereference(sdata->some_sta); + if (some_sta) { + if (WARN_ON(some_sta->sdata != sdata)) + rcu_assign_pointer(sdata->some_sta, NULL); + else + if (ether_addr_equal(some_sta->sta.addr, addr)) + return some_sta; + } + } + sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], lockdep_is_held(&local->sta_mtx)); while (sta) { @@ -278,6 +297,8 @@ static void sta_info_hash_add(struct ieee80211_local *local, lockdep_assert_held(&local->sta_mtx); sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)]; rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta); + + rcu_assign_pointer(sta->sdata->some_sta, sta); } static void sta_unblock(struct work_struct *wk) -- 1.7.3.4