If an AP_VLAN is only used with one station, cache a pointer to this station to avoid lookup. This should speed up station lookup when there is only one station assigned to the AP_VLAN interface, e.g. when using hostapd with per_sta_vif. Assigning only one station per AP_VLAN interfaces enables bridge IGMP snooping to track multicast subscriptions by station to selectively forward to only those stations that subscribed. Signed-off-by: Michael Braun <michael-dev@xxxxxxxxxxxxx> --- net/mac80211/cfg.c | 10 ++++++++-- net/mac80211/ieee80211_i.h | 14 ++++++++++---- net/mac80211/sta_info.c | 33 +++++++++++++++++++++++++++++++-- net/mac80211/tx.c | 5 +++++ 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 078e837..a69e6f2 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -67,6 +67,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy, if (type == NL80211_IFTYPE_AP_VLAN && params && params->use_4addr == 0) { RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); + RCU_INIT_POINTER(sdata->u.vlan.sta0, NULL); ieee80211_check_fast_rx_iface(sdata); } else if (type == NL80211_IFTYPE_STATION && params && params->use_4addr >= 0) { @@ -1379,8 +1380,13 @@ static int ieee80211_change_station(struct wiphy *wiphy, sta->sdata = vlansdata; ieee80211_check_fast_xmit(sta); - if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) - ieee80211_vif_inc_num_mcast(sta->sdata); + if (test_sta_flag(sta, WLAN_STA_AUTHORIZED) && + ieee80211_vif_inc_num_mcast(sta->sdata) == 1 && + sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + rcu_assign_pointer(vlansdata->u.vlan.sta0, sta); + else if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && + test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + RCU_INIT_POINTER(vlansdata->u.vlan.sta0, NULL); ieee80211_send_layer2_update(sta); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 7b3de28..48a141f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -307,6 +307,8 @@ struct ieee80211_if_vlan { /* used for all tx if the VLAN is configured to 4-addr mode */ struct sta_info __rcu *sta; + /* the one and only authenticated station in non 4-addr mode if set */ + struct sta_info __rcu *sta0; atomic_t num_mcast_sta_if; /* number of stations receiving multicast */ }; @@ -1499,21 +1501,25 @@ ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status) return false; } -static inline void +static inline int ieee80211_vif_inc_num_mcast(struct ieee80211_sub_if_data *sdata) { + int ret; + if (sdata->vif.type != NL80211_IFTYPE_AP && sdata->vif.type != NL80211_IFTYPE_AP_VLAN) - return; + return -1; if (sdata->vif.type == NL80211_IFTYPE_AP) - atomic_inc(&sdata->u.ap.num_mcast_sta_if); + ret = atomic_inc_return(&sdata->u.ap.num_mcast_sta_if); else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - atomic_inc(&sdata->u.vlan.num_mcast_sta_if); + ret = atomic_inc_return(&sdata->u.vlan.num_mcast_sta_if); if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta) /* except 4addr mode */ atomic_inc(&sdata->bss->num_mcast_sta); + + return ret; } static inline void diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 216ef65..d1c5d96 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -162,11 +162,28 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, const u8 *addr) { struct ieee80211_local *local = sdata->local; - struct sta_info *sta; + struct sta_info *sta = NULL; struct rhash_head *tmp; const struct bucket_table *tbl; rcu_read_lock(); + + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && + !sdata->u.vlan.sta) + sta = rcu_dereference(sdata->u.vlan.sta0); + + WARN_ONCE((sta && sta->sdata != sdata), + "sdata->u.vlan.sta0->sdata != sdata"); + + if (sta && sta->sdata == sdata && + ether_addr_equal(sta->sta.addr, addr)) { + rcu_read_unlock(); + /* this is safe as the caller must already hold + * another rcu read section or the mutex + */ + return sta; + } + tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); for_each_sta_info(local, tbl, addr, sta, tmp) { @@ -920,6 +937,10 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta) rcu_access_pointer(sdata->u.vlan.sta) == sta) RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && + rcu_access_pointer(sdata->u.vlan.sta0) == sta) + RCU_INIT_POINTER(sdata->u.vlan.sta0, NULL); + return 0; } @@ -1883,6 +1904,9 @@ int sta_info_move_state(struct sta_info *sta, ieee80211_recalc_p2p_go_ps_allowed(sta->sdata); } else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) { ieee80211_vif_dec_num_mcast(sta->sdata); + if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && + rcu_access_pointer(sta->sdata->u.vlan.sta0) == sta) + RCU_INIT_POINTER(sta->sdata->u.vlan.sta0, NULL); clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags); ieee80211_clear_fast_xmit(sta); ieee80211_clear_fast_rx(sta); @@ -1890,7 +1914,12 @@ int sta_info_move_state(struct sta_info *sta, break; case IEEE80211_STA_AUTHORIZED: if (sta->sta_state == IEEE80211_STA_ASSOC) { - ieee80211_vif_inc_num_mcast(sta->sdata); + if (ieee80211_vif_inc_num_mcast(sta->sdata) == 1 && + sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + rcu_assign_pointer(sta->sdata->u.vlan.sta0, + sta); + else if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + RCU_INIT_POINTER(sta->sdata->u.vlan.sta0, NULL); set_bit(WLAN_STA_AUTHORIZED, &sta->_flags); ieee80211_check_fast_xmit(sta); ieee80211_check_fast_rx(sta); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 9c82fd8..c06d5f9 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1808,6 +1808,7 @@ ieee80211_tx_multicast_to_unicast(struct ieee80211_sub_if_data *sdata, sta = rcu_dereference(sdata->u.vlan.sta); if (sta) /* 4addr */ return 0; + prev = rcu_dereference(sdata->u.vlan.sta0); case NL80211_IFTYPE_AP: break; default: @@ -1839,6 +1840,9 @@ ieee80211_tx_multicast_to_unicast(struct ieee80211_sub_if_data *sdata, return 0; } + if (prev) + goto skip_lookup; + /* clone packets and update destination mac */ list_for_each_entry_rcu(sta, &local->sta_list, list) { if (sdata != sta->sdata) @@ -1862,6 +1866,7 @@ ieee80211_tx_multicast_to_unicast(struct ieee80211_sub_if_data *sdata, prev = sta; } +skip_lookup: if (likely(prev)) { ieee80211_tx_dnat(skb, prev); return 0; -- 2.1.4