[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250805213356.3348348-3-dechen@redhat.com>
Date: Tue, 5 Aug 2025 17:33:55 -0400
From: Dennis Chen <dechen@...hat.com>
To: netdev@...r.kernel.org
Cc: dechen@...hat.com,
dchen27@...u.edu,
kuba@...nel.org,
davem@...emloft.net,
edumazet@...gle.com,
pabeni@...hat.com,
andrew+netdev@...n.ch,
petrm@...dia.com
Subject: [PATCH net-next 2/3] netdevsim: Add mock stats for ethtool
Add mock stats exposed through ethtool -S for netdevsim. The stats are
incremented every 100ms. Mock stats are enabled/disabled through a
debugfs toggle.
# echo y > /sys/kernel/debug/netdevsim/$DEV/ports/0/ethtool/mock_stats/enabled
Signed-off-by: Dennis Chen <dechen@...hat.com>
---
drivers/net/netdevsim/ethtool.c | 127 +++++++++++++++++++++++++++++-
drivers/net/netdevsim/netdev.c | 1 +
drivers/net/netdevsim/netdevsim.h | 11 +++
3 files changed, 138 insertions(+), 1 deletion(-)
diff --git a/drivers/net/netdevsim/ethtool.c b/drivers/net/netdevsim/ethtool.c
index 33d39dfdd6d9..78aea02f8bf1 100644
--- a/drivers/net/netdevsim/ethtool.c
+++ b/drivers/net/netdevsim/ethtool.c
@@ -16,6 +16,10 @@ struct nsim_stat_desc {
.desc = #s, \
.offset = offsetof(struct rtnl_link_stats64, s) }
+#define NSIM_MOCK_STAT_ENTRY(s) { \
+ .desc = #s, \
+ .offset = offsetof(struct nsim_mock_stats, s) }
+
static const struct nsim_stat_desc nsim_stats_desc[] = {
NSIM_STAT_ENTRY(tx_packets),
NSIM_STAT_ENTRY(rx_packets),
@@ -27,6 +31,14 @@ static const struct nsim_stat_desc nsim_stats_desc[] = {
#define NSIM_STATS_LEN ARRAY_SIZE(nsim_stats_desc)
+#define NSIM_MOCK_STATS_LEN ARRAY_SIZE(nsim_mock_stats_desc)
+
+static const struct nsim_stat_desc nsim_mock_stats_desc[] = {
+ NSIM_MOCK_STAT_ENTRY(hw_out_of_sequence),
+ NSIM_MOCK_STAT_ENTRY(hw_out_of_buffer),
+ NSIM_MOCK_STAT_ENTRY(hw_packet_seq_err),
+};
+
static void
nsim_get_pause_stats(struct net_device *dev,
struct ethtool_pause_stats *pause_stats)
@@ -204,9 +216,12 @@ static int nsim_get_ts_info(struct net_device *dev,
static int nsim_sset_count(struct net_device *dev, int sset)
{
+ struct netdevsim *ns = netdev_priv(dev);
+
switch (sset) {
case ETH_SS_STATS:
- return NSIM_STATS_LEN;
+ return ns->ethtool.mock_stats.enabled ?
+ NSIM_STATS_LEN + NSIM_MOCK_STATS_LEN : NSIM_STATS_LEN;
default:
return -EOPNOTSUPP;
}
@@ -214,20 +229,51 @@ static int nsim_sset_count(struct net_device *dev, int sset)
static void nsim_get_strings(struct net_device *dev, u32 sset, u8 *data)
{
+ struct netdevsim *ns = netdev_priv(dev);
+
int i;
switch (sset) {
case ETH_SS_STATS:
for (i = 0; i < NSIM_STATS_LEN; i++)
ethtool_puts(&data, nsim_stats_desc[i].desc);
+ if (ns->ethtool.mock_stats.enabled)
+ for (i = 0; i < NSIM_MOCK_STATS_LEN; i++)
+ ethtool_puts(&data,
+ nsim_mock_stats_desc[i].desc);
+
break;
}
}
+static void nsim_ethtool_add_mock_stats(struct netdevsim *ns,
+ u64 *data)
+{
+ unsigned int start, i;
+ const u8 *stats_base;
+ const u64_stats_t *p;
+ size_t offset;
+
+ stats_base = (const u8 *)&ns->ethtool.mock_stats;
+
+ data += NSIM_STATS_LEN;
+
+ do {
+ start = u64_stats_fetch_begin(&ns->ethtool.mock_stats.syncp);
+ for (i = 0; i < NSIM_MOCK_STATS_LEN; i++) {
+ offset = nsim_mock_stats_desc[i].offset;
+
+ p = (const u64_stats_t *)(stats_base + offset);
+ data[i] = u64_stats_read(p);
+ }
+ } while (u64_stats_fetch_retry(&ns->ethtool.mock_stats.syncp, start));
+}
+
static void nsim_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats,
u64 *data)
{
+ struct netdevsim *ns;
struct rtnl_link_stats64 rtstats = {};
int i;
@@ -235,6 +281,33 @@ static void nsim_get_ethtool_stats(struct net_device *dev,
for (i = 0; i < NSIM_STATS_LEN; i++)
data[i] = *(u64 *)((u8 *)&rtstats + nsim_stats_desc[i].offset);
+
+ ns = netdev_priv(dev);
+
+ if (ns->ethtool.mock_stats.enabled)
+ nsim_ethtool_add_mock_stats(ns, data);
+}
+
+#define NSIM_MOCK_STATS_INTERVAL_MS 100
+
+static void nsim_mock_stats_traffic_bump(struct nsim_mock_stats *stats)
+{
+ if (stats->enabled) {
+ stats->hw_out_of_buffer += 1;
+ stats->hw_out_of_sequence += 1;
+ stats->hw_packet_seq_err += 1;
+ }
+}
+
+static void nsim_mock_stats_traffic_work(struct work_struct *work)
+{
+ struct nsim_mock_stats *stats;
+
+ stats = container_of(work, struct nsim_mock_stats, traffic_dw.work);
+ nsim_mock_stats_traffic_bump(stats);
+
+ schedule_delayed_work(&stats->traffic_dw,
+ msecs_to_jiffies(NSIM_MOCK_STATS_INTERVAL_MS));
}
static const struct ethtool_ops nsim_ethtool_ops = {
@@ -269,6 +342,44 @@ static void nsim_ethtool_ring_init(struct netdevsim *ns)
ns->ethtool.ring.tx_max_pending = 4096;
}
+static void mock_stats_reset(struct nsim_mock_stats *mock_stats)
+{
+ mock_stats->hw_out_of_buffer = 0;
+ mock_stats->hw_out_of_sequence = 0;
+ mock_stats->hw_packet_seq_err = 0;
+}
+
+static ssize_t mock_stats_enabled_write(struct file *filp,
+ const char __user *ubuf,
+ size_t count,
+ loff_t *offp)
+{
+ bool enabled;
+ int r;
+ struct nsim_mock_stats *mock_stats = filp->private_data;
+ struct dentry *dentry = filp->f_path.dentry;
+
+ r = kstrtobool_from_user(ubuf, count, &enabled);
+ if (!r) {
+ r = debugfs_file_get(dentry);
+ if (unlikely(r))
+ return r;
+
+ mock_stats->enabled = enabled;
+ if (!enabled)
+ mock_stats_reset(mock_stats);
+
+ debugfs_file_put(dentry);
+ }
+
+ return count;
+}
+
+static struct debugfs_short_fops mock_stats_fops = {
+ .write = mock_stats_enabled_write,
+ .llseek = generic_file_llseek
+};
+
void nsim_ethtool_init(struct netdevsim *ns)
{
struct dentry *ethtool, *dir;
@@ -305,4 +416,18 @@ void nsim_ethtool_init(struct netdevsim *ns)
&ns->ethtool.ring.rx_mini_max_pending);
debugfs_create_u32("tx_max_pending", 0600, dir,
&ns->ethtool.ring.tx_max_pending);
+
+ dir = debugfs_create_dir("mock_stats", ethtool);
+ debugfs_create_file("enabled", 0600, dir, &ns->ethtool.mock_stats,
+ &mock_stats_fops);
+
+ INIT_DELAYED_WORK(&ns->ethtool.mock_stats.traffic_dw,
+ &nsim_mock_stats_traffic_work);
+ schedule_delayed_work(&ns->ethtool.mock_stats.traffic_dw,
+ msecs_to_jiffies(NSIM_MOCK_STATS_INTERVAL_MS));
+}
+
+void nsim_ethtool_exit(struct netdevsim *ns)
+{
+ cancel_delayed_work_sync(&ns->ethtool.mock_stats.traffic_dw);
}
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index 39fe28af48b9..d1864b7cbbd5 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -1143,6 +1143,7 @@ void nsim_destroy(struct netdevsim *ns)
rtnl_unlock();
if (nsim_dev_port_is_pf(ns->nsim_dev_port))
nsim_exit_netdevsim(ns);
+ nsim_ethtool_exit(ns);
/* Put this intentionally late to exercise the orphaning path */
if (ns->page) {
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index bddd24c1389d..57631ec8887a 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -82,6 +82,15 @@ struct nsim_ethtool_pauseparam {
bool report_stats_tx;
};
+struct nsim_mock_stats {
+ u64 hw_out_of_sequence;
+ u64 hw_out_of_buffer;
+ u64 hw_packet_seq_err;
+ struct u64_stats_sync syncp;
+ struct delayed_work traffic_dw;
+ bool enabled;
+};
+
struct nsim_ethtool {
u32 get_err;
u32 set_err;
@@ -90,6 +99,7 @@ struct nsim_ethtool {
struct ethtool_coalesce coalesce;
struct ethtool_ringparam ring;
struct ethtool_fecparam fec;
+ struct nsim_mock_stats mock_stats;
};
struct nsim_rq {
@@ -150,6 +160,7 @@ void nsim_destroy(struct netdevsim *ns);
bool netdev_is_nsim(struct net_device *dev);
void nsim_ethtool_init(struct netdevsim *ns);
+void nsim_ethtool_exit(struct netdevsim *ns);
void nsim_udp_tunnels_debugfs_create(struct nsim_dev *nsim_dev);
int nsim_udp_tunnels_info_create(struct nsim_dev *nsim_dev,
--
2.50.1
Powered by blists - more mailing lists