@@ -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);
}
@@ -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
@@ -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);
@@ -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;
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@fami-braun.de> --- 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(-)