wifi: mac80211: fix link sta hash table handling
authorJohannes Berg <johannes.berg@intel.com>
Mon, 18 Jul 2022 20:54:27 +0000 (22:54 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 22 Jul 2022 12:28:10 +0000 (14:28 +0200)
There are two issues here: we unhash the link stations only
directly before freeing the station they belong to, and we
also don't unhash all the links correctly in all cases. Fix
these issues.

Fixes: ba6ddab94fc6 ("wifi: mac80211: maintain link-sta hash table")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/sta_info.c

index 75122eced104435fcaa776bbb9c8466553a51984..44e21dee6077474d6883c9405833510c0a58abb0 100644 (file)
@@ -358,7 +358,7 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
                if (!(sta->sta.valid_links & BIT(i)))
                        continue;
 
-               sta_remove_link(sta, i, true);
+               sta_remove_link(sta, i, false);
        }
 
        /*
@@ -846,6 +846,8 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
 
        return 0;
  out_remove:
+       if (sta->sta.valid_links)
+               link_sta_info_hash_del(local, &sta->deflink);
        sta_info_hash_del(local, sta);
        list_del_rcu(&sta->list);
  out_drop_sta:
@@ -1140,7 +1142,7 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
 {
        struct ieee80211_local *local;
        struct ieee80211_sub_if_data *sdata;
-       int ret;
+       int ret, i;
 
        might_sleep();
 
@@ -1168,6 +1170,18 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
         */
        drv_sync_rx_queues(local, sta);
 
+       for (i = 0; i < ARRAY_SIZE(sta->link); i++) {
+               struct link_sta_info *link_sta;
+
+               if (!(sta->sta.valid_links & BIT(i)))
+                       continue;
+
+               link_sta = rcu_dereference_protected(sta->link[i],
+                                                    lockdep_is_held(&local->sta_mtx));
+
+               link_sta_info_hash_del(local, link_sta);
+       }
+
        ret = sta_info_hash_del(local, sta);
        if (WARN_ON(ret))
                return ret;