diff mbox series

[v2,net-next,02/12] tsnep: deny tc-taprio changes to per-tc max SDU

Message ID 20220923163310.3192733-3-vladimir.oltean@nxp.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series Add tc-taprio support for queueMaxSDU | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 7 of 7 maintainers
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 16 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Vladimir Oltean Sept. 23, 2022, 4:33 p.m. UTC
Since the driver does not act upon the max_sdu argument, deny any other
values except the default all-zeroes, which means that all traffic
classes should use the same MTU as the port itself.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
v1->v2: none

 drivers/net/ethernet/engleder/tsnep_tc.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

Comments

Jakub Kicinski Sept. 26, 2022, 8:40 p.m. UTC | #1
On Fri, 23 Sep 2022 19:33:00 +0300 Vladimir Oltean wrote:
> Since the driver does not act upon the max_sdu argument, deny any other
> values except the default all-zeroes, which means that all traffic
> classes should use the same MTU as the port itself.

Don't all the driver patches make you wanna turn this into an opt-in?
What are the chances we'll catch all drivers missing the validation 
in review?
Vladimir Oltean Sept. 26, 2022, 9:50 p.m. UTC | #2
On Mon, Sep 26, 2022 at 01:40:25PM -0700, Jakub Kicinski wrote:
> On Fri, 23 Sep 2022 19:33:00 +0300 Vladimir Oltean wrote:
> > Since the driver does not act upon the max_sdu argument, deny any other
> > values except the default all-zeroes, which means that all traffic
> > classes should use the same MTU as the port itself.
> 
> Don't all the driver patches make you wanna turn this into an opt-in?

Presumably you're thinking of a way through which the caller of
ndo_setup_tc(TC_SETUP_QDISC_TAPRIO, struct tc_taprio_qopt_offload *)
knows whether the driver took the new max_sdu field into consideration,
and not just accepted it blindly?

I'm not exactly up to date with all the techniques which can achieve
that without changes in drivers, and I haven't noticed other qdisc
offloads doing it either... but this would be a great trick to learn for
sure. Do you have any idea?

> What are the chances we'll catch all drivers missing the validation 
> in review?

Not that slim I think, they are all identifiable if you search for
TC_SETUP_QDISC_TAPRIO.
Jakub Kicinski Sept. 26, 2022, 11:29 p.m. UTC | #3
On Tue, 27 Sep 2022 00:50:49 +0300 Vladimir Oltean wrote:
> > Don't all the driver patches make you wanna turn this into an opt-in?  
> 
> Presumably you're thinking of a way through which the caller of
> ndo_setup_tc(TC_SETUP_QDISC_TAPRIO, struct tc_taprio_qopt_offload *)
> knows whether the driver took the new max_sdu field into consideration,
> and not just accepted it blindly?
> 
> I'm not exactly up to date with all the techniques which can achieve
> that without changes in drivers, and I haven't noticed other qdisc
> offloads doing it either... but this would be a great trick to learn for
> sure. Do you have any idea?

I usually put a capability field into the ops themselves. But since tc
offloads don't have real ops (heh) we need to do the command callback
thing. This is my knee-jerk coding of something:

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 9f42fc871c3b..2d043def76d8 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -960,6 +960,11 @@ enum tc_setup_type {
 	TC_SETUP_QDISC_FIFO,
 	TC_SETUP_QDISC_HTB,
 	TC_SETUP_ACT,
+	TC_QUERY_CAPS,
+};
+
+struct tc_query_caps {
+	u32 cmd;
 };
 
 /* These structures hold the attributes of bpf state that are being passed
diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h
index 2ff80cd04c5c..2416151a23db 100644
--- a/include/net/pkt_sched.h
+++ b/include/net/pkt_sched.h
@@ -155,6 +155,12 @@ struct tc_etf_qopt_offload {
 	s32 queue;
 };
 
+struct tc_taprio_drv_caps {
+	struct tc_query_caps base;
+
+	bool accept_max_sdu;
+};
+
 struct tc_taprio_sched_entry {
 	u8 command; /* TC_TAPRIO_CMD_* */
 
diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 136ae21ebce9..68302ee33937 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -1219,6 +1219,7 @@ static int taprio_enable_offload(struct net_device *dev,
 				 struct sched_gate_list *sched,
 				 struct netlink_ext_ack *extack)
 {
+	struct tc_taprio_drv_caps caps = { { .cmd = TC_SETUP_QDISC_TAPRIO, }, };
 	const struct net_device_ops *ops = dev->netdev_ops;
 	struct tc_taprio_qopt_offload *offload;
 	int err = 0;
@@ -1229,6 +1230,12 @@ static int taprio_enable_offload(struct net_device *dev,
 		return -EOPNOTSUPP;
 	}
 
+	ops->ndo_setup_tc(dev, TC_QUERY_CAPS, &caps);
+	if (!caps.accept_max_sdu && taprio_is_max_sdu_used(...))  {
+		NL_SET_ERR_MSG(extack, "nope.");
+		return -EOPNOTSUPP;
+	}
+
 	offload = taprio_offload_alloc(sched->num_entries);
 	if (!offload) {
 		NL_SET_ERR_MSG(extack,

> > What are the chances we'll catch all drivers missing the validation 
> > in review?  
> 
> Not that slim I think, they are all identifiable if you search for
> TC_SETUP_QDISC_TAPRIO.

Right, but that's what's in the tree _now_. Experience teaches that
people may have out of tree code which implements TAPRIO and may send
it for upstream review without as much as testing it against net-next :(
As time passes and our memories fade the chances we'd catch such code
when posted upstream go down, perhaps from high to medium but still,
the explicit opt-in is more foolproof.
Vladimir Oltean Sept. 27, 2022, 12:22 a.m. UTC | #4
On Mon, Sep 26, 2022 at 04:29:34PM -0700, Jakub Kicinski wrote:
> I usually put a capability field into the ops themselves.

Do you also have an example for the 'usual' manner?

> But since tc offloads don't have real ops (heh) we need to do the
> command callback thing. This is my knee-jerk coding of something:
> 
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 9f42fc871c3b..2d043def76d8 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -960,6 +960,11 @@ enum tc_setup_type {
>  	TC_SETUP_QDISC_FIFO,
>  	TC_SETUP_QDISC_HTB,
>  	TC_SETUP_ACT,
> +	TC_QUERY_CAPS,
> +};
> +
> +struct tc_query_caps {
> +	u32 cmd;

actually s/u32/enum tc_setup_type/

inception....

>  };

> Right, but that's what's in the tree _now_. Experience teaches that
> people may have out of tree code which implements TAPRIO and may send
> it for upstream review without as much as testing it against net-next :(
> As time passes and our memories fade the chances we'd catch such code
> when posted upstream go down, perhaps from high to medium but still,
> the explicit opt-in is more foolproof.

You also need to see the flip side. You're making code more self-maintainable
by adding bureaucracy to the run time itself. Whereas things could have
been sorted out between the qdisc and the driver in just one ndo_setup_tc()
call via the straightforward approach (every driver rejects what it
doesn't like), now you need two calls for the normal case when the
driver will accept a valid configuration.

I get the point and I think this won't probably make a big difference
for a slow path like qdisc offload (at least it won't for me), but from
an API perspective, once the mechanism will go in, it will become quite
ossified, so it's best to ask some questions about it now.

Like for example you're funneling the caps through ndo_setup_tc(), which
has these comments in its description:

 * int (*ndo_setup_tc)(struct net_device *dev, enum tc_setup_type type,
 *		       void *type_data);
 *	Called to setup any 'tc' scheduler, classifier or action on @dev.
 *	This is always called from the stack with the rtnl lock held and netif
 *	tx queues stopped. This allows the netdevice to perform queue
 *	management safely.

Do we need to offer guarantees of rtnl lock and stopped TX queues to a
function which just queries capabilities (and likely doesn't need them),
or would it be better to devise a new ndo? Generally, when you have a
separate method to query caps vs to actually do the work, different
calling contexts is generally the justification to do that, as opposed
to piggy-backing the caps that the driver acted upon through the same
struct tc_taprio_qopt_offload.
Jakub Kicinski Sept. 27, 2022, 12:43 a.m. UTC | #5
On Tue, 27 Sep 2022 00:22:53 +0000 Vladimir Oltean wrote:
> On Mon, Sep 26, 2022 at 04:29:34PM -0700, Jakub Kicinski wrote:
> > I usually put a capability field into the ops themselves.  
> 
> Do you also have an example for the 'usual' manner?

struct devlink_ops {
	/**
	 * @supported_flash_update_params:
	 * mask of parameters supported by the driver's .flash_update
	 * implemementation.
	 */
	u32 supported_flash_update_params;
	unsigned long reload_actions;
	unsigned long reload_limits;

struct ethtool_ops {
	u32     cap_link_lanes_supported:1;
	u32	supported_coalesce_params;
	u32	supported_ring_params;

> > Right, but that's what's in the tree _now_. Experience teaches that
> > people may have out of tree code which implements TAPRIO and may send
> > it for upstream review without as much as testing it against net-next :(
> > As time passes and our memories fade the chances we'd catch such code
> > when posted upstream go down, perhaps from high to medium but still,
> > the explicit opt-in is more foolproof.  
> 
> You also need to see the flip side. You're making code more self-maintainable
> by adding bureaucracy to the run time itself. Whereas things could have
> been sorted out between the qdisc and the driver in just one ndo_setup_tc()
> call via the straightforward approach (every driver rejects what it
> doesn't like), now you need two calls for the normal case when the
> driver will accept a valid configuration.

Right, the lack of a structure we can put it in is quite unfortunate :(
But I do not dare suggesting we add a structure with qdisc and cls
specific callbacks instead of the mux-y ndo_setup_tc :)
I guess we could take a shortcut and put a pointer in netdev_ops for
just the caps for now, hm.

> I get the point and I think this won't probably make a big difference
> for a slow path like qdisc offload (at least it won't for me), but from
> an API perspective, once the mechanism will go in, it will become quite
> ossified, so it's best to ask some questions about it now.
> 
> Like for example you're funneling the caps through ndo_setup_tc(), which
> has these comments in its description:
> 
>  * int (*ndo_setup_tc)(struct net_device *dev, enum tc_setup_type type,
>  *		       void *type_data);
>  *	Called to setup any 'tc' scheduler, classifier or action on @dev.
>  *	This is always called from the stack with the rtnl lock held and netif
>  *	tx queues stopped. This allows the netdevice to perform queue
>  *	management safely.
> 
> Do we need to offer guarantees of rtnl lock and stopped TX queues to a
> function which just queries capabilities (and likely doesn't need them),
> or would it be better to devise a new ndo?

The queues stopped part is not true already for classifier offloads :(

> Generally, when you have a
> separate method to query caps vs to actually do the work, different
> calling contexts is generally the justification to do that, as opposed
> to piggy-backing the caps that the driver acted upon through the same
> struct tc_taprio_qopt_offload.

If we add a new pointer for netdev_ops I'd go with a struct pointer
rather than an op, for consistency if nothing else. But if you feel
strongly either way will work.
diff mbox series

Patch

diff --git a/drivers/net/ethernet/engleder/tsnep_tc.c b/drivers/net/ethernet/engleder/tsnep_tc.c
index c4c6e1357317..82df837ffc54 100644
--- a/drivers/net/ethernet/engleder/tsnep_tc.c
+++ b/drivers/net/ethernet/engleder/tsnep_tc.c
@@ -320,11 +320,15 @@  static int tsnep_taprio(struct tsnep_adapter *adapter,
 {
 	struct tsnep_gcl *gcl;
 	struct tsnep_gcl *curr;
-	int retval;
+	int retval, tc;
 
 	if (!adapter->gate_control)
 		return -EOPNOTSUPP;
 
+	for (tc = 0; tc < TC_MAX_QUEUE; tc++)
+		if (qopt->max_sdu[tc])
+			return -EOPNOTSUPP;
+
 	if (!qopt->enable) {
 		/* disable gate control if active */
 		mutex_lock(&adapter->gate_control_lock);