diff options
-rw-r--r-- | net/mac80211/sta_info.c | 42 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 3 |
2 files changed, 41 insertions, 4 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 08e50760e092..89d449d0de6d 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -794,7 +794,7 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, return have_buffered; } -int __must_check __sta_info_destroy(struct sta_info *sta) +static int __must_check __sta_info_destroy_part1(struct sta_info *sta) { struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; @@ -831,7 +831,23 @@ int __must_check __sta_info_destroy(struct sta_info *sta) rcu_access_pointer(sdata->u.vlan.sta) == sta) RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); - synchronize_net(); + return 0; +} + +static void __sta_info_destroy_part2(struct sta_info *sta) +{ + struct ieee80211_local *local = sta->local; + struct ieee80211_sub_if_data *sdata = sta->sdata; + int ret; + + /* + * NOTE: This assumes at least synchronize_net() was done + * after _part1 and before _part2! + */ + + might_sleep(); + lockdep_assert_held(&local->sta_mtx); + /* now keys can no longer be reached */ ieee80211_free_sta_keys(local, sta); @@ -863,6 +879,18 @@ int __must_check __sta_info_destroy(struct sta_info *sta) ieee80211_recalc_min_chandef(sdata); cleanup_single_sta(sta); +} + +int __must_check __sta_info_destroy(struct sta_info *sta) +{ + int err = __sta_info_destroy_part1(sta); + + if (err) + return err; + + synchronize_net(); + + __sta_info_destroy_part2(sta); return 0; } @@ -936,6 +964,7 @@ int sta_info_flush(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct sta_info *sta, *tmp; + LIST_HEAD(free_list); int ret = 0; might_sleep(); @@ -943,10 +972,17 @@ int sta_info_flush(struct ieee80211_sub_if_data *sdata) mutex_lock(&local->sta_mtx); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { if (sdata == sta->sdata) { - WARN_ON(__sta_info_destroy(sta)); + if (!WARN_ON(__sta_info_destroy_part1(sta))) + list_add(&sta->free_list, &free_list); ret++; } } + + if (!list_empty(&free_list)) { + synchronize_net(); + list_for_each_entry_safe(sta, tmp, &free_list, free_list) + __sta_info_destroy_part2(sta); + } mutex_unlock(&local->sta_mtx); return ret; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 9104f812e9de..f6081e574a28 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -247,6 +247,7 @@ struct ieee80211_tx_latency_stat { * mac80211 is communicating with. * * @list: global linked list entry + * @free_list: list entry for keeping track of stations to free * @hnext: hash table linked list pointer * @local: pointer to the global information * @sdata: virtual interface this station belongs to @@ -329,7 +330,7 @@ struct ieee80211_tx_latency_stat { */ struct sta_info { /* General information, mostly static */ - struct list_head list; + struct list_head list, free_list; struct rcu_head rcu_head; struct sta_info __rcu *hnext; struct ieee80211_local *local; |