Subject: [RFC PATCH net-next 1/2] devlink: Introduce stats to the port object

From: Michael Guralnik <[email-masked]>
Date: Thu, 7 Apr 2022 11:40:49 +0300

Introducing a mechanism for drivers to register statistics counters to
devlink ports.
Drivers to supply only name and a get method for the counter.

Signed-off-by: Michael Guralnik <[email-masked]>
---
 include/net/devlink.h | 28 +++++++++++++++
 net/core/devlink.c    | 80 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 108 insertions(+)

diff --git a/include/net/devlink.h b/include/net/devlink.h
index 8d5349d2fb68..e9c3996ed359 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -115,6 +115,11 @@ struct devlink_rate {
 	};
 };
 
+struct devlink_port_stats {
+	struct list_head stats_list;
+	int (*get)(struct devlink_port *port);
+};
+
 struct devlink_port {
 	struct list_head list;
 	struct list_head param_list;
@@ -135,6 +140,7 @@ struct devlink_port {
 	struct mutex reporters_lock; /* Protects reporter_list */
 
 	struct devlink_rate *devlink_rate;
+	struct devlink_port_stats stats;
 };
 
 struct devlink_port_new_attrs {
@@ -387,6 +393,16 @@ struct devlink_param_gset_ctx {
 	enum devlink_param_cmode cmode;
 };
 
+struct devlink_port_stat {
+	char *name;
+	u64 val;
+};
+
+struct devlink_port_stat_item {
+	struct list_head list;
+	const struct devlink_port_stat *stat;
+};
+
 /**
  * struct devlink_flash_notify - devlink dev flash notify data
  * @status_msg: current status string
@@ -1712,6 +1728,18 @@ devlink_trap_policers_unregister(struct devlink *devlink,
 				 const struct devlink_trap_policer *policers,
 				 size_t policers_count);
 
+void devlink_port_stat_unregister(struct devlink_port *port,
+				  const struct devlink_port_stat *stat);
+
+int devlink_port_stats_register(struct devlink_port *port,
+				const struct devlink_port_stat *stats,
+				size_t stats_count,
+				int (*get)(struct devlink_port *port));
+
+void devlink_port_stats_unregister(struct devlink_port *port,
+				   const struct devlink_port_stat *stats,
+				   size_t stats_count);
+
 #if IS_ENABLED(CONFIG_NET_DEVLINK)
 
 struct devlink *__must_check devlink_try_get(struct devlink *devlink);
diff --git a/net/core/devlink.c b/net/core/devlink.c
index fcd9f6d85cf1..9636d7998630 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -4740,6 +4740,85 @@ static void devlink_param_notify(struct devlink *devlink,
 				msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
 }
 
+struct devlink_port_stat_item *
+devlink_port_stat_find_by_name(struct list_head *stats_list, const char *stat_name)
+{
+	struct devlink_port_stat_item *stat_item;
+
+	list_for_each_entry(stat_item, stats_list, list)
+		if (!strcmp(stat_item->stat->name, stat_name))
+			return stat_item;
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(devlink_port_stat_find_by_name);
+
+void devlink_port_stat_unregister(struct devlink_port *port,
+			          const struct devlink_port_stat *stat)
+{
+	struct devlink_port_stat_item *stat_item;
+
+	stat_item =
+		devlink_port_stat_find_by_name(&port->stats.stats_list, stat->name);
+	WARN_ON(!stat_item);
+	list_del(&stat_item->list);
+	kfree(stat_item);
+}
+
+static int devlink_port_stat_register(struct devlink_port *port,
+				      const struct devlink_port_stat *stats)
+{
+	struct devlink_port_stat_item *stat_item;
+
+	WARN_ON(devlink_port_stat_find_by_name(&port->stats.stats_list, stats->name));
+
+	stat_item = kzalloc(sizeof(*stat_item), GFP_KERNEL);
+	if (!stat_item)
+		return -ENOMEM;
+
+	stat_item->stat = stats;
+
+	list_add_tail(&stat_item->list, &port->stats.stats_list);
+	return 0;
+}
+
+void devlink_port_stats_unregister(struct devlink_port *port,
+				   const struct devlink_port_stat *stats,
+				   size_t stats_count)
+{
+	const struct devlink_port_stat *stat = stats;
+	int i;
+
+	for (i = 0; i < stats_count; i++, stat++)
+		devlink_port_stat_unregister(port, stat);
+}
+EXPORT_SYMBOL_GPL(devlink_port_stats_unregister);
+
+int devlink_port_stats_register(struct devlink_port *port,
+				const struct devlink_port_stat *stats,
+				size_t stats_count,
+				int (*get)(struct devlink_port *port))
+{
+	const struct devlink_port_stat *stat = stats;
+	int i, err;
+
+	port->stats.get = get;
+	for (i = 0; i < stats_count; i++, stat++) {
+		err = devlink_port_stat_register(port, stat);
+		if (err)
+			goto rollback;
+	}
+	return 0;
+
+rollback:
+	if (!i)
+		return err;
+
+	for (stat--; i > 0; i--, stat--)
+		devlink_port_stat_unregister(port, stat);
+	return err;
+}
+EXPORT_SYMBOL_GPL(devlink_port_stats_register);
+
 static int devlink_nl_cmd_param_get_dumpit(struct sk_buff *msg,
 					   struct netlink_callback *cb)
 {
@@ -9281,6 +9360,7 @@ int devlink_port_register(struct devlink *devlink,
 	list_add_tail(&devlink_port->list, &devlink->port_list);
 	INIT_LIST_HEAD(&devlink_port->param_list);
 	INIT_LIST_HEAD(&devlink_port->region_list);
+	INIT_LIST_HEAD(&devlink_port->stats.stats_list);
 	mutex_unlock(&devlink->lock);
 	INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn);
 	devlink_port_type_warn_schedule(devlink_port);
-- 
2.17.2