Netdev List
 help / color / mirror / Atom feed
From: MD Danish Anwar <danishanwar@ti.com>
To: "David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Simon Horman <horms@kernel.org>, Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	MD Danish Anwar <danishanwar@ti.com>,
	Roger Quadros <rogerq@kernel.org>,
	Andrew Lunn <andrew+netdev@lunn.ch>,
	Jacob Keller <jacob.e.keller@intel.com>,
	"Meghana Malladi" <m-malladi@ti.com>,
	David Carlier <devnexen@gmail.com>,
	"Vadim Fedorenko" <vadim.fedorenko@linux.dev>,
	Kevin Hao <haokexin@gmail.com>,
	Himanshu Mittal <h-mittal1@ti.com>,
	Hangbin Liu <liuhangbin@gmail.com>,
	Markus Elfring <elfring@users.sourceforge.net>,
	Fernando Fernandez Mancera <fmancera@suse.de>,
	Jan Vaclav <jvaclav@redhat.com>
Cc: <netdev@vger.kernel.org>, <linux-doc@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>,
	<linux-arm-kernel@lists.infradead.org>
Subject: [PATCH net-next v3 1/3] net: hsr: Add standard LRE stats via RTM_GETSTATS / IFLA_STATS_LINK_XSTATS
Date: Mon, 8 Jun 2026 15:39:28 +0530	[thread overview]
Message-ID: <20260608100930.210149-2-danishanwar@ti.com> (raw)
In-Reply-To: <20260608100930.210149-1-danishanwar@ti.com>

Per the IEC-62439-3 specification the Link Redundancy Entity (LRE)
maintains a well-defined set of counters applicable to both software
and offloaded HSR/PRP implementations. Define these counters as
individual netlink attributes inside a LINK_XSTATS_TYPE_HSR nest,
following the approach used by bridge and bond with IFLA_STATS_LINK_XSTATS.

The full IEC-62439-3 MIB counter set is represented, with per-port (A,
B, C) granularity where applicable:

  lreCntTx{A,B,C}          - sent HSR/PRP tagged frames per port
  lreCntRx{A,B,C}          - received HSR/PRP tagged frames per port
  lreCntErrWrongLan{A,B,C} - received frames with wrong LAN ID (PRP)
  lreCntErrors{A,B,C}      - received frames with errors per port
  lreCntUnique{A,B,C}       - frames received without duplicate
  lreCntDuplicate{A,B,C}    - frames received with exactly one duplicate
  lreCntMulti{A,B,C}        - frames received with more than one duplicate
  lreCntOwnRx{A,B}          - own-address frames received (HSR only)

Each counter is encoded as its own HSR_XSTATS_* u64 netlink attribute.
Unsupported counters are initialised to ~0ULL by the kernel and omitted
from the netlink reply; user-space must treat an absent attribute as
"not available".

The UAPI attribute enum (HSR_XSTATS_*) is added to hsr_netlink.h.
LINK_XSTATS_TYPE_HSR is added to the LINK_XSTATS_TYPE_* enum in both
include/uapi/linux/if_link.h and tools/include/uapi/linux/if_link.h.

A kernel-internal struct hsr_lre_stats (in linux/if_hsr.h) is provided
for offload drivers to fill via ndo_get_offload_stats. Unsupported
fields must be left at the ~0ULL value initialised by the HSR layer
before calling the NDO.

The HSR stack calls ndo_get_offload_stats(IFLA_STATS_LINK_XSTATS) on
slave A to collect offload counters.

Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
---
 include/linux/if_hsr.h             |  48 +++++++++++
 include/uapi/linux/hsr_netlink.h   |  56 ++++++++++++
 include/uapi/linux/if_link.h       |   1 +
 net/hsr/hsr_netlink.c              | 132 +++++++++++++++++++++++++++--
 tools/include/uapi/linux/if_link.h |   1 +
 5 files changed, 230 insertions(+), 8 deletions(-)

diff --git a/include/linux/if_hsr.h b/include/linux/if_hsr.h
index f4cf2dd36d193..b8c20f0906194 100644
--- a/include/linux/if_hsr.h
+++ b/include/linux/if_hsr.h
@@ -38,6 +38,54 @@ struct hsr_tag {
 
 #define HSR_HLEN	6
 
+/**
+ * struct hsr_lre_stats - Kernel-internal IEC-62439-3 LRE counter set.
+ *
+ * This is the buffer type written by ndo_get_offload_stats() when called
+ * with attr_id == IFLA_STATS_LINK_XSTATS on an HSR slave device.  Each
+ * field maps to one HSR_XSTATS_* netlink attribute.  Fields that the
+ * offload driver does not support must be left at the initialised value of
+ * ~0ULL; the HSR layer will skip those when building the netlink reply.
+ *
+ * Per-port suffix: _a = port A (slave 1 / LAN-A),
+ *                  _b = port B (slave 2 / LAN-B),
+ *                  _c = interlink / application interface.
+ *
+ * @cnt_tx_a: lreCntTxA - sent HSR/PRP tagged frames on port A.
+ * @cnt_tx_b: lreCntTxB - sent HSR/PRP tagged frames on port B.
+ * @cnt_tx_c: lreCntTxC - sent HSR/PRP tagged frames on port C.
+ * @cnt_rx_a: lreCntRxA - received HSR/PRP tagged frames on port A.
+ * @cnt_rx_b: lreCntRxB - received HSR/PRP tagged frames on port B.
+ * @cnt_rx_c: lreCntRxC - received HSR/PRP tagged frames on port C.
+ * @cnt_err_wrong_lan_a: lreCntErrWrongLanA - wrong LAN ID frames on port A.
+ * @cnt_err_wrong_lan_b: lreCntErrWrongLanB - wrong LAN ID frames on port B.
+ * @cnt_err_wrong_lan_c: lreCntErrWrongLanC - wrong LAN ID frames on port C.
+ * @cnt_errors_a: lreCntErrorsA - received frames with errors on port A.
+ * @cnt_errors_b: lreCntErrorsB - received frames with errors on port B.
+ * @cnt_errors_c: lreCntErrorsC - received frames with errors on port C.
+ * @cnt_unique_a: lreCntUniqueA - frames received without duplicate on port A.
+ * @cnt_unique_b: lreCntUniqueB - frames received without duplicate on port B.
+ * @cnt_unique_c: lreCntUniqueC - frames received without duplicate on port C.
+ * @cnt_duplicate_a: lreCntDuplicateA - frames with one duplicate on port A.
+ * @cnt_duplicate_b: lreCntDuplicateB - frames with one duplicate on port B.
+ * @cnt_duplicate_c: lreCntDuplicateC - frames with one duplicate on port C.
+ * @cnt_multi_a: lreCntMultiA - frames with more than one duplicate on port A.
+ * @cnt_multi_b: lreCntMultiB - frames with more than one duplicate on port B.
+ * @cnt_multi_c: lreCntMultiC - frames with more than one duplicate on port C.
+ * @cnt_own_rx_a: lreCntOwnRxA - own-address frames received on port A.
+ * @cnt_own_rx_b: lreCntOwnRxB - own-address frames received on port B.
+ */
+struct hsr_lre_stats {
+	u64 cnt_tx_a, cnt_tx_b, cnt_tx_c;
+	u64 cnt_rx_a, cnt_rx_b, cnt_rx_c;
+	u64 cnt_err_wrong_lan_a, cnt_err_wrong_lan_b, cnt_err_wrong_lan_c;
+	u64 cnt_errors_a, cnt_errors_b, cnt_errors_c;
+	u64 cnt_unique_a, cnt_unique_b, cnt_unique_c;
+	u64 cnt_duplicate_a, cnt_duplicate_b, cnt_duplicate_c;
+	u64 cnt_multi_a, cnt_multi_b, cnt_multi_c;
+	u64 cnt_own_rx_a, cnt_own_rx_b;
+};
+
 #if IS_ENABLED(CONFIG_HSR)
 extern bool is_hsr_master(struct net_device *dev);
 extern int hsr_get_version(struct net_device *dev, enum hsr_version *ver);
diff --git a/include/uapi/linux/hsr_netlink.h b/include/uapi/linux/hsr_netlink.h
index d540ea9bbef4b..c414a2bb93b79 100644
--- a/include/uapi/linux/hsr_netlink.h
+++ b/include/uapi/linux/hsr_netlink.h
@@ -48,4 +48,60 @@ enum {
 };
 #define HSR_C_MAX (__HSR_C_MAX - 1)
 
+/* HSR/PRP LRE extended statistics attributes.
+ * Reported inside LINK_XSTATS_TYPE_HSR (RTM_GETSTATS / ip stats show).
+ * Counter definitions follow IEC-62439-3 MIB naming.
+ *
+ * All counters are __u64.  Unsupported counters are omitted from the
+ * netlink reply; user-space must treat an absent attribute as "not available".
+ *
+ * Per-port suffix: _A = port A (slave 1), _B = port B (slave 2),
+ *                  _C = interlink / application interface.
+ */
+enum {
+	/* Sent HSR/PRP tagged frames per port */
+	HSR_XSTATS_CNT_TX_A = 1,
+	HSR_XSTATS_CNT_TX_B,
+	HSR_XSTATS_CNT_TX_C,
+
+	/* Received HSR/PRP tagged frames per port */
+	HSR_XSTATS_CNT_RX_A,
+	HSR_XSTATS_CNT_RX_B,
+	HSR_XSTATS_CNT_RX_C,
+
+	/* Received frames with wrong LAN ID (PRP only) per port */
+	HSR_XSTATS_CNT_ERR_WRONG_LAN_A,
+	HSR_XSTATS_CNT_ERR_WRONG_LAN_B,
+	HSR_XSTATS_CNT_ERR_WRONG_LAN_C,
+
+	/* Received frames with errors per port */
+	HSR_XSTATS_CNT_ERRORS_A,
+	HSR_XSTATS_CNT_ERRORS_B,
+	HSR_XSTATS_CNT_ERRORS_C,
+
+	/* Frames received with no duplicate per port */
+	HSR_XSTATS_CNT_UNIQUE_A,
+	HSR_XSTATS_CNT_UNIQUE_B,
+	HSR_XSTATS_CNT_UNIQUE_C,
+
+	/* Frames received with exactly one duplicate per port */
+	HSR_XSTATS_CNT_DUPLICATE_A,
+	HSR_XSTATS_CNT_DUPLICATE_B,
+	HSR_XSTATS_CNT_DUPLICATE_C,
+
+	/* Frames received with more than one duplicate per port */
+	HSR_XSTATS_CNT_MULTI_A,
+	HSR_XSTATS_CNT_MULTI_B,
+	HSR_XSTATS_CNT_MULTI_C,
+
+	/* Frames received matching this node's own address (HSR only) */
+	HSR_XSTATS_CNT_OWN_RX_A,
+	HSR_XSTATS_CNT_OWN_RX_B,
+
+	HSR_XSTATS_PAD,
+	__HSR_XSTATS_MAX,
+};
+
+#define HSR_XSTATS_MAX (__HSR_XSTATS_MAX - 1)
+
 #endif /* __UAPI_HSR_NETLINK_H */
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 79ce4bc24cba6..3dcd51e64f29d 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -1905,6 +1905,7 @@ enum {
 	LINK_XSTATS_TYPE_UNSPEC,
 	LINK_XSTATS_TYPE_BRIDGE,
 	LINK_XSTATS_TYPE_BOND,
+	LINK_XSTATS_TYPE_HSR,
 	__LINK_XSTATS_TYPE_MAX
 };
 #define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1)
diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c
index db0b0af7a6920..9455f65868ca2 100644
--- a/net/hsr/hsr_netlink.c
+++ b/net/hsr/hsr_netlink.c
@@ -11,6 +11,8 @@
 #include <linux/kernel.h>
 #include <net/rtnetlink.h>
 #include <net/genetlink.h>
+#include <uapi/linux/if_link.h>
+#include <uapi/linux/hsr_netlink.h>
 #include "hsr_main.h"
 #include "hsr_device.h"
 #include "hsr_framereg.h"
@@ -189,15 +191,129 @@ static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev)
 	return -EMSGSIZE;
 }
 
+/*
+ * Number of real HSR_XSTATS_* u64 counter attributes.
+ * Real counters run from HSR_XSTATS_CNT_TX_A(1) through
+ * HSR_XSTATS_CNT_OWN_RX_B(25); HSR_XSTATS_PAD is not a counter.
+ */
+#define HSR_XSTATS_CNT_ATTRS (HSR_XSTATS_PAD - 1)
+
+static size_t hsr_get_linkxstats_size(const struct net_device *dev, int attr)
+{
+	if (attr != IFLA_STATS_LINK_XSTATS)
+		return 0;
+
+	/* Nest header (LINK_XSTATS_TYPE_HSR) + one u64 nla per counter */
+	return nla_total_size(0) +
+	       HSR_XSTATS_CNT_ATTRS * nla_total_size_64bit(sizeof(u64));
+}
+
+/* Put a u64 counter attribute; skip if value is ~0ULL (unsupported). */
+static int hsr_put_stat(struct sk_buff *skb, int attr_id, u64 val)
+{
+	if (val == ~0ULL)
+		return 0;
+	return nla_put_u64_64bit(skb, attr_id, val, HSR_XSTATS_PAD);
+}
+
+static int hsr_fill_linkxstats(struct sk_buff *skb,
+			       const struct net_device *dev,
+			       int *prividx, int attr)
+{
+	struct hsr_lre_stats stats;
+	struct hsr_port *port;
+	struct hsr_priv *hsr = netdev_priv(dev);
+	struct nlattr *nest;
+	int s_prividx = *prividx;
+	int err;
+
+	if (attr != IFLA_STATS_LINK_XSTATS)
+		return 0;
+
+	*prividx = 0;
+
+	nest = nla_nest_start_noflag(skb, LINK_XSTATS_TYPE_HSR);
+	if (!nest)
+		return -EMSGSIZE;
+
+	/* Initialise all counters to ~0ULL ("unsupported") */
+	memset(&stats, 0xff, sizeof(stats));
+
+	/* Ask the offload driver (if any) via ndo_get_offload_stats on slave A.
+	 * Guard with ndo_has_offload_stats so we only call drivers that
+	 * explicitly declare support for IFLA_STATS_LINK_XSTATS, avoiding
+	 * spurious -EINVAL from drivers that implement the NDO for a different
+	 * attr_id (e.g. IFLA_OFFLOAD_XSTATS_CPU_HIT).
+	 */
+	port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
+	if (port) {
+		const struct net_device_ops *ops = port->dev->netdev_ops;
+
+		if (ops->ndo_has_offload_stats &&
+		    ops->ndo_has_offload_stats(port->dev,
+					       IFLA_STATS_LINK_XSTATS) &&
+		    ops->ndo_get_offload_stats) {
+			err = ops->ndo_get_offload_stats(IFLA_STATS_LINK_XSTATS,
+							 port->dev, &stats);
+			if (err && err != -EOPNOTSUPP) {
+				nla_nest_cancel(skb, nest);
+				return err;
+			}
+		}
+	}
+
+#define PUT_STAT(attr, field) \
+	do { \
+		if (HSR_XSTATS_##attr < s_prividx) \
+			break; \
+		if (hsr_put_stat(skb, HSR_XSTATS_##attr, stats.field)) { \
+			*prividx = HSR_XSTATS_##attr; \
+			nla_nest_end(skb, nest); \
+			return -EMSGSIZE; \
+		} \
+	} while (0)
+
+	PUT_STAT(CNT_TX_A,		cnt_tx_a);
+	PUT_STAT(CNT_TX_B,		cnt_tx_b);
+	PUT_STAT(CNT_TX_C,		cnt_tx_c);
+	PUT_STAT(CNT_RX_A,		cnt_rx_a);
+	PUT_STAT(CNT_RX_B,		cnt_rx_b);
+	PUT_STAT(CNT_RX_C,		cnt_rx_c);
+	PUT_STAT(CNT_ERR_WRONG_LAN_A,	cnt_err_wrong_lan_a);
+	PUT_STAT(CNT_ERR_WRONG_LAN_B,	cnt_err_wrong_lan_b);
+	PUT_STAT(CNT_ERR_WRONG_LAN_C,	cnt_err_wrong_lan_c);
+	PUT_STAT(CNT_ERRORS_A,		cnt_errors_a);
+	PUT_STAT(CNT_ERRORS_B,		cnt_errors_b);
+	PUT_STAT(CNT_ERRORS_C,		cnt_errors_c);
+	PUT_STAT(CNT_UNIQUE_A,		cnt_unique_a);
+	PUT_STAT(CNT_UNIQUE_B,		cnt_unique_b);
+	PUT_STAT(CNT_UNIQUE_C,		cnt_unique_c);
+	PUT_STAT(CNT_DUPLICATE_A,	cnt_duplicate_a);
+	PUT_STAT(CNT_DUPLICATE_B,	cnt_duplicate_b);
+	PUT_STAT(CNT_DUPLICATE_C,	cnt_duplicate_c);
+	PUT_STAT(CNT_MULTI_A,		cnt_multi_a);
+	PUT_STAT(CNT_MULTI_B,		cnt_multi_b);
+	PUT_STAT(CNT_MULTI_C,		cnt_multi_c);
+	PUT_STAT(CNT_OWN_RX_A,		cnt_own_rx_a);
+	PUT_STAT(CNT_OWN_RX_B,		cnt_own_rx_b);
+
+#undef PUT_STAT
+
+	nla_nest_end(skb, nest);
+	return 0;
+}
+
 static struct rtnl_link_ops hsr_link_ops __read_mostly = {
-	.kind		= "hsr",
-	.maxtype	= IFLA_HSR_MAX,
-	.policy		= hsr_policy,
-	.priv_size	= sizeof(struct hsr_priv),
-	.setup		= hsr_dev_setup,
-	.newlink	= hsr_newlink,
-	.dellink	= hsr_dellink,
-	.fill_info	= hsr_fill_info,
+	.kind			= "hsr",
+	.maxtype		= IFLA_HSR_MAX,
+	.policy			= hsr_policy,
+	.priv_size		= sizeof(struct hsr_priv),
+	.setup			= hsr_dev_setup,
+	.newlink		= hsr_newlink,
+	.dellink		= hsr_dellink,
+	.fill_info		= hsr_fill_info,
+	.get_linkxstats_size	= hsr_get_linkxstats_size,
+	.fill_linkxstats	= hsr_fill_linkxstats,
 };
 
 /* attribute policy */
diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h
index 7e46ca4cd31bb..13f122996d01a 100644
--- a/tools/include/uapi/linux/if_link.h
+++ b/tools/include/uapi/linux/if_link.h
@@ -1844,6 +1844,7 @@ enum {
 	LINK_XSTATS_TYPE_UNSPEC,
 	LINK_XSTATS_TYPE_BRIDGE,
 	LINK_XSTATS_TYPE_BOND,
+	LINK_XSTATS_TYPE_HSR,
 	__LINK_XSTATS_TYPE_MAX
 };
 #define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1)
-- 
2.34.1


  reply	other threads:[~2026-06-08 10:09 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-08 10:09 [PATCH net-next v3 0/3] Add standard stats for HSR/PRP MD Danish Anwar
2026-06-08 10:09 ` MD Danish Anwar [this message]
2026-06-08 10:09 ` [PATCH net-next v3 2/3] net: ti: icssg: Add static_assert to guard stat array counts MD Danish Anwar
2026-06-08 10:09 ` [PATCH net-next v3 3/3] net: ti: icssg: Add HSR offload statistics support MD Danish Anwar
2026-06-10 18:47 ` [PATCH net-next v3 0/3] Add standard stats for HSR/PRP Simon Horman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260608100930.210149-2-danishanwar@ti.com \
    --to=danishanwar@ti.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=corbet@lwn.net \
    --cc=davem@davemloft.net \
    --cc=devnexen@gmail.com \
    --cc=edumazet@google.com \
    --cc=elfring@users.sourceforge.net \
    --cc=fmancera@suse.de \
    --cc=h-mittal1@ti.com \
    --cc=haokexin@gmail.com \
    --cc=horms@kernel.org \
    --cc=jacob.e.keller@intel.com \
    --cc=jvaclav@redhat.com \
    --cc=kuba@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=liuhangbin@gmail.com \
    --cc=m-malladi@ti.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=rogerq@kernel.org \
    --cc=skhan@linuxfoundation.org \
    --cc=vadim.fedorenko@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox