2023-08-07 20:31:41

by Vladimir Oltean

[permalink] [raw]
Subject: [PATCH v4 net-next 00/11] Improve the taprio qdisc's relationship with its children

Changes in v4:
- Clean up some leftovers in the ptp_mock driver.
- Add CONFIG_PTP_1588_CLOCK_MOCK to tools/testing/selftests/tc-testing/config
- Wait for taprio schedule to become operational in the selftests

Changes in v3:
Fix ptp_mock compilation as module, fix small mistakes in selftests.

Changes in v2:
It was requested to add test cases for the taprio software and offload modes.
Those are patches 08 and 09.

That implies adding taprio offload support to netdevsim, which is patch 07.

In turn, that implies adding a PHC driver for netdevsim, which is patch 06.

v1 at:
https://lore.kernel.org/lkml/[email protected]/

Original message:

Prompted by Vinicius' request to consolidate some child Qdisc
dereferences in taprio:
https://lore.kernel.org/netdev/[email protected]/

I remembered that I had left some unfinished work in this Qdisc, namely
commit af7b29b1deaa ("Revert "net/sched: taprio: make qdisc_leaf() see
the per-netdev-queue pfifo child qdiscs"").

This patch set represents another stab at, essentially, what's in the
title. Not only does taprio not properly detect when it's grafted as a
non-root qdisc, but it also returns incorrect per-class stats.
Eventually, Vinicius' request is addressed too, although in a different
form than the one he requested (which was purely cosmetic).

Review from people more experienced with Qdiscs than me would be
appreciated. I tried my best to explain what I consider to be problems.
I am deliberately targeting net-next because the changes are too
invasive for net - they were reverted from stable once already.

Vladimir Oltean (11):
net/sched: taprio: don't access q->qdiscs[] in unoffloaded mode during
attach()
net/sched: taprio: keep child Qdisc refcount elevated at 2 in offload
mode
net/sched: taprio: try again to report q->qdiscs[] to qdisc_leaf()
net/sched: taprio: delete misleading comment about preallocating child
qdiscs
net/sched: taprio: dump class stats for the actual q->qdiscs[]
net: ptp: create a mock-up PTP Hardware Clock driver
net: netdevsim: use mock PHC driver
net: netdevsim: mimic tc-taprio offload
selftests/tc-testing: add ptp_mock Kconfig dependency
selftests/tc-testing: test that taprio can only be attached as root
selftests/tc-testing: verify that a qdisc can be grafted onto a taprio
class

MAINTAINERS | 7 +
drivers/net/Kconfig | 1 +
drivers/net/netdevsim/ethtool.c | 11 ++
drivers/net/netdevsim/netdev.c | 38 +++-
drivers/net/netdevsim/netdevsim.h | 2 +
drivers/ptp/Kconfig | 11 ++
drivers/ptp/Makefile | 1 +
drivers/ptp/ptp_mock.c | 175 ++++++++++++++++++
include/linux/ptp_mock.h | 38 ++++
net/sched/sch_taprio.c | 68 ++++---
tools/testing/selftests/tc-testing/config | 3 +-
.../tc-testing/taprio_wait_for_admin.sh | 16 ++
.../tc-testing/tc-tests/qdiscs/taprio.json | 102 +++++++++-
13 files changed, 443 insertions(+), 30 deletions(-)
create mode 100644 drivers/ptp/ptp_mock.c
create mode 100644 include/linux/ptp_mock.h
create mode 100755 tools/testing/selftests/tc-testing/taprio_wait_for_admin.sh

--
2.34.1



2023-08-07 20:38:35

by Vladimir Oltean

[permalink] [raw]
Subject: [PATCH v4 net-next 08/11] net: netdevsim: mimic tc-taprio offload

To be able to use netdevsim for tc-testing with an offloaded tc-taprio
schedule, it needs to report a PTP clock (which it now does), and to
accept ndo_setup_tc(TC_SETUP_QDISC_TAPRIO) calls.

Since netdevsim has no packet I/O, this doesn't do anything intelligent,
it only allows taprio offload code paths to go through some level of
automated testing.

Signed-off-by: Vladimir Oltean <[email protected]>
---
v2->v4: none
v1->v2: patch is new

drivers/net/netdevsim/netdev.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index 2a4a0c4065cf..2eac92f49631 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -209,6 +209,31 @@ static int nsim_set_vf_link_state(struct net_device *dev, int vf, int state)
return 0;
}

+static void nsim_taprio_stats(struct tc_taprio_qopt_stats *stats)
+{
+ stats->window_drops = 0;
+ stats->tx_overruns = 0;
+}
+
+static int nsim_setup_tc_taprio(struct net_device *dev,
+ struct tc_taprio_qopt_offload *offload)
+{
+ int err = 0;
+
+ switch (offload->cmd) {
+ case TAPRIO_CMD_REPLACE:
+ case TAPRIO_CMD_DESTROY:
+ break;
+ case TAPRIO_CMD_STATS:
+ nsim_taprio_stats(&offload->stats);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ }
+
+ return err;
+}
+
static LIST_HEAD(nsim_block_cb_list);

static int
@@ -217,6 +242,8 @@ nsim_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data)
struct netdevsim *ns = netdev_priv(dev);

switch (type) {
+ case TC_SETUP_QDISC_TAPRIO:
+ return nsim_setup_tc_taprio(dev, type_data);
case TC_SETUP_BLOCK:
return flow_block_cb_setup_simple(type_data,
&nsim_block_cb_list,
--
2.34.1


2023-08-07 20:41:47

by Vladimir Oltean

[permalink] [raw]
Subject: [PATCH v4 net-next 01/11] net/sched: taprio: don't access q->qdiscs[] in unoffloaded mode during attach()

This is a simple code transformation with no intended behavior change,
just to make it absolutely clear that q->qdiscs[] is only attached to
the child taprio classes in full offload mode.

Right now we use the q->qdiscs[] variable in taprio_attach() for
software mode too, but that is quite confusing and avoidable. We use
it only to reach the netdev TX queue, but we could as well just use
netdev_get_tx_queue() for that.

Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v4: none

net/sched/sch_taprio.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 8c9cfff7fd05..1cbc7fcd56c0 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -2145,14 +2145,20 @@ static void taprio_attach(struct Qdisc *sch)

/* Attach underlying qdisc */
for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
- struct Qdisc *qdisc = q->qdiscs[ntx];
+ struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, ntx);
struct Qdisc *old;

if (FULL_OFFLOAD_IS_ENABLED(q->flags)) {
+ struct Qdisc *qdisc = q->qdiscs[ntx];
+
qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
- old = dev_graft_qdisc(qdisc->dev_queue, qdisc);
+ old = dev_graft_qdisc(dev_queue, qdisc);
} else {
- old = dev_graft_qdisc(qdisc->dev_queue, sch);
+ /* In software mode, attach the root taprio qdisc
+ * to all netdev TX queues, so that dev_qdisc_enqueue()
+ * goes through taprio_enqueue().
+ */
+ old = dev_graft_qdisc(dev_queue, sch);
qdisc_refcount_inc(sch);
}
if (old)
--
2.34.1


2023-08-07 20:58:59

by Vladimir Oltean

[permalink] [raw]
Subject: [PATCH v4 net-next 05/11] net/sched: taprio: dump class stats for the actual q->qdiscs[]

This makes a difference for the software scheduling mode, where
dev_queue->qdisc_sleeping is the same as the taprio root Qdisc itself,
but when we're talking about what Qdisc and stats get reported for a
traffic class, the root taprio isn't what comes to mind, but q->qdiscs[]
is.

To understand the difference, I've attempted to send 100 packets in
software mode through class 8001:5, and recorded the stats before and
after the change.

Here is before:

$ tc -s class show dev eth0
class taprio 8001:1 root leaf 8001:
Sent 9400 bytes 100 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:2 root leaf 8001:
Sent 9400 bytes 100 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:3 root leaf 8001:
Sent 9400 bytes 100 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:4 root leaf 8001:
Sent 9400 bytes 100 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:5 root leaf 8001:
Sent 9400 bytes 100 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:6 root leaf 8001:
Sent 9400 bytes 100 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:7 root leaf 8001:
Sent 9400 bytes 100 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:8 root leaf 8001:
Sent 9400 bytes 100 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0

and here is after:

class taprio 8001:1 root
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:2 root
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:3 root
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:4 root
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:5 root
Sent 9400 bytes 100 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:6 root
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:7 root
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0
class taprio 8001:8 root leaf 800d:
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
window_drops 0

The most glaring (and expected) difference is that before, all class
stats reported the global stats, whereas now, they really report just
the counters for that traffic class.

Finally, Pedro Tammela points out that there is a tc selftest which
checks specifically which handle do the child Qdiscs corresponding to
each class have. That's changing here - taprio no longer reports
tcm->tcm_info as the same handle "1:" as itself (the root Qdisc), but 0
(the handle of the default pfifo child Qdiscs). Since iproute2 does not
print a child Qdisc handle of 0, adjust the test's expected output.

Link: https://lore.kernel.org/netdev/[email protected]/
Signed-off-by: Vladimir Oltean <[email protected]>
---
v3->v4: none
v2->v3: fix selftest broken by the change
v1->v2:
- reword commit message
- rebase on top of the TAPRIO_CMD_TC_STATS -> TAPRIO_CMD_QUEUE_STATS change

net/sched/sch_taprio.c | 8 +++-----
.../selftests/tc-testing/tc-tests/qdiscs/taprio.json | 2 +-
2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index d5693bd52e93..1cb5e41c0ec7 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -2472,11 +2472,11 @@ static unsigned long taprio_find(struct Qdisc *sch, u32 classid)
static int taprio_dump_class(struct Qdisc *sch, unsigned long cl,
struct sk_buff *skb, struct tcmsg *tcm)
{
- struct netdev_queue *dev_queue = taprio_queue_get(sch, cl);
+ struct Qdisc *child = taprio_leaf(sch, cl);

tcm->tcm_parent = TC_H_ROOT;
tcm->tcm_handle |= TC_H_MIN(cl);
- tcm->tcm_info = rtnl_dereference(dev_queue->qdisc_sleeping)->handle;
+ tcm->tcm_info = child->handle;

return 0;
}
@@ -2486,16 +2486,14 @@ static int taprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
__releases(d->lock)
__acquires(d->lock)
{
- struct netdev_queue *dev_queue = taprio_queue_get(sch, cl);
+ struct Qdisc *child = taprio_leaf(sch, cl);
struct tc_taprio_qopt_offload offload = {
.cmd = TAPRIO_CMD_QUEUE_STATS,
.queue_stats = {
.queue = cl - 1,
},
};
- struct Qdisc *child;

- child = rtnl_dereference(dev_queue->qdisc_sleeping);
if (gnet_stats_copy_basic(d, NULL, &child->bstats, true) < 0 ||
qdisc_qstats_copy(d, child) < 0)
return -1;
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
index 08d4861c2e78..860b55df0d6d 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
@@ -104,7 +104,7 @@
"cmdUnderTest": "$TC qdisc add dev $ETH root handle 1: taprio num_tc 3 map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@0 1@0 base-time 1000000000 sched-entry S 01 300000 flags 0x1 clockid CLOCK_TAI",
"expExitCode": "0",
"verifyCmd": "$TC class show dev $ETH",
- "matchPattern": "class taprio 1:[0-9]+ root leaf 1:",
+ "matchPattern": "class taprio 1:[0-9]+ root",
"matchCount": "8",
"teardown": [
"echo \"1\" > /sys/bus/netdevsim/del_device"
--
2.34.1


2023-08-07 21:04:39

by Vladimir Oltean

[permalink] [raw]
Subject: [PATCH v4 net-next 02/11] net/sched: taprio: keep child Qdisc refcount elevated at 2 in offload mode

Normally, Qdiscs have one reference on them held by their owner and one
held for each TXQ to which they are attached, however this is not the
case with the children of an offloaded taprio. Instead, the taprio qdisc
currently lives in the following fragile equilibrium.

In the software scheduling case, taprio attaches itself (the root Qdisc)
to all TXQs, thus having a refcount of 1 + the number of TX queues. In
this mode, the q->qdiscs[] children are not visible directly to the
Qdisc API. The lifetime of the Qdiscs from this private array lasts
until qdisc_destroy() -> taprio_destroy().

In the fully offloaded case, the root taprio has a refcount of 1, and
all child q->qdiscs[] also have a refcount of 1. The child q->qdiscs[]
are attached to the netdev TXQs directly and thus are visible to the
Qdisc API, however taprio loses a reference to them very early - during
qdisc_graft(parent==NULL) -> taprio_attach(). At that time, taprio frees
the q->qdiscs[] array to not leak memory, but interestingly, it does not
release a reference on these qdiscs because it doesn't effectively own
them - they are created by taprio but owned by the Qdisc core, and will
be freed by qdisc_graft(parent==NULL, new==NULL) -> qdisc_put(old) when
the Qdisc is deleted or when the child Qdisc is replaced with something
else.

My interest is to change this equilibrium such that taprio also owns a
reference on the q->qdiscs[] child Qdiscs for the lifetime of the root
Qdisc, including in full offload mode. I want this because I would like
taprio_leaf(), taprio_dump_class(), taprio_dump_class_stats() to have
insight into q->qdiscs[] for the software scheduling mode - currently
they look at dev_queue->qdisc_sleeping, which is, as mentioned, the same
as the root taprio.

The following set of changes is necessary:
- don't free q->qdiscs[] early in taprio_attach(), free it late in
taprio_destroy() for consistency with software mode. But:
- currently that's not possible, because taprio doesn't own a reference
on q->qdiscs[]. So hold that reference - once during the initial
attach() and once during subsequent graft() calls when the child is
changed.
- always keep track of the current child in q->qdiscs[], even for full
offload mode, so that we free in taprio_destroy() what we should, and
not something stale.

Signed-off-by: Vladimir Oltean <[email protected]>
---
v2->v4: none
v1->v2:
- fix refcount not dropping to 0 after a graft operation - spotted by
Paolo
- slightly reword commit message and comments

net/sched/sch_taprio.c | 39 +++++++++++++++++++++++++--------------
1 file changed, 25 insertions(+), 14 deletions(-)

diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 1cbc7fcd56c0..7c1fc3dc3e55 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -2146,30 +2146,31 @@ static void taprio_attach(struct Qdisc *sch)
/* Attach underlying qdisc */
for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, ntx);
- struct Qdisc *old;
+ struct Qdisc *old, *dev_queue_qdisc;

if (FULL_OFFLOAD_IS_ENABLED(q->flags)) {
struct Qdisc *qdisc = q->qdiscs[ntx];

+ /* In offload mode, the root taprio qdisc is bypassed
+ * and the netdev TX queues see the children directly
+ */
qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
- old = dev_graft_qdisc(dev_queue, qdisc);
+ dev_queue_qdisc = qdisc;
} else {
/* In software mode, attach the root taprio qdisc
* to all netdev TX queues, so that dev_qdisc_enqueue()
* goes through taprio_enqueue().
*/
- old = dev_graft_qdisc(dev_queue, sch);
- qdisc_refcount_inc(sch);
+ dev_queue_qdisc = sch;
}
+ old = dev_graft_qdisc(dev_queue, dev_queue_qdisc);
+ /* The qdisc's refcount requires to be elevated once
+ * for each netdev TX queue it is grafted onto
+ */
+ qdisc_refcount_inc(dev_queue_qdisc);
if (old)
qdisc_put(old);
}
-
- /* access to the child qdiscs is not needed in offload mode */
- if (FULL_OFFLOAD_IS_ENABLED(q->flags)) {
- kfree(q->qdiscs);
- q->qdiscs = NULL;
- }
}

static struct netdev_queue *taprio_queue_get(struct Qdisc *sch,
@@ -2198,13 +2199,23 @@ static int taprio_graft(struct Qdisc *sch, unsigned long cl,
if (dev->flags & IFF_UP)
dev_deactivate(dev);

+ /* In offload mode, the child Qdisc is directly attached to the netdev
+ * TX queue, and thus, we need to keep its refcount elevated in order
+ * to counteract qdisc_graft()'s call to qdisc_put() once per TX queue.
+ * However, save the reference to the new qdisc in the private array in
+ * both software and offload cases, to have an up-to-date reference to
+ * our children.
+ */
+ *old = q->qdiscs[cl - 1];
if (FULL_OFFLOAD_IS_ENABLED(q->flags)) {
- *old = dev_graft_qdisc(dev_queue, new);
- } else {
- *old = q->qdiscs[cl - 1];
- q->qdiscs[cl - 1] = new;
+ WARN_ON_ONCE(dev_graft_qdisc(dev_queue, new) != *old);
+ if (new)
+ qdisc_refcount_inc(new);
+ if (*old)
+ qdisc_put(*old);
}

+ q->qdiscs[cl - 1] = new;
if (new)
new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;

--
2.34.1


2023-08-07 21:20:56

by Vladimir Oltean

[permalink] [raw]
Subject: [PATCH v4 net-next 07/11] net: netdevsim: use mock PHC driver

I'd like to make netdevsim offload tc-taprio, but currently, this Qdisc
emits a ETHTOOL_GET_TS_INFO call to the driver to make sure that it has
a PTP clock, so that it is reasonably capable of offloading the schedule.

By using the mock PHC driver, that becomes possible.

Hardware timestamping is not necessary, and netdevsim does not support
packet I/O anyway.

Signed-off-by: Vladimir Oltean <[email protected]>
---
v3->v4: none
v2->v3: split off from common patch with ptp_mock introduction
v1->v2: patch is new

drivers/net/Kconfig | 1 +
drivers/net/netdevsim/ethtool.c | 11 +++++++++++
drivers/net/netdevsim/netdev.c | 11 ++++++++++-
drivers/net/netdevsim/netdevsim.h | 2 ++
4 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 368c6f5b327e..4953c1494723 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -592,6 +592,7 @@ config NETDEVSIM
depends on INET
depends on IPV6 || IPV6=n
depends on PSAMPLE || PSAMPLE=n
+ depends on PTP_1588_CLOCK_MOCK || PTP_1588_CLOCK_MOCK=n
select NET_DEVLINK
help
This driver is a developer testing tool and software model that can
diff --git a/drivers/net/netdevsim/ethtool.c b/drivers/net/netdevsim/ethtool.c
index ffd9f84b6644..bd546d4d26c6 100644
--- a/drivers/net/netdevsim/ethtool.c
+++ b/drivers/net/netdevsim/ethtool.c
@@ -140,6 +140,16 @@ nsim_set_fecparam(struct net_device *dev, struct ethtool_fecparam *fecparam)
return 0;
}

+static int nsim_get_ts_info(struct net_device *dev,
+ struct ethtool_ts_info *info)
+{
+ struct netdevsim *ns = netdev_priv(dev);
+
+ info->phc_index = mock_phc_index(ns->phc);
+
+ return 0;
+}
+
static const struct ethtool_ops nsim_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_ALL_PARAMS,
.get_pause_stats = nsim_get_pause_stats,
@@ -153,6 +163,7 @@ static const struct ethtool_ops nsim_ethtool_ops = {
.set_channels = nsim_set_channels,
.get_fecparam = nsim_get_fecparam,
.set_fecparam = nsim_set_fecparam,
+ .get_ts_info = nsim_get_ts_info,
};

static void nsim_ethtool_ring_init(struct netdevsim *ns)
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index 0c8daeb0d62b..2a4a0c4065cf 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -291,13 +291,19 @@ static void nsim_setup(struct net_device *dev)

static int nsim_init_netdevsim(struct netdevsim *ns)
{
+ struct mock_phc *phc;
int err;

+ phc = mock_phc_create(&ns->nsim_bus_dev->dev);
+ if (IS_ERR(phc))
+ return PTR_ERR(phc);
+
+ ns->phc = phc;
ns->netdev->netdev_ops = &nsim_netdev_ops;

err = nsim_udp_tunnels_info_create(ns->nsim_dev, ns->netdev);
if (err)
- return err;
+ goto err_phc_destroy;

rtnl_lock();
err = nsim_bpf_init(ns);
@@ -320,6 +326,8 @@ static int nsim_init_netdevsim(struct netdevsim *ns)
err_utn_destroy:
rtnl_unlock();
nsim_udp_tunnels_info_destroy(ns->netdev);
+err_phc_destroy:
+ mock_phc_destroy(ns->phc);
return err;
}

@@ -383,6 +391,7 @@ void nsim_destroy(struct netdevsim *ns)
rtnl_unlock();
if (nsim_dev_port_is_pf(ns->nsim_dev_port))
nsim_udp_tunnels_info_destroy(dev);
+ mock_phc_destroy(ns->phc);
free_netdev(dev);
}

diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index 7be98b7dcca9..028c825b86db 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/netdevice.h>
+#include <linux/ptp_mock.h>
#include <linux/u64_stats_sync.h>
#include <net/devlink.h>
#include <net/udp_tunnel.h>
@@ -93,6 +94,7 @@ struct netdevsim {
struct net_device *netdev;
struct nsim_dev *nsim_dev;
struct nsim_dev_port *nsim_dev_port;
+ struct mock_phc *phc;

u64 tx_packets;
u64 tx_bytes;
--
2.34.1


2023-08-07 21:46:42

by Vladimir Oltean

[permalink] [raw]
Subject: [PATCH v4 net-next 10/11] selftests/tc-testing: test that taprio can only be attached as root

Check that the "Can only be attached as root qdisc" error message from
taprio is effective by attempting to attach it to a class of another
taprio qdisc. That operation should fail.

In the bug that was squashed by change "net/sched: taprio: try again to
report q->qdiscs[] to qdisc_leaf()", grafting a child taprio to a root
software taprio would be misinterpreted as a change() to the root
taprio. Catch this by looking at whether the base-time of the root
taprio has changed to follow the base-time of the child taprio,
something which should have absolutely never happened assuming correct
semantics.

Vinicius points out that looking at "base_time" in the tc qdisc show
output is unreliable because user space is in a race with the kernel
applying the setting. So we create a helper bash script which waits
while there is any pending schedule.

Link: https://lore.kernel.org/netdev/[email protected]/
Signed-off-by: Vladimir Oltean <[email protected]>
Reviewed-by: Pedro Tammela <[email protected]>
---
v3->v4:
introduce (and use) taprio_wait_for_admin.sh. The test depends upon this
taprio patch to work properly:
https://patchwork.kernel.org/project/netdevbpf/patch/[email protected]/
Note: I couldn't find a way around the horrible tcmd.safe_substitute(NAMES)
and the string quoting (no possibility to perform complex shell operations)
than to put the logic I needed in a separate shell script and execute that.

v2->v3: none
v1->v2: patch is new

.../tc-testing/taprio_wait_for_admin.sh | 16 ++++++
.../tc-testing/tc-tests/qdiscs/taprio.json | 50 +++++++++++++++++++
2 files changed, 66 insertions(+)
create mode 100755 tools/testing/selftests/tc-testing/taprio_wait_for_admin.sh

diff --git a/tools/testing/selftests/tc-testing/taprio_wait_for_admin.sh b/tools/testing/selftests/tc-testing/taprio_wait_for_admin.sh
new file mode 100755
index 000000000000..f5335e8ad6b4
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/taprio_wait_for_admin.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+TC="$1"; shift
+ETH="$1"; shift
+
+# The taprio architecture changes the admin schedule from a hrtimer and not
+# from process context, so we need to wait in order to make sure that any
+# schedule change actually took place.
+while :; do
+ has_admin="$($TC -j qdisc show dev $ETH root | jq '.[].options | has("admin")')"
+ if [ "$has_admin" = "false" ]; then
+ break;
+ fi
+
+ sleep 1
+done
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
index 860b55df0d6d..f475967c509f 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
@@ -156,5 +156,55 @@
"teardown": [
"echo \"1\" > /sys/bus/netdevsim/del_device"
]
+ },
+ {
+ "id": "39b4",
+ "name": "Reject grafting taprio as child qdisc of software taprio",
+ "category": [
+ "qdisc",
+ "taprio"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "echo \"1 1 8\" > /sys/bus/netdevsim/new_device",
+ "$TC qdisc replace dev $ETH handle 8001: parent root stab overhead 24 taprio num_tc 8 map 0 1 2 3 4 5 6 7 queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 base-time 0 sched-entry S ff 20000000 clockid CLOCK_TAI",
+ "./taprio_wait_for_admin.sh $TC $ETH"
+ ],
+ "cmdUnderTest": "$TC qdisc replace dev $ETH parent 8001:7 taprio num_tc 8 map 0 1 2 3 4 5 6 7 queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 base-time 200 sched-entry S ff 20000000 clockid CLOCK_TAI",
+ "expExitCode": "2",
+ "verifyCmd": "bash -c \"./taprio_wait_for_admin.sh $TC $ETH && $TC -j qdisc show dev $ETH root | jq '.[].options.base_time'\"",
+ "matchPattern": "0",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $ETH root",
+ "echo \"1\" > /sys/bus/netdevsim/del_device"
+ ]
+ },
+ {
+ "id": "e8a1",
+ "name": "Reject grafting taprio as child qdisc of offloaded taprio",
+ "category": [
+ "qdisc",
+ "taprio"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "echo \"1 1 8\" > /sys/bus/netdevsim/new_device",
+ "$TC qdisc replace dev $ETH handle 8001: parent root stab overhead 24 taprio num_tc 8 map 0 1 2 3 4 5 6 7 queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 base-time 0 sched-entry S ff 20000000 flags 0x2",
+ "./taprio_wait_for_admin.sh $TC $ETH"
+ ],
+ "cmdUnderTest": "$TC qdisc replace dev $ETH parent 8001:7 taprio num_tc 8 map 0 1 2 3 4 5 6 7 queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 base-time 200 sched-entry S ff 20000000 flags 0x2",
+ "expExitCode": "2",
+ "verifyCmd": "bash -c \"./taprio_wait_for_admin.sh $TC $ETH && $TC -j qdisc show dev $ETH root | jq '.[].options.base_time'\"",
+ "matchPattern": "0",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $ETH root",
+ "echo \"1\" > /sys/bus/netdevsim/del_device"
+ ]
}
]
--
2.34.1


2023-08-07 21:56:44

by Vladimir Oltean

[permalink] [raw]
Subject: [PATCH v4 net-next 11/11] selftests/tc-testing: verify that a qdisc can be grafted onto a taprio class

The reason behind commit af7b29b1deaa ("Revert "net/sched: taprio: make
qdisc_leaf() see the per-netdev-queue pfifo child qdiscs"") was that the
patch it reverted caused a crash when attaching a CBS shaper to one of
the taprio classes. Prevent that from happening again by adding a test
case for it, which now passes correctly in both offload and software
modes.

Signed-off-by: Vladimir Oltean <[email protected]>
Reviewed-by: Pedro Tammela <[email protected]>
---
v3->v4: none
v2->v3: fix expected output for test 6a83 (missing "refcnt 2")
v1->v2: patch is new

.../tc-testing/tc-tests/qdiscs/taprio.json | 50 +++++++++++++++++++
1 file changed, 50 insertions(+)

diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
index f475967c509f..0599635c4bc6 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
@@ -206,5 +206,55 @@
"$TC qdisc del dev $ETH root",
"echo \"1\" > /sys/bus/netdevsim/del_device"
]
+ },
+ {
+ "id": "a7bf",
+ "name": "Graft cbs as child of software taprio",
+ "category": [
+ "qdisc",
+ "taprio",
+ "cbs"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "echo \"1 1 8\" > /sys/bus/netdevsim/new_device",
+ "$TC qdisc replace dev $ETH handle 8001: parent root stab overhead 24 taprio num_tc 8 map 0 1 2 3 4 5 6 7 queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 base-time 0 sched-entry S ff 20000000 clockid CLOCK_TAI"
+ ],
+ "cmdUnderTest": "$TC qdisc replace dev $ETH handle 8002: parent 8001:8 cbs idleslope 20000 sendslope -980000 hicredit 30 locredit -1470",
+ "expExitCode": "0",
+ "verifyCmd": "$TC -d qdisc show dev $ETH",
+ "matchPattern": "qdisc cbs 8002: parent 8001:8 hicredit 30 locredit -1470 sendslope -980000 idleslope 20000 offload 0",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $ETH root",
+ "echo \"1\" > /sys/bus/netdevsim/del_device"
+ ]
+ },
+ {
+ "id": "6a83",
+ "name": "Graft cbs as child of offloaded taprio",
+ "category": [
+ "qdisc",
+ "taprio",
+ "cbs"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "echo \"1 1 8\" > /sys/bus/netdevsim/new_device",
+ "$TC qdisc replace dev $ETH handle 8001: parent root stab overhead 24 taprio num_tc 8 map 0 1 2 3 4 5 6 7 queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 base-time 0 sched-entry S ff 20000000 flags 0x2"
+ ],
+ "cmdUnderTest": "$TC qdisc replace dev $ETH handle 8002: parent 8001:8 cbs idleslope 20000 sendslope -980000 hicredit 30 locredit -1470",
+ "expExitCode": "0",
+ "verifyCmd": "$TC -d qdisc show dev $ETH",
+ "matchPattern": "qdisc cbs 8002: parent 8001:8 refcnt 2 hicredit 30 locredit -1470 sendslope -980000 idleslope 20000 offload 0",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $ETH root",
+ "echo \"1\" > /sys/bus/netdevsim/del_device"
+ ]
}
]
--
2.34.1


2023-08-07 21:58:50

by Vladimir Oltean

[permalink] [raw]
Subject: [PATCH v4 net-next 04/11] net/sched: taprio: delete misleading comment about preallocating child qdiscs

As mentioned in commit af7b29b1deaa ("Revert "net/sched: taprio: make
qdisc_leaf() see the per-netdev-queue pfifo child qdiscs"") - unlike
mqprio, taprio doesn't use q->qdiscs[] only as a temporary transport
between Qdisc_ops :: init() and Qdisc_ops :: attach().

Delete the comment, which is just stolen from mqprio, but there, the
usage patterns are a lot different, and this is nothing but confusing.

Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v4: none

net/sched/sch_taprio.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 831e6f3fda2a..d5693bd52e93 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -2099,11 +2099,8 @@ static int taprio_init(struct Qdisc *sch, struct nlattr *opt,
return -EOPNOTSUPP;
}

- /* pre-allocate qdisc, attachment can't fail */
- q->qdiscs = kcalloc(dev->num_tx_queues,
- sizeof(q->qdiscs[0]),
+ q->qdiscs = kcalloc(dev->num_tx_queues, sizeof(q->qdiscs[0]),
GFP_KERNEL);
-
if (!q->qdiscs)
return -ENOMEM;

--
2.34.1


2023-08-10 00:40:35

by patchwork-bot+netdevbpf

[permalink] [raw]
Subject: Re: [PATCH v4 net-next 00/11] Improve the taprio qdisc's relationship with its children

Hello:

This series was applied to netdev/net-next.git (main)
by Jakub Kicinski <[email protected]>:

On Mon, 7 Aug 2023 22:33:13 +0300 you wrote:
> Changes in v4:
> - Clean up some leftovers in the ptp_mock driver.
> - Add CONFIG_PTP_1588_CLOCK_MOCK to tools/testing/selftests/tc-testing/config
> - Wait for taprio schedule to become operational in the selftests
>
> Changes in v3:
> Fix ptp_mock compilation as module, fix small mistakes in selftests.
>
> [...]

Here is the summary with links:
- [v4,net-next,01/11] net/sched: taprio: don't access q->qdiscs[] in unoffloaded mode during attach()
https://git.kernel.org/netdev/net-next/c/09e0c3bbde90
- [v4,net-next,02/11] net/sched: taprio: keep child Qdisc refcount elevated at 2 in offload mode
https://git.kernel.org/netdev/net-next/c/25b0d4e4e41f
- [v4,net-next,03/11] net/sched: taprio: try again to report q->qdiscs[] to qdisc_leaf()
https://git.kernel.org/netdev/net-next/c/98766add2d55
- [v4,net-next,04/11] net/sched: taprio: delete misleading comment about preallocating child qdiscs
https://git.kernel.org/netdev/net-next/c/6e0ec800c174
- [v4,net-next,05/11] net/sched: taprio: dump class stats for the actual q->qdiscs[]
https://git.kernel.org/netdev/net-next/c/665338b2a7a0
- [v4,net-next,06/11] net: ptp: create a mock-up PTP Hardware Clock driver
https://git.kernel.org/netdev/net-next/c/40b0425f8ba1
- [v4,net-next,07/11] net: netdevsim: use mock PHC driver
https://git.kernel.org/netdev/net-next/c/b63e78fca889
- [v4,net-next,08/11] net: netdevsim: mimic tc-taprio offload
https://git.kernel.org/netdev/net-next/c/35da47fe1c47
- [v4,net-next,09/11] selftests/tc-testing: add ptp_mock Kconfig dependency
https://git.kernel.org/netdev/net-next/c/355adce3010b
- [v4,net-next,10/11] selftests/tc-testing: test that taprio can only be attached as root
https://git.kernel.org/netdev/net-next/c/1890cf08bd99
- [v4,net-next,11/11] selftests/tc-testing: verify that a qdisc can be grafted onto a taprio class
https://git.kernel.org/netdev/net-next/c/29c298d2bc82

You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html