]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: mac80211: add API to show the link STAs in debugfs
authorBenjamin Berg <benjamin.berg@intel.com>
Tue, 6 Sep 2022 12:26:52 +0000 (14:26 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 7 Oct 2022 13:23:41 +0000 (15:23 +0200)
Create debugfs data per-link. For drivers, there is a new operation
link_sta_add_debugfs which will always be called.

For non-MLO, the station directory will be used directly rather than
creating a corresponding subdirectory. As such, non-MLO drivers can
simply continue to create the data from sta_debugfs_add.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
[add missing inlines if !CONFIG_MAC80211_DEBUGFS]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/debugfs_sta.c
net/mac80211/debugfs_sta.h
net/mac80211/driver-ops.c
net/mac80211/driver-ops.h
net/mac80211/sta_info.c
net/mac80211/sta_info.h

index 7778a92d958214fe3651866e09c7c04a5ca986e4..c413050ec8ddb0aa5ce1762fa4de86e33a8b4463 100644 (file)
@@ -3790,6 +3790,13 @@ struct ieee80211_prep_tx_info {
  *     should be within a CONFIG_MAC80211_DEBUGFS conditional. This
  *     callback can sleep.
  *
+ * @link_sta_add_debugfs: Drivers can use this callback to add debugfs files
+ *     when a link is added to a mac80211 station. This callback
+ *     should be within a CPTCFG_MAC80211_DEBUGFS conditional. This
+ *     callback can sleep.
+ *     For non-MLO the callback will be called once for the deflink with the
+ *     station's directory rather than a separate subdirectory.
+ *
  * @sta_notify: Notifies low level driver about power state transition of an
  *     associated station, AP,  IBSS/WDS/mesh peer etc. For a VIF operating
  *     in AP mode, this callback will not be called when the flag
@@ -4260,6 +4267,10 @@ struct ieee80211_ops {
                                struct ieee80211_vif *vif,
                                struct ieee80211_sta *sta,
                                struct dentry *dir);
+       void (*link_sta_add_debugfs)(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif,
+                                    struct ieee80211_link_sta *link_sta,
+                                    struct dentry *dir);
 #endif
        void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        enum sta_notify_cmd, struct ieee80211_sta *sta);
index d3397c1248d36fe2fe520d9e49180991b206256d..68c07d4b95a538e8297eefab18467561e9205157 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright 2007      Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright(c) 2016 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2021 Intel Corporation
+ * Copyright (C) 2018 - 2022 Intel Corporation
  */
 
 #include <linux/debugfs.h>
@@ -435,8 +435,16 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu
 }
 STA_OPS_RW(agg_status);
 
-static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
-                               size_t count, loff_t *ppos)
+/* link sta attributes */
+#define LINK_STA_OPS(name)                                             \
+static const struct file_operations link_sta_ ##name## _ops = {                \
+       .read = link_sta_##name##_read,                                 \
+       .open = simple_open,                                            \
+       .llseek = generic_file_llseek,                                  \
+}
+
+static ssize_t link_sta_ht_capa_read(struct file *file, char __user *userbuf,
+                                    size_t count, loff_t *ppos)
 {
 #define PRINT_HT_CAP(_cond, _str) \
        do { \
@@ -446,8 +454,8 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
        char *buf, *p;
        int i;
        ssize_t bufsz = 512;
-       struct sta_info *sta = file->private_data;
-       struct ieee80211_sta_ht_cap *htc = &sta->sta.deflink.ht_cap;
+       struct link_sta_info *link_sta = file->private_data;
+       struct ieee80211_sta_ht_cap *htc = &link_sta->pub->ht_cap;
        ssize_t ret;
 
        buf = kzalloc(bufsz, GFP_KERNEL);
@@ -524,14 +532,14 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
        kfree(buf);
        return ret;
 }
-STA_OPS(ht_capa);
+LINK_STA_OPS(ht_capa);
 
-static ssize_t sta_vht_capa_read(struct file *file, char __user *userbuf,
-                                size_t count, loff_t *ppos)
+static ssize_t link_sta_vht_capa_read(struct file *file, char __user *userbuf,
+                                     size_t count, loff_t *ppos)
 {
        char *buf, *p;
-       struct sta_info *sta = file->private_data;
-       struct ieee80211_sta_vht_cap *vhtc = &sta->sta.deflink.vht_cap;
+       struct link_sta_info *link_sta = file->private_data;
+       struct ieee80211_sta_vht_cap *vhtc = &link_sta->pub->vht_cap;
        ssize_t ret;
        ssize_t bufsz = 512;
 
@@ -638,15 +646,15 @@ static ssize_t sta_vht_capa_read(struct file *file, char __user *userbuf,
        kfree(buf);
        return ret;
 }
-STA_OPS(vht_capa);
+LINK_STA_OPS(vht_capa);
 
-static ssize_t sta_he_capa_read(struct file *file, char __user *userbuf,
-                               size_t count, loff_t *ppos)
+static ssize_t link_sta_he_capa_read(struct file *file, char __user *userbuf,
+                                    size_t count, loff_t *ppos)
 {
        char *buf, *p;
        size_t buf_sz = PAGE_SIZE;
-       struct sta_info *sta = file->private_data;
-       struct ieee80211_sta_he_cap *hec = &sta->sta.deflink.he_cap;
+       struct link_sta_info *link_sta = file->private_data;
+       struct ieee80211_sta_he_cap *hec = &link_sta->pub->he_cap;
        struct ieee80211_he_mcs_nss_supp *nss = &hec->he_mcs_nss_supp;
        u8 ppe_size;
        u8 *cap;
@@ -1011,7 +1019,7 @@ out:
        kfree(buf);
        return ret;
 }
-STA_OPS(he_capa);
+LINK_STA_OPS(he_capa);
 
 #define DEBUGFS_ADD(name) \
        debugfs_create_file(#name, 0400, \
@@ -1048,12 +1056,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
        DEBUGFS_ADD(num_ps_buf_frames);
        DEBUGFS_ADD(last_seq_ctrl);
        DEBUGFS_ADD(agg_status);
-       DEBUGFS_ADD(ht_capa);
-       DEBUGFS_ADD(vht_capa);
-       DEBUGFS_ADD(he_capa);
-
-       DEBUGFS_ADD_COUNTER(rx_duplicates, deflink.rx_stats.num_duplicates);
-       DEBUGFS_ADD_COUNTER(rx_fragments, deflink.rx_stats.fragments);
+       /* FIXME: Kept here as the statistics are only done on the deflink */
        DEBUGFS_ADD_COUNTER(tx_filtered, deflink.status_stats.filtered);
 
        if (local->ops->wake_tx_queue) {
@@ -1076,3 +1079,83 @@ void ieee80211_sta_debugfs_remove(struct sta_info *sta)
        debugfs_remove_recursive(sta->debugfs_dir);
        sta->debugfs_dir = NULL;
 }
+
+#undef DEBUGFS_ADD
+#undef DEBUGFS_ADD_COUNTER
+
+#define DEBUGFS_ADD(name) \
+       debugfs_create_file(#name, 0400, \
+               link_sta->debugfs_dir, link_sta, &link_sta_ ##name## _ops)
+#define DEBUGFS_ADD_COUNTER(name, field)                               \
+       debugfs_create_ulong(#name, 0400, link_sta->debugfs_dir, &link_sta->field)
+
+void ieee80211_link_sta_debugfs_add(struct link_sta_info *link_sta)
+{
+       if (WARN_ON(!link_sta->sta->debugfs_dir))
+               return;
+
+       /* For non-MLO, leave the files in the main directory. */
+       if (link_sta->sta->sta.valid_links) {
+               char link_dir_name[10];
+
+               snprintf(link_dir_name, sizeof(link_dir_name),
+                        "link-%d", link_sta->link_id);
+
+               link_sta->debugfs_dir =
+                       debugfs_create_dir(link_dir_name,
+                                          link_sta->sta->debugfs_dir);
+       } else {
+               if (WARN_ON(link_sta != &link_sta->sta->deflink))
+                       return;
+
+               link_sta->debugfs_dir = link_sta->sta->debugfs_dir;
+       }
+
+       DEBUGFS_ADD(ht_capa);
+       DEBUGFS_ADD(vht_capa);
+       DEBUGFS_ADD(he_capa);
+
+       DEBUGFS_ADD_COUNTER(rx_duplicates, rx_stats.num_duplicates);
+       DEBUGFS_ADD_COUNTER(rx_fragments, rx_stats.fragments);
+}
+
+void ieee80211_link_sta_debugfs_remove(struct link_sta_info *link_sta)
+{
+       if (!link_sta->debugfs_dir || !link_sta->sta->debugfs_dir) {
+               link_sta->debugfs_dir = NULL;
+               return;
+       }
+
+       if (link_sta->debugfs_dir == link_sta->sta->debugfs_dir) {
+               WARN_ON(link_sta != &link_sta->sta->deflink);
+               link_sta->sta->debugfs_dir = NULL;
+               return;
+       }
+
+       debugfs_remove_recursive(link_sta->debugfs_dir);
+       link_sta->debugfs_dir = NULL;
+}
+
+void ieee80211_link_sta_debugfs_drv_add(struct link_sta_info *link_sta)
+{
+       if (WARN_ON(!link_sta->debugfs_dir))
+               return;
+
+       drv_link_sta_add_debugfs(link_sta->sta->local, link_sta->sta->sdata,
+                                link_sta->pub, link_sta->debugfs_dir);
+}
+
+void ieee80211_link_sta_debugfs_drv_remove(struct link_sta_info *link_sta)
+{
+       if (!link_sta->debugfs_dir)
+               return;
+
+       if (WARN_ON(link_sta->debugfs_dir == link_sta->sta->debugfs_dir))
+               return;
+
+       /* Recreate the directory excluding the driver data */
+       debugfs_remove_recursive(link_sta->debugfs_dir);
+       link_sta->debugfs_dir = NULL;
+
+       ieee80211_link_sta_debugfs_add(link_sta);
+}
index d2e7c27ad6d1c45f34686c44c3af3a51c5ccfe77..cde8148bdb18f77d43b6dcd9c6ea95c417881771 100644 (file)
@@ -7,9 +7,21 @@
 #ifdef CONFIG_MAC80211_DEBUGFS
 void ieee80211_sta_debugfs_add(struct sta_info *sta);
 void ieee80211_sta_debugfs_remove(struct sta_info *sta);
+
+void ieee80211_link_sta_debugfs_add(struct link_sta_info *link_sta);
+void ieee80211_link_sta_debugfs_remove(struct link_sta_info *link_sta);
+
+void ieee80211_link_sta_debugfs_drv_add(struct link_sta_info *link_sta);
+void ieee80211_link_sta_debugfs_drv_remove(struct link_sta_info *link_sta);
 #else
 static inline void ieee80211_sta_debugfs_add(struct sta_info *sta) {}
 static inline void ieee80211_sta_debugfs_remove(struct sta_info *sta) {}
+
+static inline void ieee80211_link_sta_debugfs_add(struct link_sta_info *link_sta) {}
+static inline void ieee80211_link_sta_debugfs_remove(struct link_sta_info *link_sta) {}
+
+static inline void ieee80211_link_sta_debugfs_drv_add(struct link_sta_info *link_sta) {}
+static inline void ieee80211_link_sta_debugfs_drv_remove(struct link_sta_info *link_sta) {}
 #endif
 
 #endif /* __MAC80211_DEBUGFS_STA_H */
index 5392ffa182704feded6ec63a50579368b5c0d60d..d737db4e07e2436de3b47ccb98be943ad7afc1c5 100644 (file)
@@ -7,6 +7,7 @@
 #include "ieee80211_i.h"
 #include "trace.h"
 #include "driver-ops.h"
+#include "debugfs_sta.h"
 
 int drv_start(struct ieee80211_local *local)
 {
@@ -497,6 +498,11 @@ int drv_change_sta_links(struct ieee80211_local *local,
                         struct ieee80211_sta *sta,
                         u16 old_links, u16 new_links)
 {
+       struct sta_info *info = container_of(sta, struct sta_info, sta);
+       struct link_sta_info *link_sta;
+       unsigned long links_to_add;
+       unsigned long links_to_rem;
+       unsigned int link_id;
        int ret = -EOPNOTSUPP;
 
        might_sleep();
@@ -510,11 +516,30 @@ int drv_change_sta_links(struct ieee80211_local *local,
        if (old_links == new_links)
                return 0;
 
+       links_to_add = ~old_links & new_links;
+       links_to_rem = old_links & ~new_links;
+
+       for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) {
+               link_sta = rcu_dereference_protected(info->link[link_id],
+                                                    lockdep_is_held(&local->sta_mtx));
+
+               ieee80211_link_sta_debugfs_drv_remove(link_sta);
+       }
+
        trace_drv_change_sta_links(local, sdata, sta, old_links, new_links);
        if (local->ops->change_sta_links)
                ret = local->ops->change_sta_links(&local->hw, &sdata->vif, sta,
                                                   old_links, new_links);
        trace_drv_return_int(local, ret);
 
-       return ret;
+       if (ret)
+               return ret;
+
+       for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
+               link_sta = rcu_dereference_protected(info->link[link_id],
+                                                    lockdep_is_held(&local->sta_mtx));
+               ieee80211_link_sta_debugfs_drv_add(link_sta);
+       }
+
+       return 0;
 }
index 81e40b0a3b1675a872f8f5f9adb90c626b145ab8..809bad53e15b67d63c9d953aacc60e16f2fa57d6 100644 (file)
@@ -480,6 +480,22 @@ static inline void drv_sta_add_debugfs(struct ieee80211_local *local,
                local->ops->sta_add_debugfs(&local->hw, &sdata->vif,
                                            sta, dir);
 }
+
+static inline void drv_link_sta_add_debugfs(struct ieee80211_local *local,
+                                           struct ieee80211_sub_if_data *sdata,
+                                           struct ieee80211_link_sta *link_sta,
+                                           struct dentry *dir)
+{
+       might_sleep();
+
+       sdata = get_bss_sdata(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
+
+       if (local->ops->link_sta_add_debugfs)
+               local->ops->link_sta_add_debugfs(&local->hw, &sdata->vif,
+                                                link_sta, dir);
+}
 #endif
 
 static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local,
index 71b1488bd3900f1659bbdf702a9e04c4fff2c231..e6beaea4075ec51aec55ad3ed90db0e5d6583d01 100644 (file)
@@ -366,6 +366,9 @@ static void sta_remove_link(struct sta_info *sta, unsigned int link_id,
        if (unhash)
                link_sta_info_hash_del(sta->local, link_sta);
 
+       if (test_sta_flag(sta, WLAN_STA_INSERTED))
+               ieee80211_link_sta_debugfs_remove(link_sta);
+
        if (link_sta != &sta->deflink)
                alloc = container_of(link_sta, typeof(*alloc), info);
 
@@ -875,6 +878,26 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
 
        ieee80211_sta_debugfs_add(sta);
        rate_control_add_sta_debugfs(sta);
+       if (sta->sta.valid_links) {
+               int i;
+
+               for (i = 0; i < ARRAY_SIZE(sta->link); i++) {
+                       struct link_sta_info *link_sta;
+
+                       link_sta = rcu_dereference_protected(sta->link[i],
+                                                            lockdep_is_held(&local->sta_mtx));
+
+                       if (!link_sta)
+                               continue;
+
+                       ieee80211_link_sta_debugfs_add(link_sta);
+                       if (sdata->vif.active_links & BIT(i))
+                               ieee80211_link_sta_debugfs_drv_add(link_sta);
+               }
+       } else {
+               ieee80211_link_sta_debugfs_add(&sta->deflink);
+               ieee80211_link_sta_debugfs_drv_add(&sta->deflink);
+       }
 
        sinfo->generation = local->sta_generation;
        cfg80211_new_sta(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL);
@@ -2824,6 +2847,8 @@ int ieee80211_sta_allocate_link(struct sta_info *sta, unsigned int link_id)
 
        sta_info_add_link(sta, link_id, &alloc->info, &alloc->sta);
 
+       ieee80211_link_sta_debugfs_add(&alloc->info);
+
        return 0;
 }
 
index 2517ea714dc42d080cce857073f6c4d865fb3057..6e672bf9c79d5f8dda81fe9aff0e0ed830278fdb 100644 (file)
@@ -513,6 +513,7 @@ struct ieee80211_fragment_cache {
  * @status_stats.avg_ack_signal: average ACK signal
  * @cur_max_bandwidth: maximum bandwidth to use for TX to the station,
  *     taken from HT/VHT capabilities or VHT operating mode notification
+ * @debugfs_dir: debug filesystem directory dentry
  * @pub: public (driver visible) link STA data
  * TODO Move other link params from sta_info as required for MLD operation
  */
@@ -560,6 +561,10 @@ struct link_sta_info {
 
        enum ieee80211_sta_rx_bandwidth cur_max_bandwidth;
 
+#ifdef CONFIG_MAC80211_DEBUGFS
+       struct dentry *debugfs_dir;
+#endif
+
        struct ieee80211_link_sta *pub;
 };