Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next v2 0/3] net: dsa: yt921x: Add port qdisc tbf support
@ 2026-05-04 10:12 David Yang
  2026-05-04 10:12 ` [PATCH net-next v2 1/3] net: sched: tbf: add extack to offload params David Yang
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: David Yang @ 2026-05-04 10:12 UTC (permalink / raw)
  To: netdev
  Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Jamal Hadi Salim,
	Jiri Pirko, Simon Horman, linux-kernel

v1: https://lore.kernel.org/r/20260502215314.917687-1-mmyangfl@gmail.com
  - remove queue related register definiations
  - add missing extack param during tbf setup
v0: https://lore.kernel.org/r/20260409171209.2575583-1-mmyangfl@gmail.com
  - picked from old series
  - add extack to the offload struct
  - add all params to the offload struct

David Yang (3):
  net: sched: tbf: add extack to offload params
  net: sched: tbf: pass all params to offload users
  net: dsa: yt921x: Add port qdisc tbf support

 drivers/net/dsa/yt921x.c | 126 +++++++++++++++++++++++++++++++++++++++
 drivers/net/dsa/yt921x.h |  18 ++++++
 include/net/pkt_cls.h    |   7 ++-
 net/sched/sch_tbf.c      |  15 ++++-
 4 files changed, 162 insertions(+), 4 deletions(-)

-- 
2.53.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH net-next v2 1/3] net: sched: tbf: add extack to offload params
  2026-05-04 10:12 [PATCH net-next v2 0/3] net: dsa: yt921x: Add port qdisc tbf support David Yang
@ 2026-05-04 10:12 ` David Yang
  2026-05-04 10:12 ` [PATCH net-next v2 2/3] net: sched: tbf: pass all params to offload users David Yang
  2026-05-04 10:12 ` [PATCH net-next v2 3/3] net: dsa: yt921x: Add port qdisc tbf support David Yang
  2 siblings, 0 replies; 11+ messages in thread
From: David Yang @ 2026-05-04 10:12 UTC (permalink / raw)
  To: netdev
  Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Jamal Hadi Salim,
	Jiri Pirko, Simon Horman, linux-kernel

Drivers might have error messages to propagate to user space. Propagate
the netlink extack so that they can inform user space in a verbal way of
their limitations.

Signed-off-by: David Yang <mmyangfl@gmail.com>
---
 include/net/pkt_cls.h | 1 +
 net/sched/sch_tbf.c   | 9 +++++++--
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index 99ac747b7906..3bd08d7f39c1 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -1046,6 +1046,7 @@ struct tc_tbf_qopt_offload_replace_params {
 };
 
 struct tc_tbf_qopt_offload {
+	struct netlink_ext_ack *extack;
 	enum tc_tbf_command command;
 	u32 handle;
 	u32 parent;
diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
index f2340164f579..4576111fe075 100644
--- a/net/sched/sch_tbf.c
+++ b/net/sched/sch_tbf.c
@@ -139,7 +139,8 @@ static u64 psched_ns_t2l(const struct psched_ratecfg *r,
 	return len;
 }
 
-static void tbf_offload_change(struct Qdisc *sch)
+static void tbf_offload_change(struct Qdisc *sch,
+			       struct netlink_ext_ack *extack)
 {
 	struct tbf_sched_data *q = qdisc_priv(sch);
 	struct net_device *dev = qdisc_dev(sch);
@@ -148,6 +149,7 @@ static void tbf_offload_change(struct Qdisc *sch)
 	if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
 		return;
 
+	qopt.extack = extack;
 	qopt.command = TC_TBF_REPLACE;
 	qopt.handle = sch->handle;
 	qopt.parent = sch->parent;
@@ -166,6 +168,7 @@ static void tbf_offload_destroy(struct Qdisc *sch)
 	if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
 		return;
 
+	qopt.extack = NULL;
 	qopt.command = TC_TBF_DESTROY;
 	qopt.handle = sch->handle;
 	qopt.parent = sch->parent;
@@ -176,6 +179,7 @@ static int tbf_offload_dump(struct Qdisc *sch)
 {
 	struct tc_tbf_qopt_offload qopt;
 
+	qopt.extack = NULL;
 	qopt.command = TC_TBF_STATS;
 	qopt.handle = sch->handle;
 	qopt.parent = sch->parent;
@@ -193,6 +197,7 @@ static void tbf_offload_graft(struct Qdisc *sch, struct Qdisc *new,
 		.parent		= sch->parent,
 		.child_handle	= new->handle,
 		.command	= TC_TBF_GRAFT,
+		.extack		= extack,
 	};
 
 	qdisc_offload_graft_helper(qdisc_dev(sch), sch, new, old,
@@ -477,7 +482,7 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt,
 	qdisc_put(old);
 	err = 0;
 
-	tbf_offload_change(sch);
+	tbf_offload_change(sch, extack);
 done:
 	return err;
 }
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH net-next v2 2/3] net: sched: tbf: pass all params to offload users
  2026-05-04 10:12 [PATCH net-next v2 0/3] net: dsa: yt921x: Add port qdisc tbf support David Yang
  2026-05-04 10:12 ` [PATCH net-next v2 1/3] net: sched: tbf: add extack to offload params David Yang
@ 2026-05-04 10:12 ` David Yang
  2026-05-07  1:22   ` Jakub Kicinski
  2026-05-07  1:23   ` Jakub Kicinski
  2026-05-04 10:12 ` [PATCH net-next v2 3/3] net: dsa: yt921x: Add port qdisc tbf support David Yang
  2 siblings, 2 replies; 11+ messages in thread
From: David Yang @ 2026-05-04 10:12 UTC (permalink / raw)
  To: netdev
  Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Jamal Hadi Salim,
	Jiri Pirko, Simon Horman, linux-kernel

Drivers might have fine-grained control over queues. Pass all available
params so they can make use of them.

Signed-off-by: David Yang <mmyangfl@gmail.com>
---
 include/net/pkt_cls.h | 6 +++++-
 net/sched/sch_tbf.c   | 6 +++++-
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index 3bd08d7f39c1..d73c04e3d919 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -1040,8 +1040,12 @@ enum tc_tbf_command {
 };
 
 struct tc_tbf_qopt_offload_replace_params {
+	u32		limit;
+	u32		max_size;
+	s64		buffer;
+	s64		mtu;
 	struct psched_ratecfg rate;
-	u32 max_size;
+	struct psched_ratecfg peak;
 	struct gnet_stats_queue *qstats;
 };
 
diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
index 4576111fe075..8a1110d59dec 100644
--- a/net/sched/sch_tbf.c
+++ b/net/sched/sch_tbf.c
@@ -153,8 +153,12 @@ static void tbf_offload_change(struct Qdisc *sch,
 	qopt.command = TC_TBF_REPLACE;
 	qopt.handle = sch->handle;
 	qopt.parent = sch->parent;
-	qopt.replace_params.rate = q->rate;
+	qopt.replace_params.limit = q->limit;
 	qopt.replace_params.max_size = q->max_size;
+	qopt.replace_params.buffer = q->buffer;
+	qopt.replace_params.mtu = q->mtu;
+	qopt.replace_params.rate = q->rate;
+	qopt.replace_params.peak = q->peak;
 	qopt.replace_params.qstats = &sch->qstats;
 
 	dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TBF, &qopt);
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH net-next v2 3/3] net: dsa: yt921x: Add port qdisc tbf support
  2026-05-04 10:12 [PATCH net-next v2 0/3] net: dsa: yt921x: Add port qdisc tbf support David Yang
  2026-05-04 10:12 ` [PATCH net-next v2 1/3] net: sched: tbf: add extack to offload params David Yang
  2026-05-04 10:12 ` [PATCH net-next v2 2/3] net: sched: tbf: pass all params to offload users David Yang
@ 2026-05-04 10:12 ` David Yang
  2026-05-07  1:22   ` Jakub Kicinski
  2026-05-07  1:23   ` Jakub Kicinski
  2 siblings, 2 replies; 11+ messages in thread
From: David Yang @ 2026-05-04 10:12 UTC (permalink / raw)
  To: netdev
  Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Jamal Hadi Salim,
	Jiri Pirko, Simon Horman, linux-kernel

Enable port shaping and support limiting the rate of outgoing traffic.

Signed-off-by: David Yang <mmyangfl@gmail.com>
---
 drivers/net/dsa/yt921x.c | 126 +++++++++++++++++++++++++++++++++++++++
 drivers/net/dsa/yt921x.h |  18 ++++++
 2 files changed, 144 insertions(+)

diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c
index fd1fdcd5f9a3..fd77e1ef53bb 100644
--- a/drivers/net/dsa/yt921x.c
+++ b/drivers/net/dsa/yt921x.c
@@ -24,6 +24,7 @@
 #include <net/dsa.h>
 #include <net/dscp.h>
 #include <net/ieee8021q.h>
+#include <net/pkt_cls.h>
 
 #include "yt921x.h"
 
@@ -1272,6 +1273,17 @@ yt921x_marker_tfm_police(struct yt921x_marker *marker,
 				 priv, port, extack);
 }
 
+static int
+yt921x_marker_tfm_shape(struct yt921x_marker *marker, u64 rate, u64 burst,
+			unsigned int flags, struct yt921x_priv *priv, int port,
+			struct netlink_ext_ack *extack)
+{
+	return yt921x_marker_tfm(marker, rate, burst, flags,
+				 priv->port_shape_slot_ns, YT921X_SHAPE_CIR_MAX,
+				 YT921X_SHAPE_CBS_MAX, YT921X_SHAPE_UNIT_MAX,
+				 priv, port, extack);
+}
+
 static int
 yt921x_police_validate(const struct flow_action_police *police,
 		       const struct flow_action *action,
@@ -1378,6 +1390,112 @@ yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port,
 	return res;
 }
 
+static int
+yt921x_tbf_validate(struct yt921x_priv *priv,
+		    const struct tc_tbf_qopt_offload *qopt)
+{
+	struct netlink_ext_ack *extack = qopt->extack;
+
+	if (qopt->parent != TC_H_ROOT) {
+		NL_SET_ERR_MSG_MOD(extack, "Parent should be \"root\"");
+		return -EOPNOTSUPP;
+	}
+
+	switch (qopt->command) {
+	case TC_TBF_REPLACE: {
+		const struct tc_tbf_qopt_offload_replace_params *p;
+
+		p = &qopt->replace_params;
+
+		if (p->mtu || p->peak.rate_bytes_ps) {
+			NL_SET_ERR_MSG_MOD(extack,
+					   "Offload not supported when mtu/peakrate is configured");
+			return -EOPNOTSUPP;
+		}
+
+		if (!p->rate.mpu) {
+			NL_SET_ERR_MSG_MOD(extack, "Assuming mpu = 64");
+		} else if (p->rate.mpu != 64) {
+			NL_SET_ERR_MSG_MOD(extack,
+					   "Offload not supported when mpu is other than 64");
+			return -EOPNOTSUPP;
+		}
+
+		break;
+	}
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int
+yt921x_dsa_port_setup_tc_tbf_port(struct dsa_switch *ds, int port,
+				  const struct tc_tbf_qopt_offload *qopt)
+{
+	struct yt921x_priv *priv = to_yt921x_priv(ds);
+	struct netlink_ext_ack *extack = qopt->extack;
+	u32 ctrls[2];
+	int res;
+
+	switch (qopt->command) {
+	case TC_TBF_DESTROY:
+		ctrls[0] = 0;
+		ctrls[1] = 0;
+		break;
+	case TC_TBF_REPLACE: {
+		const struct tc_tbf_qopt_offload_replace_params *p;
+		struct yt921x_marker marker;
+
+		p = &qopt->replace_params;
+
+		res = yt921x_marker_tfm_shape(&marker, p->rate.rate_bytes_ps,
+					      p->max_size,
+					      YT921X_MARKER_SINGLE_BUCKET,
+					      priv, port, extack);
+		if (res)
+			return res;
+
+		ctrls[0] = YT921X_PORT_SHAPE_CTRLa_CIR(marker.cir) |
+			   YT921X_PORT_SHAPE_CTRLa_CBS(marker.cbs);
+		ctrls[1] = YT921X_PORT_SHAPE_CTRLb_UNIT(marker.unit) |
+			   YT921X_PORT_SHAPE_CTRLb_EN;
+		break;
+	}
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	mutex_lock(&priv->reg_lock);
+	res = yt921x_reg64_write(priv, YT921X_PORTn_SHAPE_CTRL(port), ctrls);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int
+yt921x_dsa_port_setup_tc(struct dsa_switch *ds, int port,
+			 enum tc_setup_type type, void *type_data)
+{
+	struct yt921x_priv *priv = to_yt921x_priv(ds);
+	int res;
+
+	switch (type) {
+	case TC_SETUP_QDISC_TBF: {
+		const struct tc_tbf_qopt_offload *qopt = type_data;
+
+		res = yt921x_tbf_validate(priv, qopt);
+		if (res)
+			return res;
+
+		return yt921x_dsa_port_setup_tc_tbf_port(ds, port, qopt);
+	}
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
 static int
 yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress)
 {
@@ -3524,6 +3642,13 @@ static int yt921x_chip_setup_tc(struct yt921x_priv *priv)
 		return res;
 	priv->meter_slot_ns = ctrl * op_ns;
 
+	ctrl = max(priv->port_shape_slot_ns / op_ns,
+		   YT921X_PORT_SHAPE_SLOT_MIN);
+	res = yt921x_reg_write(priv, YT921X_PORT_SHAPE_SLOT, ctrl);
+	if (res)
+		return res;
+	priv->port_shape_slot_ns = ctrl * op_ns;
+
 	return 0;
 }
 
@@ -3680,6 +3805,7 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
 	/* rate */
 	.port_policer_del	= yt921x_dsa_port_policer_del,
 	.port_policer_add	= yt921x_dsa_port_policer_add,
+	.port_setup_tc		= yt921x_dsa_port_setup_tc,
 	/* hsr */
 	.port_hsr_leave		= dsa_port_simple_hsr_leave,
 	.port_hsr_join		= dsa_port_simple_hsr_join,
diff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/yt921x.h
index 546b12a8994a..70fa780c337f 100644
--- a/drivers/net/dsa/yt921x.h
+++ b/drivers/net/dsa/yt921x.h
@@ -531,6 +531,19 @@ enum yt921x_app_selector {
 #define  YT921X_MIRROR_PORT_M			GENMASK(3, 0)
 #define   YT921X_MIRROR_PORT(x)				FIELD_PREP(YT921X_MIRROR_PORT_M, (x))
 
+#define YT921X_PORT_SHAPE_SLOT		0x34000c
+#define  YT921X_PORT_SHAPE_SLOT_SLOT_M		GENMASK(11, 0)
+#define YT921X_PORTn_SHAPE_CTRL(port)	(0x354000 + 8 * (port))
+#define  YT921X_PORT_SHAPE_CTRLb_EN		BIT(4)
+#define  YT921X_PORT_SHAPE_CTRLb_PKT_MODE	BIT(3)	/* 0: byte rate mode */
+#define  YT921X_PORT_SHAPE_CTRLb_UNIT_M		GENMASK(2, 0)
+#define   YT921X_PORT_SHAPE_CTRLb_UNIT(x)		FIELD_PREP(YT921X_PORT_SHAPE_CTRLb_UNIT_M, (x))
+#define  YT921X_PORT_SHAPE_CTRLa_CBS_M		GENMASK(31, 18)
+#define   YT921X_PORT_SHAPE_CTRLa_CBS(x)		FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CBS_M, (x))
+#define  YT921X_PORT_SHAPE_CTRLa_CIR_M		GENMASK(17, 0)
+#define   YT921X_PORT_SHAPE_CTRLa_CIR(x)		FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CIR_M, (x))
+#define YT921X_PORTn_SHAPE_STAT(port)	(0x356000 + 4 * (port))
+
 #define YT921X_EDATA_EXTMODE	0xfb
 #define YT921X_EDATA_LEN	0x100
 
@@ -556,6 +569,10 @@ enum yt921x_fdb_entry_status {
 #define YT921X_METER_UNIT_MAX	((1 << 3) - 1)
 #define YT921X_METER_CIR_MAX	((1 << 18) - 1)
 #define YT921X_METER_CBS_MAX	((1 << 16) - 1)
+#define YT921X_PORT_SHAPE_SLOT_MIN	80
+#define YT921X_SHAPE_UNIT_MAX	((1 << 3) - 1)
+#define YT921X_SHAPE_CIR_MAX	((1 << 18) - 1)
+#define YT921X_SHAPE_CBS_MAX	((1 << 14) - 1)
 
 #define YT921X_LAG_NUM		2
 #define YT921X_LAG_PORT_NUM	4
@@ -652,6 +669,7 @@ struct yt921x_priv {
 
 	const struct yt921x_info *info;
 	unsigned int meter_slot_ns;
+	unsigned int port_shape_slot_ns;
 	/* cache of dsa_cpu_ports(ds) */
 	u16 cpu_ports_mask;
 	unsigned char cycle_ns;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next v2 2/3] net: sched: tbf: pass all params to offload users
  2026-05-04 10:12 ` [PATCH net-next v2 2/3] net: sched: tbf: pass all params to offload users David Yang
@ 2026-05-07  1:22   ` Jakub Kicinski
  2026-05-07  1:23   ` Jakub Kicinski
  1 sibling, 0 replies; 11+ messages in thread
From: Jakub Kicinski @ 2026-05-07  1:22 UTC (permalink / raw)
  To: mmyangfl
  Cc: Jakub Kicinski, netdev, andrew, olteanv, davem, edumazet, pabeni,
	jhs, jiri, horms, linux-kernel

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: sched: tbf: pass all params to offload users

This commit exposes additional TBF parameters (limit, buffer, mtu, and peak
rate) to hardware offload drivers so they have finer-grained control over
queue configuration.

> diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
> index 3bd08d7f39c1..d73c04e3d919 100644
> --- a/include/net/pkt_cls.h
> +++ b/include/net/pkt_cls.h
> @@ -1040,8 +1040,12 @@ enum tc_tbf_command {
>  };
>  
>  struct tc_tbf_qopt_offload_replace_params {
> +	u32		limit;
> +	u32		max_size;
> +	s64		buffer;
> +	s64		mtu;

Are buffer and mtu the most clear variable names for the offload API? 

In sch_tbf.c, these fields store token bucket depths converted to nanoseconds,
rather than raw configuration bytes. Exposing time values under names that
typically imply byte sizes could mislead driver authors.

>  	struct psched_ratecfg rate;
> -	u32 max_size;
> +	struct psched_ratecfg peak;
>  	struct gnet_stats_queue *qstats;
>  };

[ ... ]

> diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
> index 4576111fe075..8a1110d59dec 100644
> --- a/net/sched/sch_tbf.c
> +++ b/net/sched/sch_tbf.c
> @@ -153,8 +153,12 @@ static void tbf_offload_change(struct Qdisc *sch,
>  	qopt.command = TC_TBF_REPLACE;
>  	qopt.handle = sch->handle;
>  	qopt.parent = sch->parent;
> -	qopt.replace_params.rate = q->rate;
> +	qopt.replace_params.limit = q->limit;
>  	qopt.replace_params.max_size = q->max_size;

Does passing q->max_size here provide the driver with the original burst size?

The software TBF overwrites q->max_size with min(burst, pburst) when a peak
rate is configured. As a result, the offload API only passes this overwritten
value, meaning a driver attempting to offload a dual-rate TBF is not given
the original burst size in bytes and would have to reverse-engineer it from
the nanosecond buffer value.

> +	qopt.replace_params.buffer = q->buffer;
> +	qopt.replace_params.mtu = q->mtu;

Will this cause the yt921x offload (introduced in the next patch) to
unconditionally reject all offloads?

The yt921x driver attempts to reject dual-rate offloads by checking if p->mtu
or p->peak.rate_bytes_ps are set. However, the tc userspace tool always
populates the mtu parameter with a default value (like 2047 bytes) for legacy
rate table compatibility.

Since tc always sets mtu, q->mtu will be non-zero, causing the yt921x driver
to return -EOPNOTSUPP and reject the configuration. 

Should the offload API explicitly pass the raw burst and pburst parameters in
bytes instead?

> +	qopt.replace_params.rate = q->rate;
> +	qopt.replace_params.peak = q->peak;
>  	qopt.replace_params.qstats = &sch->qstats;
>  
>  	dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TBF, &qopt);

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next v2 3/3] net: dsa: yt921x: Add port qdisc tbf support
  2026-05-04 10:12 ` [PATCH net-next v2 3/3] net: dsa: yt921x: Add port qdisc tbf support David Yang
@ 2026-05-07  1:22   ` Jakub Kicinski
  2026-05-07  1:23   ` Jakub Kicinski
  1 sibling, 0 replies; 11+ messages in thread
From: Jakub Kicinski @ 2026-05-07  1:22 UTC (permalink / raw)
  To: mmyangfl
  Cc: Jakub Kicinski, netdev, andrew, olteanv, davem, edumazet, pabeni,
	jhs, jiri, horms, linux-kernel

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: dsa: yt921x: Add port qdisc tbf support

This patch enables port shaping and adds support for limiting the rate of
outgoing traffic using the TBF qdisc offload.

> diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c
> index 42dca9617fb1..dc38e0dd22ee 100644
> --- a/drivers/net/dsa/yt921x.c
> +++ b/drivers/net/dsa/yt921x.c
[ ... ]
> @@ -1378,6 +1390,112 @@ yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port,
>  	return res;
>  }
>  
> +static int
> +yt921x_tbf_validate(struct yt921x_priv *priv,
> +		    const struct tc_tbf_qopt_offload *qopt)
> +{
[ ... ]
> +	switch (qopt->command) {
> +	case TC_TBF_REPLACE: {
> +		const struct tc_tbf_qopt_offload_replace_params *p;
> +
> +		p = &qopt->replace_params;
> +
> +		if (p->mtu || p->peak.rate_bytes_ps) {

The userspace tc utility unconditionally sets a default MTU (typically 2047)
when adding a TBF qdisc, which is passed to the driver via p->mtu. Since
p->mtu is never zero, will this unconditionally reject all offload requests?

> +			NL_SET_ERR_MSG_MOD(extack,
> +					   "Offload not supported when mtu/peakrate is configured");
> +			return -EOPNOTSUPP;
> +		}
> +
> +		if (!p->rate.mpu) {

Does this code miss validation for p->rate.overhead?

The TC TBF layer allows configuring overhead to account for additional
headers. If the hardware relies on fixed wire frame sizes and the driver
ignores p->rate.overhead, could this lead to a silent misconfiguration
where the hardware shapes traffic at a different effective rate than
requested?

> +			NL_SET_ERR_MSG_MOD(extack, "Assuming mpu = 64");
> +		} else if (p->rate.mpu != 64) {
> +			NL_SET_ERR_MSG_MOD(extack,
> +					   "Offload not supported when mpu is other than 64");
> +			return -EOPNOTSUPP;
> +		}

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next v2 2/3] net: sched: tbf: pass all params to offload users
  2026-05-04 10:12 ` [PATCH net-next v2 2/3] net: sched: tbf: pass all params to offload users David Yang
  2026-05-07  1:22   ` Jakub Kicinski
@ 2026-05-07  1:23   ` Jakub Kicinski
  2026-05-07  3:11     ` David Yang
  1 sibling, 1 reply; 11+ messages in thread
From: Jakub Kicinski @ 2026-05-07  1:23 UTC (permalink / raw)
  To: mmyangfl
  Cc: Jakub Kicinski, netdev, andrew, olteanv, davem, edumazet, pabeni,
	jhs, jiri, horms, linux-kernel

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: sched: tbf: pass all params to offload users

This change extends struct tc_tbf_qopt_offload_replace_params with
limit, max_size, buffer, mtu and a peak ratecfg, and populates them
from tbf_sched_data in tbf_offload_change() so offload drivers can see
all the tbf parameters.

> diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
> index 3bd08d7f39c1..d73c04e3d919 100644
> --- a/include/net/pkt_cls.h
> +++ b/include/net/pkt_cls.h
> @@ -1040,8 +1040,12 @@ enum tc_tbf_command {
>  };
>  
>  struct tc_tbf_qopt_offload_replace_params {
> +	u32		limit;
> +	u32		max_size;
> +	s64		buffer;
> +	s64		mtu;

The buffer and mtu fields are stored in tbf_sched_data in nanoseconds
(see tbf_change() in net/sched/sch_tbf.c where they are derived via
PSCHED_TICKS2NS(qopt->buffer) and psched_l2t_ns()), but they are
exposed here as bare s64 buffer / s64 mtu right next to max_size
which is a byte count.

Would it be worth renaming these to buffer_ns / mtu_ns, or adding
kerneldoc to describe their unit?

A driver author reading this struct and seeing mtu adjacent to
max_size might reasonably assume mtu is a byte MTU and program
hardware accordingly.

>  	struct psched_ratecfg rate;
> -	u32 max_size;
> +	struct psched_ratecfg peak;

tbf_change() does memset(&peak, 0, sizeof(peak)) when no peakrate is
configured, and the qdisc internally uses tbf_peak_present(q) which
checks q->peak.rate_bytes_ps != 0 to distinguish absent from present.

With peak exposed to offload drivers, every driver now has to
reproduce this convention implicitly (e.g. by checking
p->peak.rate_bytes_ps).

Would an explicit indicator such as a bool peak_present field, or at
least kerneldoc spelling out the rate_bytes_ps == 0 means absent
contract, make this safer for future offload consumers?

>  	struct gnet_stats_queue *qstats;
>  };
>  

[ ... ]
-- 
pw-bot: cr

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next v2 3/3] net: dsa: yt921x: Add port qdisc tbf support
  2026-05-04 10:12 ` [PATCH net-next v2 3/3] net: dsa: yt921x: Add port qdisc tbf support David Yang
  2026-05-07  1:22   ` Jakub Kicinski
@ 2026-05-07  1:23   ` Jakub Kicinski
  2026-05-07  3:42     ` David Yang
  1 sibling, 1 reply; 11+ messages in thread
From: Jakub Kicinski @ 2026-05-07  1:23 UTC (permalink / raw)
  To: mmyangfl
  Cc: Jakub Kicinski, netdev, andrew, olteanv, davem, edumazet, pabeni,
	jhs, jiri, horms, linux-kernel

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: dsa: yt921x: Add port qdisc tbf support

This commit wires up the port_setup_tc DSA operation to offload a TBF
qdisc onto the yt921x per-port shaper, adding register definitions for
the shaper control block and a marker-transform helper that shares the
existing policer plumbing.

> diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c
> index 42dca9617fb1..dc38e0dd22ee 100644
> --- a/drivers/net/dsa/yt921x.c
> +++ b/drivers/net/dsa/yt921x.c

[ ... ]

> @@ -1378,6 +1390,112 @@ yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port,
>  	return res;
>  }
>  
> +static int
> +yt921x_tbf_validate(struct yt921x_priv *priv,
> +		    const struct tc_tbf_qopt_offload *qopt)
> +{
> +	struct netlink_ext_ack *extack = qopt->extack;
> +
> +	if (qopt->parent != TC_H_ROOT) {
> +		NL_SET_ERR_MSG_MOD(extack, "Parent should be \"root\"");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	switch (qopt->command) {
> +	case TC_TBF_REPLACE: {
> +		const struct tc_tbf_qopt_offload_replace_params *p;
> +
> +		p = &qopt->replace_params;
> +
> +		if (p->mtu || p->peak.rate_bytes_ps) {
> +			NL_SET_ERR_MSG_MOD(extack,
> +					   "Offload not supported when mtu/peakrate is configured");
> +			return -EOPNOTSUPP;
> +		}

Is the p->mtu check overly broad here?  tbf_change() in
net/sched/sch_tbf.c sets q->mtu from the user-supplied qopt->mtu whenever
TCA_TBF_PBURST is absent, so p->mtu can be non-zero even when no peakrate
has been requested.  A user that explicitly passes the mtu parameter to
tc would then be denied offload with an error that attributes the failure
to mtu/peakrate together.

Would gating only on p->peak.rate_bytes_ps better reflect what the
hardware cannot do?

> +
> +		if (!p->rate.mpu) {
> +			NL_SET_ERR_MSG_MOD(extack, "Assuming mpu = 64");
> +		} else if (p->rate.mpu != 64) {
> +			NL_SET_ERR_MSG_MOD(extack,
> +					   "Offload not supported when mpu is other than 64");
> +			return -EOPNOTSUPP;
> +		}

Is setting an extack message on the success path intentional?
psched_ratecfg_precompute() leaves mpu at 0 by default, so the common
tc qdisc add ... tbf rate X burst Y invocation will hit the first branch,
return 0, and iproute2 will then print:

    Warning: yt921x: Assuming mpu = 64.

on every TBF replace.

Also, is the assumption actually enforced anywhere?  yt921x_marker_tfm_shape()
and yt921x_marker_tfm() do not appear to compensate the rate computation
for a 64-byte MPU, so the message seems to document an assumption that is
not modelled by the transform.

> +
> +		break;
> +	}
> +	default:
> +		break;
> +	}
> +
> +	return 0;
> +}

Would it be clearer to have yt921x_tbf_validate() return -EOPNOTSUPP for
commands the handler cannot service, so the supported command set is
stated once?  The default arm here returns 0 for every command other than
TC_TBF_REPLACE (including TC_TBF_STATS and TC_TBF_GRAFT) while
yt921x_dsa_port_setup_tc_tbf_port() below returns -EOPNOTSUPP for those
same commands.  There is no observable bug today, but if the validator is
ever used on its own the two lists can drift.

> +
> +static int
> +yt921x_dsa_port_setup_tc_tbf_port(struct dsa_switch *ds, int port,
> +				  const struct tc_tbf_qopt_offload *qopt)
> +{
> +	struct yt921x_priv *priv = to_yt921x_priv(ds);
> +	struct netlink_ext_ack *extack = qopt->extack;
> +	u32 ctrls[2];
> +	int res;
> +
> +	switch (qopt->command) {
> +	case TC_TBF_DESTROY:
> +		ctrls[0] = 0;
> +		ctrls[1] = 0;
> +		break;

Is there a concern about the write ordering on teardown?
yt921x_reg64_write() -> yt921x_regs_write() performs two independent
register writes (reg+0 then reg+4).  On TC_TBF_DESTROY the first write
sets CIR=0 and CBS=0 while the previously programmed EN bit in ctrls[1]
is still set; only the second write clears EN.  The REPLACE path writes
configuration before arming EN, but the inverse is not followed here.

Would writing ctrls[1]=0 first (or clearing EN via a read-modify-write
before zeroing CIR/CBS) be safer for the hardware state machine?

> +	case TC_TBF_REPLACE: {
[ ... ]
> +		ctrls[0] = YT921X_PORT_SHAPE_CTRLa_CIR(marker.cir) |
> +			   YT921X_PORT_SHAPE_CTRLa_CBS(marker.cbs);
> +		ctrls[1] = YT921X_PORT_SHAPE_CTRLb_UNIT(marker.unit) |
> +			   YT921X_PORT_SHAPE_CTRLb_EN;
> +		break;
> +	}
> +	default:
> +		return -EOPNOTSUPP;
> +	}

Does returning -EOPNOTSUPP for TC_TBF_STATS defeat the hw offload
indicator?  qdisc_offload_dump_helper() in net/sched/sch_api.c first
clears TCQ_F_OFFLOADED, then calls into the driver, and only re-sets the
flag on a zero return:

    sch->flags &= ~TCQ_F_OFFLOADED;
    if (!tc_skip_hw(sch->flags))
        err = qdisc_offload(sch, type, type_data, extack);
    if (err == -EOPNOTSUPP)
        return 0;
    if (err)
        return err;
    sch->flags |= TCQ_F_OFFLOADED;

tbf_dump() invokes this path on every dump, so even after a successful
TC_TBF_REPLACE, tc -s qdisc show would keep reporting offload false and
tc_fill_qdisc() would emit TCA_HW_OFFLOAD=0.

Would a stub TC_TBF_STATS case that returns 0 (leaving bstats/qstats
untouched) preserve the flag here?  mlxsw does this in its
spectrum_qdisc.c.

[ ... ]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next v2 2/3] net: sched: tbf: pass all params to offload users
  2026-05-07  1:23   ` Jakub Kicinski
@ 2026-05-07  3:11     ` David Yang
  2026-05-07 14:37       ` Jakub Kicinski
  0 siblings, 1 reply; 11+ messages in thread
From: David Yang @ 2026-05-07  3:11 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, andrew, olteanv, davem, edumazet, pabeni, jhs, jiri,
	horms, linux-kernel

On Thu, May 7, 2026 at 9:23 AM Jakub Kicinski <kuba@kernel.org> wrote:
> >       qopt.command = TC_TBF_REPLACE;
> >       qopt.handle = sch->handle;
> >       qopt.parent = sch->parent;
> > -     qopt.replace_params.rate = q->rate;
> > +     qopt.replace_params.limit = q->limit;
> >       qopt.replace_params.max_size = q->max_size;
>
> Does passing q->max_size here provide the driver with the original burst size?
>
> The software TBF overwrites q->max_size with min(burst, pburst) when a peak
> rate is configured. As a result, the offload API only passes this overwritten
> value, meaning a driver attempting to offload a dual-rate TBF is not given
> the original burst size in bytes and would have to reverse-engineer it from
> the nanosecond buffer value.

I'm not an expert on TBF so I don't know if it's fine to expose the
internal of schedulers to the drivers, also this would require changes
on struct tbf_sched_data.

> >  struct tc_tbf_qopt_offload_replace_params {
> > +     u32             limit;
> > +     u32             max_size;
> > +     s64             buffer;
> > +     s64             mtu;
>
> The buffer and mtu fields are stored in tbf_sched_data in nanoseconds
> (see tbf_change() in net/sched/sch_tbf.c where they are derived via
> PSCHED_TICKS2NS(qopt->buffer) and psched_l2t_ns()), but they are
> exposed here as bare s64 buffer / s64 mtu right next to max_size
> which is a byte count.
>
> Would it be worth renaming these to buffer_ns / mtu_ns, or adding
> kerneldoc to describe their unit?
>
> A driver author reading this struct and seeing mtu adjacent to
> max_size might reasonably assume mtu is a byte MTU and program
> hardware accordingly.

These are carbon copies of struct tbf_sched_data, I see no reason to
rename just here.

> >       struct psched_ratecfg rate;
> > -     u32 max_size;
> > +     struct psched_ratecfg peak;
>
> tbf_change() does memset(&peak, 0, sizeof(peak)) when no peakrate is
> configured, and the qdisc internally uses tbf_peak_present(q) which
> checks q->peak.rate_bytes_ps != 0 to distinguish absent from present.
>
> With peak exposed to offload drivers, every driver now has to
> reproduce this convention implicitly (e.g. by checking
> p->peak.rate_bytes_ps).
>
> Would an explicit indicator such as a bool peak_present field, or at
> least kerneldoc spelling out the rate_bytes_ps == 0 means absent
> contract, make this safer for future offload consumers?

No similar logic is found for struct flow_action_police.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next v2 3/3] net: dsa: yt921x: Add port qdisc tbf support
  2026-05-07  1:23   ` Jakub Kicinski
@ 2026-05-07  3:42     ` David Yang
  0 siblings, 0 replies; 11+ messages in thread
From: David Yang @ 2026-05-07  3:42 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, andrew, olteanv, davem, edumazet, pabeni, jhs, jiri,
	horms, linux-kernel

On Thu, May 7, 2026 at 9:23 AM Jakub Kicinski <kuba@kernel.org> wrote:
> > +                     NL_SET_ERR_MSG_MOD(extack,
> > +                                        "Offload not supported when mtu/peakrate is configured");
> > +                     return -EOPNOTSUPP;
> > +             }
> > +
> > +             if (!p->rate.mpu) {
>
> Does this code miss validation for p->rate.overhead?
>
> The TC TBF layer allows configuring overhead to account for additional
> headers. If the hardware relies on fixed wire frame sizes and the driver
> ignores p->rate.overhead, could this lead to a silent misconfiguration
> where the hardware shapes traffic at a different effective rate than
> requested?

I didn't see it can be non-zero while testing with tc-tbf(8).

> > @@ -1378,6 +1390,112 @@ yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port,
> >       return res;
> >  }
> >
> > +static int
> > +yt921x_tbf_validate(struct yt921x_priv *priv,
> > +                 const struct tc_tbf_qopt_offload *qopt)
> > +{
> > +     struct netlink_ext_ack *extack = qopt->extack;
> > +
> > +     if (qopt->parent != TC_H_ROOT) {
> > +             NL_SET_ERR_MSG_MOD(extack, "Parent should be \"root\"");
> > +             return -EOPNOTSUPP;
> > +     }
> > +
> > +     switch (qopt->command) {
> > +     case TC_TBF_REPLACE: {
> > +             const struct tc_tbf_qopt_offload_replace_params *p;
> > +
> > +             p = &qopt->replace_params;
> > +
> > +             if (p->mtu || p->peak.rate_bytes_ps) {
> > +                     NL_SET_ERR_MSG_MOD(extack,
> > +                                        "Offload not supported when mtu/peakrate is configured");
> > +                     return -EOPNOTSUPP;
> > +             }
>
> Is the p->mtu check overly broad here?  tbf_change() in
> net/sched/sch_tbf.c sets q->mtu from the user-supplied qopt->mtu whenever
> TCA_TBF_PBURST is absent, so p->mtu can be non-zero even when no peakrate
> has been requested.  A user that explicitly passes the mtu parameter to
> tc would then be denied offload with an error that attributes the failure
> to mtu/peakrate together.
>
> Would gating only on p->peak.rate_bytes_ps better reflect what the
> hardware cannot do?

IIRC I have checked that with tc-tbf(8) (or I used the wrong series);
will check that later.

> > +
> > +             if (!p->rate.mpu) {
> > +                     NL_SET_ERR_MSG_MOD(extack, "Assuming mpu = 64");
> > +             } else if (p->rate.mpu != 64) {
> > +                     NL_SET_ERR_MSG_MOD(extack,
> > +                                        "Offload not supported when mpu is other than 64");
> > +                     return -EOPNOTSUPP;
> > +             }
>
> Is setting an extack message on the success path intentional?
> psched_ratecfg_precompute() leaves mpu at 0 by default, so the common
> tc qdisc add ... tbf rate X burst Y invocation will hit the first branch,
> return 0, and iproute2 will then print:
>
>     Warning: yt921x: Assuming mpu = 64.
>
> on every TBF replace.

https://lore.kernel.org/all/20260413153248.0588690b@kernel.org/
...BTW we can use extack for "warnings" like this, too

> Also, is the assumption actually enforced anywhere?  yt921x_marker_tfm_shape()
> and yt921x_marker_tfm() do not appear to compensate the rate computation
> for a 64-byte MPU, so the message seems to document an assumption that is
> not modelled by the transform.

tc-tbf(8): "For ethernet, no packet uses less than 64 bytes." I don't
see a need for rate computation unless I get it wrong.

> > +
> > +             break;
> > +     }
> > +     default:
> > +             break;
> > +     }
> > +
> > +     return 0;
> > +}
>
> Would it be clearer to have yt921x_tbf_validate() return -EOPNOTSUPP for
> commands the handler cannot service, so the supported command set is
> stated once?  The default arm here returns 0 for every command other than
> TC_TBF_REPLACE (including TC_TBF_STATS and TC_TBF_GRAFT) while
> yt921x_dsa_port_setup_tc_tbf_port() below returns -EOPNOTSUPP for those
> same commands.  There is no observable bug today, but if the validator is
> ever used on its own the two lists can drift.
>
> > +
> > +static int
> > +yt921x_dsa_port_setup_tc_tbf_port(struct dsa_switch *ds, int port,
> > +                               const struct tc_tbf_qopt_offload *qopt)
> > +{
> > +     struct yt921x_priv *priv = to_yt921x_priv(ds);
> > +     struct netlink_ext_ack *extack = qopt->extack;
> > +     u32 ctrls[2];
> > +     int res;
> > +
> > +     switch (qopt->command) {
> > +     case TC_TBF_DESTROY:
> > +             ctrls[0] = 0;
> > +             ctrls[1] = 0;
> > +             break;
>
> Is there a concern about the write ordering on teardown?
> yt921x_reg64_write() -> yt921x_regs_write() performs two independent
> register writes (reg+0 then reg+4).  On TC_TBF_DESTROY the first write
> sets CIR=0 and CBS=0 while the previously programmed EN bit in ctrls[1]
> is still set; only the second write clears EN.  The REPLACE path writes
> configuration before arming EN, but the inverse is not followed here.
>
> Would writing ctrls[1]=0 first (or clearing EN via a read-modify-write
> before zeroing CIR/CBS) be safer for the hardware state machine?

The AI got confused on register operations; see comment for multi-word
registers.

> > +     case TC_TBF_REPLACE: {
> [ ... ]
> > +             ctrls[0] = YT921X_PORT_SHAPE_CTRLa_CIR(marker.cir) |
> > +                        YT921X_PORT_SHAPE_CTRLa_CBS(marker.cbs);
> > +             ctrls[1] = YT921X_PORT_SHAPE_CTRLb_UNIT(marker.unit) |
> > +                        YT921X_PORT_SHAPE_CTRLb_EN;
> > +             break;
> > +     }
> > +     default:
> > +             return -EOPNOTSUPP;
> > +     }
>
> Does returning -EOPNOTSUPP for TC_TBF_STATS defeat the hw offload
> indicator?  qdisc_offload_dump_helper() in net/sched/sch_api.c first
> clears TCQ_F_OFFLOADED, then calls into the driver, and only re-sets the
> flag on a zero return:

No one else did that; mt753x_tc_setup_qdisc_tbf().

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next v2 2/3] net: sched: tbf: pass all params to offload users
  2026-05-07  3:11     ` David Yang
@ 2026-05-07 14:37       ` Jakub Kicinski
  0 siblings, 0 replies; 11+ messages in thread
From: Jakub Kicinski @ 2026-05-07 14:37 UTC (permalink / raw)
  To: David Yang
  Cc: netdev, andrew, olteanv, davem, edumazet, pabeni, jhs, jiri,
	horms, linux-kernel

On Thu, 7 May 2026 11:11:58 +0800 David Yang wrote:
> > >  struct tc_tbf_qopt_offload_replace_params {
> > > +     u32             limit;
> > > +     u32             max_size;
> > > +     s64             buffer;
> > > +     s64             mtu;  
> >
> > The buffer and mtu fields are stored in tbf_sched_data in nanoseconds
> > (see tbf_change() in net/sched/sch_tbf.c where they are derived via
> > PSCHED_TICKS2NS(qopt->buffer) and psched_l2t_ns()), but they are
> > exposed here as bare s64 buffer / s64 mtu right next to max_size
> > which is a byte count.
> >
> > Would it be worth renaming these to buffer_ns / mtu_ns, or adding
> > kerneldoc to describe their unit?
> >
> > A driver author reading this struct and seeing mtu adjacent to
> > max_size might reasonably assume mtu is a byte MTU and program
> > hardware accordingly.  
> 
> These are carbon copies of struct tbf_sched_data, I see no reason to
> rename just here.

Driver API has broader exposure and more potential for
misunderstandings. AI's naming suggestion makes sense to me.

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-05-07 14:37 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-04 10:12 [PATCH net-next v2 0/3] net: dsa: yt921x: Add port qdisc tbf support David Yang
2026-05-04 10:12 ` [PATCH net-next v2 1/3] net: sched: tbf: add extack to offload params David Yang
2026-05-04 10:12 ` [PATCH net-next v2 2/3] net: sched: tbf: pass all params to offload users David Yang
2026-05-07  1:22   ` Jakub Kicinski
2026-05-07  1:23   ` Jakub Kicinski
2026-05-07  3:11     ` David Yang
2026-05-07 14:37       ` Jakub Kicinski
2026-05-04 10:12 ` [PATCH net-next v2 3/3] net: dsa: yt921x: Add port qdisc tbf support David Yang
2026-05-07  1:22   ` Jakub Kicinski
2026-05-07  1:23   ` Jakub Kicinski
2026-05-07  3:42     ` David Yang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox