diff options
author | Johannes Berg <johannes.berg@intel.com> | 2022-06-15 09:20:45 +0200 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2022-06-20 12:57:08 +0200 |
commit | c71420db653aba30a234d1e4cf86dde376e604fa (patch) | |
tree | 1e29e5b76bc2b04a6a3e93f9b362f3380b60558b /net/mac80211/sta_info.c | |
parent | ce6893e917862cae669af52a440d086f416c4cc6 (diff) | |
download | linux-c71420db653aba30a234d1e4cf86dde376e604fa.tar.bz2 |
wifi: mac80211: RCU-ify link STA pointers
We need to be able to access these in a race-free way under
traffic while adding/removing them, so RCU-ify the pointers.
This requires passing a link_sta to a lot of functions so
we don't have to do the RCU handling everywhere.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/sta_info.c')
-rw-r--r-- | net/mac80211/sta_info.c | 28 |
1 files changed, 18 insertions, 10 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index b1426a2459e8..1f4189e08675 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -67,6 +67,7 @@ struct sta_link_alloc { struct link_sta_info info; struct ieee80211_link_sta sta; + struct rcu_head rcu_head; }; static const struct rhashtable_params sta_rht_params = { @@ -258,19 +259,23 @@ static void sta_info_free_link(struct link_sta_info *link_sta) static void sta_remove_link(struct sta_info *sta, unsigned int link_id) { struct sta_link_alloc *alloc = NULL; + struct link_sta_info *link_sta; - if (WARN_ON(!sta->link[link_id])) + link_sta = rcu_dereference_protected(sta->link[link_id], + lockdep_is_held(&sta->local->sta_mtx)); + + if (WARN_ON(!link_sta)) return; - if (sta->link[link_id] != &sta->deflink) - alloc = container_of(sta->link[link_id], typeof(*alloc), info); + if (link_sta != &sta->deflink) + alloc = container_of(link_sta, typeof(*alloc), info); sta->sta.valid_links &= ~BIT(link_id); - sta->link[link_id] = NULL; - sta->sta.link[link_id] = NULL; + RCU_INIT_POINTER(sta->link[link_id], NULL); + RCU_INIT_POINTER(sta->sta.link[link_id], NULL); if (alloc) { sta_info_free_link(&alloc->info); - kfree(alloc); + kfree_rcu(alloc, rcu_head); } } @@ -404,8 +409,9 @@ static void sta_info_add_link(struct sta_info *sta, { link_info->sta = sta; link_info->link_id = link_id; - sta->link[link_id] = link_info; - sta->sta.link[link_id] = link_sta; + link_info->pub = link_sta; + rcu_assign_pointer(sta->link[link_id], link_info); + rcu_assign_pointer(sta->sta.link[link_id], link_sta); } struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, @@ -2682,13 +2688,15 @@ int ieee80211_sta_allocate_link(struct sta_info *sta, unsigned int link_id) int ieee80211_sta_activate_link(struct sta_info *sta, unsigned int link_id) { struct ieee80211_sub_if_data *sdata = sta->sdata; + struct link_sta_info *link_sta; u16 old_links = sta->sta.valid_links; u16 new_links = old_links | BIT(link_id); int ret; - lockdep_assert_held(&sdata->local->sta_mtx); + link_sta = rcu_dereference_protected(sta->link[link_id], + lockdep_is_held(&sdata->local->sta_mtx)); - if (WARN_ON(old_links == new_links || !sta->link[link_id])) + if (WARN_ON(old_links == new_links || !link_sta)) return -EINVAL; sta->sta.valid_links = new_links; |