[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <9454bbffe8de24c0afcc6b307057e927ffaec6ca.1702314695.git.ecree.xilinx@gmail.com>
Date: Mon, 11 Dec 2023 17:18:26 +0000
From: <edward.cree@....com>
To: <linux-net-drivers@....com>, <davem@...emloft.net>, <kuba@...nel.org>,
<pabeni@...hat.com>, <edumazet@...gle.com>
CC: Edward Cree <ecree.xilinx@...il.com>, <netdev@...r.kernel.org>,
<habetsm.xilinx@...il.com>, Jonathan Cooper <jonathan.s.cooper@....com>
Subject: [PATCH net-next 1/7] sfc: initial debugfs implementation
From: Edward Cree <ecree.xilinx@...il.com>
Just a handful of nodes, including one enum with a string table for
pretty printing the values.
Reviewed-by: Jonathan Cooper <jonathan.s.cooper@....com>
Signed-off-by: Edward Cree <ecree.xilinx@...il.com>
---
drivers/net/ethernet/sfc/Makefile | 1 +
drivers/net/ethernet/sfc/debugfs.c | 234 ++++++++++++++++++++++++
drivers/net/ethernet/sfc/debugfs.h | 56 ++++++
drivers/net/ethernet/sfc/ef10.c | 10 +
drivers/net/ethernet/sfc/ef100_netdev.c | 7 +
drivers/net/ethernet/sfc/ef100_nic.c | 8 +
drivers/net/ethernet/sfc/efx.c | 15 +-
drivers/net/ethernet/sfc/efx_common.c | 7 +
drivers/net/ethernet/sfc/net_driver.h | 29 +++
9 files changed, 366 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/sfc/debugfs.c
create mode 100644 drivers/net/ethernet/sfc/debugfs.h
diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile
index 8f446b9bd5ee..1fbdd04dc2c5 100644
--- a/drivers/net/ethernet/sfc/Makefile
+++ b/drivers/net/ethernet/sfc/Makefile
@@ -12,6 +12,7 @@ sfc-$(CONFIG_SFC_MTD) += mtd.o
sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \
mae.o tc.o tc_bindings.o tc_counters.o \
tc_encap_actions.o tc_conntrack.o
+sfc-$(CONFIG_DEBUG_FS) += debugfs.o
obj-$(CONFIG_SFC) += sfc.o
diff --git a/drivers/net/ethernet/sfc/debugfs.c b/drivers/net/ethernet/sfc/debugfs.c
new file mode 100644
index 000000000000..cf800addb4ff
--- /dev/null
+++ b/drivers/net/ethernet/sfc/debugfs.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/****************************************************************************
+ * Driver for Solarflare network controllers and boards
+ * Copyright 2023, Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include "debugfs.h"
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/dcache.h>
+#include <linux/seq_file.h>
+
+/* Maximum length for a name component or symlink target */
+#define EFX_DEBUGFS_NAME_LEN 32
+
+/* Top-level debug directory ([/sys/kernel]/debug/sfc) */
+static struct dentry *efx_debug_root;
+
+/* "cards" directory ([/sys/kernel]/debug/sfc/cards) */
+static struct dentry *efx_debug_cards;
+
+/**
+ * efx_init_debugfs_netdev - create debugfs sym-link for net device
+ * @net_dev: Net device
+ *
+ * Create sym-link named after @net_dev to the debugfs directories for the
+ * corresponding NIC. The sym-link must be cleaned up using
+ * efx_fini_debugfs_netdev().
+ *
+ * Return: a negative error code or 0 on success.
+ */
+static int efx_init_debugfs_netdev(struct net_device *net_dev)
+{
+ struct efx_nic *efx = efx_netdev_priv(net_dev);
+ char target[EFX_DEBUGFS_NAME_LEN];
+ char name[EFX_DEBUGFS_NAME_LEN];
+
+ if (snprintf(name, sizeof(name), "nic_%s", net_dev->name) >=
+ sizeof(name))
+ return -ENAMETOOLONG;
+ if (snprintf(target, sizeof(target), "cards/%s", pci_name(efx->pci_dev))
+ >= sizeof(target))
+ return -ENAMETOOLONG;
+ efx->debug_symlink = debugfs_create_symlink(name,
+ efx_debug_root, target);
+ if (IS_ERR_OR_NULL(efx->debug_symlink))
+ return efx->debug_symlink ? PTR_ERR(efx->debug_symlink) :
+ -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * efx_fini_debugfs_netdev - remove debugfs sym-link for net device
+ * @net_dev: Net device
+ *
+ * Remove sym-link created for @net_dev by efx_init_debugfs_netdev().
+ */
+void efx_fini_debugfs_netdev(struct net_device *net_dev)
+{
+ struct efx_nic *efx = efx_netdev_priv(net_dev);
+
+ debugfs_remove(efx->debug_symlink);
+ efx->debug_symlink = NULL;
+}
+
+/* replace debugfs sym-links on net device rename */
+void efx_update_debugfs_netdev(struct efx_nic *efx)
+{
+ mutex_lock(&efx->debugfs_symlink_mutex);
+ if (efx->debug_symlink)
+ efx_fini_debugfs_netdev(efx->net_dev);
+ efx_init_debugfs_netdev(efx->net_dev);
+ mutex_unlock(&efx->debugfs_symlink_mutex);
+}
+
+static int efx_debugfs_enum_read(struct seq_file *s, void *d)
+{
+ struct efx_debugfs_enum_data *data = s->private;
+ u64 value = 0;
+ size_t len;
+
+ len = min(data->vlen, sizeof(value));
+#ifdef BIG_ENDIAN
+ /* If data->value is narrower than u64, we need to copy into the
+ * far end of value, as that's where the low bits live.
+ */
+ memcpy(((void *)&value) + sizeof(value) - len, data->value, len);
+#else
+ memcpy(&value, data->value, len);
+#endif
+ seq_printf(s, "%#llx => %s\n", value,
+ value < data->max ? data->names[value] : "(invalid)");
+ return 0;
+}
+
+static int efx_debugfs_enum_open(struct inode *inode, struct file *f)
+{
+ struct efx_debugfs_enum_data *data = inode->i_private;
+
+ return single_open(f, efx_debugfs_enum_read, data);
+}
+
+static const struct file_operations efx_debugfs_enum_ops = {
+ .owner = THIS_MODULE,
+ .open = efx_debugfs_enum_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
+static void efx_debugfs_create_enum(const char *name, umode_t mode,
+ struct dentry *parent,
+ struct efx_debugfs_enum_data *data)
+{
+ debugfs_create_file(name, mode, parent, data, &efx_debugfs_enum_ops);
+}
+
+static const char *const efx_interrupt_mode_names[] = {
+ [EFX_INT_MODE_MSIX] = "MSI-X",
+ [EFX_INT_MODE_MSI] = "MSI",
+ [EFX_INT_MODE_LEGACY] = "legacy",
+};
+
+#define EFX_DEBUGFS_EFX(_type, _name) \
+ debugfs_create_##_type(#_name, 0444, efx->debug_dir, &efx->_name)
+
+/* Create basic debugfs parameter files for an Efx NIC */
+static void efx_init_debugfs_nic_files(struct efx_nic *efx)
+{
+ EFX_DEBUGFS_EFX(x32, rx_dma_len);
+ EFX_DEBUGFS_EFX(x32, rx_buffer_order);
+ EFX_DEBUGFS_EFX(x32, rx_buffer_truesize);
+ efx->debug_interrupt_mode.max = ARRAY_SIZE(efx_interrupt_mode_names);
+ efx->debug_interrupt_mode.names = efx_interrupt_mode_names;
+ efx->debug_interrupt_mode.vlen = sizeof(efx->interrupt_mode);
+ efx->debug_interrupt_mode.value = &efx->interrupt_mode;
+ efx_debugfs_create_enum("interrupt_mode", 0444, efx->debug_dir,
+ &efx->debug_interrupt_mode);
+}
+
+/**
+ * efx_init_debugfs_nic - create debugfs directory for NIC
+ * @efx: Efx NIC
+ *
+ * Create debugfs directory containing parameter-files for @efx.
+ * The directory must be cleaned up using efx_fini_debugfs_nic().
+ *
+ * Return: a negative error code or 0 on success.
+ */
+int efx_init_debugfs_nic(struct efx_nic *efx)
+{
+ /* Create directory */
+ efx->debug_dir = debugfs_create_dir(pci_name(efx->pci_dev),
+ efx_debug_cards);
+ if (!efx->debug_dir)
+ return -ENOMEM;
+ efx_init_debugfs_nic_files(efx);
+ return 0;
+}
+
+/**
+ * efx_fini_debugfs_nic - remove debugfs directory for NIC
+ * @efx: Efx NIC
+ *
+ * Remove debugfs directory created for @efx by efx_init_debugfs_nic().
+ */
+void efx_fini_debugfs_nic(struct efx_nic *efx)
+{
+ debugfs_remove_recursive(efx->debug_dir);
+ efx->debug_dir = NULL;
+}
+
+/**
+ * efx_init_debugfs - create debugfs directories for sfc driver
+ *
+ * Create debugfs directories "sfc" and "sfc/cards". This must be
+ * called before any of the other functions that create debugfs
+ * directories. The directories must be cleaned up using
+ * efx_fini_debugfs().
+ *
+ * Return: a negative error code or 0 on success.
+ */
+int efx_init_debugfs(void)
+{
+ int rc;
+
+ /* Create top-level directory */
+ efx_debug_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ if (!efx_debug_root) {
+ pr_err("debugfs_create_dir %s failed.\n", KBUILD_MODNAME);
+ rc = -ENOMEM;
+ goto err;
+ } else if (IS_ERR(efx_debug_root)) {
+ rc = PTR_ERR(efx_debug_root);
+ pr_err("debugfs_create_dir %s failed, rc=%d.\n",
+ KBUILD_MODNAME, rc);
+ goto err;
+ }
+
+ /* Create "cards" directory */
+ efx_debug_cards = debugfs_create_dir("cards", efx_debug_root);
+ if (!efx_debug_cards) {
+ pr_err("debugfs_create_dir cards failed.\n");
+ rc = -ENOMEM;
+ goto err;
+ } else if (IS_ERR(efx_debug_cards)) {
+ rc = PTR_ERR(efx_debug_cards);
+ pr_err("debugfs_create_dir cards failed, rc=%d.\n", rc);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ efx_fini_debugfs();
+ return rc;
+}
+
+/**
+ * efx_fini_debugfs - remove debugfs directories for sfc driver
+ *
+ * Remove directories created by efx_init_debugfs().
+ */
+void efx_fini_debugfs(void)
+{
+ debugfs_remove_recursive(efx_debug_root);
+ efx_debug_cards = NULL;
+ efx_debug_root = NULL;
+}
diff --git a/drivers/net/ethernet/sfc/debugfs.h b/drivers/net/ethernet/sfc/debugfs.h
new file mode 100644
index 000000000000..1fe40fbffa5e
--- /dev/null
+++ b/drivers/net/ethernet/sfc/debugfs.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/****************************************************************************
+ * Driver for Solarflare network controllers and boards
+ * Copyright 2023, Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#ifndef EFX_DEBUGFS_H
+#define EFX_DEBUGFS_H
+#include "net_driver.h"
+
+#ifdef CONFIG_DEBUG_FS
+
+/**
+ * DOC: Directory layout for sfc debugfs
+ *
+ * At top level ([/sys/kernel]/debug/sfc) are per-netdev symlinks "nic_$name"
+ * and the "cards" directory. For each PCI device to which the driver has
+ * bound and created a &struct efx_nic, there is a directory &efx_nic.debug_dir
+ * in "cards" whose name is the PCI address of the device; it is to this
+ * directory that the netdev symlink points.
+ */
+
+void efx_fini_debugfs_netdev(struct net_device *net_dev);
+void efx_update_debugfs_netdev(struct efx_nic *efx);
+
+int efx_init_debugfs_nic(struct efx_nic *efx);
+void efx_fini_debugfs_nic(struct efx_nic *efx);
+
+int efx_init_debugfs(void);
+void efx_fini_debugfs(void);
+
+#else /* CONFIG_DEBUG_FS */
+
+static inline void efx_fini_debugfs_netdev(struct net_device *net_dev) {}
+
+static inline void efx_update_debugfs_netdev(struct efx_nic *efx) {}
+
+static inline int efx_init_debugfs_nic(struct efx_nic *efx)
+{
+ return 0;
+}
+static inline void efx_fini_debugfs_nic(struct efx_nic *efx) {}
+
+static inline int efx_init_debugfs(void)
+{
+ return 0;
+}
+static inline void efx_fini_debugfs(void) {}
+
+#endif /* CONFIG_DEBUG_FS */
+
+#endif /* EFX_DEBUGFS_H */
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index 6dfa062feebc..58e18fc92093 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -19,6 +19,7 @@
#include "workarounds.h"
#include "selftest.h"
#include "ef10_sriov.h"
+#include "debugfs.h"
#include <linux/in.h>
#include <linux/jhash.h>
#include <linux/wait.h>
@@ -580,6 +581,13 @@ static int efx_ef10_probe(struct efx_nic *efx)
if (rc)
goto fail3;
+ /* Populate debugfs */
+#ifdef CONFIG_DEBUG_FS
+ rc = efx_init_debugfs_nic(efx);
+ if (rc)
+ pci_err(efx->pci_dev, "failed to init device debugfs\n");
+#endif
+
rc = device_create_file(&efx->pci_dev->dev,
&dev_attr_link_control_flag);
if (rc)
@@ -693,6 +701,7 @@ static int efx_ef10_probe(struct efx_nic *efx)
fail4:
device_remove_file(&efx->pci_dev->dev, &dev_attr_link_control_flag);
fail3:
+ efx_fini_debugfs_nic(efx);
efx_mcdi_detach(efx);
mutex_lock(&nic_data->udp_tunnels_lock);
@@ -962,6 +971,7 @@ static void efx_ef10_remove(struct efx_nic *efx)
device_remove_file(&efx->pci_dev->dev, &dev_attr_link_control_flag);
efx_mcdi_detach(efx);
+ efx_fini_debugfs_nic(efx);
memset(nic_data->udp_tunnels, 0, sizeof(nic_data->udp_tunnels));
mutex_lock(&nic_data->udp_tunnels_lock);
diff --git a/drivers/net/ethernet/sfc/ef100_netdev.c b/drivers/net/ethernet/sfc/ef100_netdev.c
index 7f7d560cb2b4..e844d57754b7 100644
--- a/drivers/net/ethernet/sfc/ef100_netdev.c
+++ b/drivers/net/ethernet/sfc/ef100_netdev.c
@@ -26,10 +26,12 @@
#include "tc_bindings.h"
#include "tc_encap_actions.h"
#include "efx_devlink.h"
+#include "debugfs.h"
static void ef100_update_name(struct efx_nic *efx)
{
strcpy(efx->name, efx->net_dev->name);
+ efx_update_debugfs_netdev(efx);
}
static int ef100_alloc_vis(struct efx_nic *efx, unsigned int *allocated_vis)
@@ -405,6 +407,11 @@ void ef100_remove_netdev(struct efx_probe_data *probe_data)
ef100_pf_unset_devlink_port(efx);
efx_fini_tc(efx);
#endif
+#ifdef CONFIG_DEBUG_FS
+ mutex_lock(&efx->debugfs_symlink_mutex);
+ efx_fini_debugfs_netdev(efx->net_dev);
+ mutex_unlock(&efx->debugfs_symlink_mutex);
+#endif
down_write(&efx->filter_sem);
efx_mcdi_filter_table_remove(efx);
diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c
index 6da06931187d..ad378aa05dc3 100644
--- a/drivers/net/ethernet/sfc/ef100_nic.c
+++ b/drivers/net/ethernet/sfc/ef100_nic.c
@@ -27,6 +27,7 @@
#include "tc.h"
#include "mae.h"
#include "rx_common.h"
+#include "debugfs.h"
#define EF100_MAX_VIS 4096
#define EF100_NUM_MCDI_BUFFERS 1
@@ -1077,6 +1078,12 @@ static int ef100_probe_main(struct efx_nic *efx)
/* Post-IO section. */
+ /* Populate debugfs */
+#ifdef CONFIG_DEBUG_FS
+ rc = efx_init_debugfs_nic(efx);
+ if (rc)
+ pci_err(efx->pci_dev, "failed to init device debugfs\n");
+#endif
rc = efx_mcdi_init(efx);
if (rc)
goto fail;
@@ -1213,6 +1220,7 @@ void ef100_remove(struct efx_nic *efx)
efx_mcdi_detach(efx);
efx_mcdi_fini(efx);
+ efx_fini_debugfs_nic(efx);
if (nic_data)
efx_nic_free_buffer(efx, &nic_data->mcdi_buf);
kfree(nic_data);
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 19f4b4d0b851..9266c7b5b4fd 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -33,6 +33,7 @@
#include "selftest.h"
#include "sriov.h"
#include "efx_devlink.h"
+#include "debugfs.h"
#include "mcdi_port_common.h"
#include "mcdi_pcol.h"
@@ -401,6 +402,11 @@ static void efx_remove_all(struct efx_nic *efx)
#endif
efx_remove_port(efx);
efx_remove_nic(efx);
+#ifdef CONFIG_DEBUG_FS
+ mutex_lock(&efx->debugfs_symlink_mutex);
+ efx_fini_debugfs_netdev(efx->net_dev);
+ mutex_unlock(&efx->debugfs_symlink_mutex);
+#endif
}
/**************************************************************************
@@ -667,6 +673,7 @@ static void efx_update_name(struct efx_nic *efx)
strcpy(efx->name, efx->net_dev->name);
efx_mtd_rename(efx);
efx_set_channel_names(efx);
+ efx_update_debugfs_netdev(efx);
}
static int efx_netdev_event(struct notifier_block *this,
@@ -1319,6 +1326,10 @@ static int __init efx_init_module(void)
printk(KERN_INFO "Solarflare NET driver\n");
+ rc = efx_init_debugfs();
+ if (rc)
+ goto err_debugfs;
+
rc = register_netdevice_notifier(&efx_netdev_notifier);
if (rc)
goto err_notifier;
@@ -1344,6 +1355,8 @@ static int __init efx_init_module(void)
err_reset:
unregister_netdevice_notifier(&efx_netdev_notifier);
err_notifier:
+ efx_fini_debugfs();
+ err_debugfs:
return rc;
}
@@ -1355,7 +1368,7 @@ static void __exit efx_exit_module(void)
pci_unregister_driver(&efx_pci_driver);
efx_destroy_reset_workqueue();
unregister_netdevice_notifier(&efx_netdev_notifier);
-
+ efx_fini_debugfs();
}
module_init(efx_init_module);
diff --git a/drivers/net/ethernet/sfc/efx_common.c b/drivers/net/ethernet/sfc/efx_common.c
index 175bd9cdfdac..7a9d6b6b66e5 100644
--- a/drivers/net/ethernet/sfc/efx_common.c
+++ b/drivers/net/ethernet/sfc/efx_common.c
@@ -1022,6 +1022,9 @@ int efx_init_struct(struct efx_nic *efx, struct pci_dev *pci_dev)
INIT_WORK(&efx->mac_work, efx_mac_work);
init_waitqueue_head(&efx->flush_wq);
+#ifdef CONFIG_DEBUG_FS
+ mutex_init(&efx->debugfs_symlink_mutex);
+#endif
efx->tx_queues_per_channel = 1;
efx->rxq_entries = EFX_DEFAULT_DMAQ_SIZE;
efx->txq_entries = EFX_DEFAULT_DMAQ_SIZE;
@@ -1056,6 +1059,10 @@ void efx_fini_struct(struct efx_nic *efx)
efx_fini_channels(efx);
+#ifdef CONFIG_DEBUG_FS
+ mutex_destroy(&efx->debugfs_symlink_mutex);
+#endif
+
kfree(efx->vpd_sn);
if (efx->workqueue) {
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
index 27d86e90a3bb..961e2db31c6e 100644
--- a/drivers/net/ethernet/sfc/net_driver.h
+++ b/drivers/net/ethernet/sfc/net_driver.h
@@ -107,6 +107,24 @@ struct hwtstamp_config;
struct efx_self_tests;
+/**
+ * struct efx_debugfs_enum_data - information for pretty-printing enums
+ * @value: pointer to the actual enum
+ * @vlen: sizeof the enum
+ * @names: array of names of enumerated values. May contain some %NULL entries.
+ * @max: number of entries in @names, typically from ARRAY_SIZE()
+ *
+ * Where a driver struct contains an enum member which we wish to expose in
+ * debugfs, we also embed an instance of this struct, which
+ * efx_debugfs_enum_read() uses to pretty-print the value.
+ */
+struct efx_debugfs_enum_data {
+ void *value;
+ size_t vlen;
+ const char *const *names;
+ unsigned int max;
+};
+
/**
* struct efx_buffer - A general-purpose DMA buffer
* @addr: host base address of the buffer
@@ -1123,6 +1141,17 @@ struct efx_nic {
u32 rps_next_id;
#endif
+#ifdef CONFIG_DEBUG_FS
+ /** @debug_dir: NIC debugfs directory */
+ struct dentry *debug_dir;
+ /** @debug_symlink: NIC debugfs symlink (``nic_eth%d``) */
+ struct dentry *debug_symlink;
+ /** @debug_interrupt_mode: debugfs details for printing @interrupt_mode */
+ struct efx_debugfs_enum_data debug_interrupt_mode;
+ /** @debugfs_symlink_mutex: protects debugfs @debug_symlink */
+ struct mutex debugfs_symlink_mutex;
+#endif
+
atomic_t active_queues;
atomic_t rxq_flush_pending;
atomic_t rxq_flush_outstanding;
Powered by blists - more mailing lists