Commit 29afcd69 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'improve-the-taprio-qdisc-s-relationship-with-its-children'

Vladimir Oltean says:

====================
Improve the taprio qdisc's relationship with its children

v1: https://lore.kernel.org/lkml/20230531173928.1942027-1-vladimir.oltean@nxp.com/

Prompted by Vinicius' request to consolidate some child Qdisc
dereferences in taprio:
https://lore.kernel.org/netdev/87edmxv7x2.fsf@intel.com/

I remembered that I had left some unfinished work in this Qdisc, namely
commit af7b29b1 ("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.
====================

Link: https://lore.kernel.org/r/20230807193324.4128292-1-vladimir.oltean@nxp.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents e972a547 29c298d2
......@@ -17170,6 +17170,13 @@ F: drivers/ptp/*
F: include/linux/ptp_cl*
K: (?:\b|_)ptp(?:\b|_)
PTP MOCKUP CLOCK SUPPORT
M: Vladimir Oltean <vladimir.oltean@nxp.com>
L: netdev@vger.kernel.org
S: Maintained
F: drivers/ptp/ptp_mock.c
F: include/linux/ptp_mock.h
PTP VIRTUAL CLOCK SUPPORT
M: Yangbo Lu <yangbo.lu@nxp.com>
L: netdev@vger.kernel.org
......
......@@ -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
......
......@@ -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)
......
......@@ -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,
......@@ -291,13 +318,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 +353,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 +418,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);
}
......
......@@ -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;
......
......@@ -155,6 +155,17 @@ config PTP_1588_CLOCK_IDTCM
To compile this driver as a module, choose M here: the module
will be called ptp_clockmatrix.
config PTP_1588_CLOCK_MOCK
tristate "Mock-up PTP clock"
depends on PTP_1588_CLOCK
help
This driver offers a set of PTP clock manipulation operations over
the system monotonic time. It can be used by virtual network device
drivers to emulate PTP capabilities.
To compile this driver as a module, choose M here: the module
will be called ptp_mock.
config PTP_1588_CLOCK_VMW
tristate "VMware virtual PTP clock"
depends on ACPI && HYPERVISOR_GUEST && X86
......
......@@ -16,6 +16,7 @@ ptp-qoriq-y += ptp_qoriq.o
ptp-qoriq-$(CONFIG_DEBUG_FS) += ptp_qoriq_debugfs.o
obj-$(CONFIG_PTP_1588_CLOCK_IDTCM) += ptp_clockmatrix.o
obj-$(CONFIG_PTP_1588_CLOCK_IDT82P33) += ptp_idt82p33.o
obj-$(CONFIG_PTP_1588_CLOCK_MOCK) += ptp_mock.o
obj-$(CONFIG_PTP_1588_CLOCK_VMW) += ptp_vmw.o
obj-$(CONFIG_PTP_1588_CLOCK_OCP) += ptp_ocp.o
obj-$(CONFIG_PTP_DFL_TOD) += ptp_dfl_tod.o
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2023 NXP
*
* Mock-up PTP Hardware Clock driver for virtual network devices
*
* Create a PTP clock which offers PTP time manipulation operations
* using a timecounter/cyclecounter on top of CLOCK_MONOTONIC_RAW.
*/
#include <linux/ptp_clock_kernel.h>
#include <linux/ptp_mock.h>
#include <linux/timecounter.h>
/* Clamp scaled_ppm between -2,097,152,000 and 2,097,152,000,
* and thus "adj" between -68,719,476 and 68,719,476
*/
#define MOCK_PHC_MAX_ADJ_PPB 32000000
/* Timestamps from ktime_get_raw() have 1 ns resolution, so the scale factor
* (MULT >> SHIFT) needs to be 1. Pick SHIFT as 31 bits, which translates
* MULT(freq 0) into 0x80000000.
*/
#define MOCK_PHC_CC_SHIFT 31
#define MOCK_PHC_CC_MULT (1 << MOCK_PHC_CC_SHIFT)
#define MOCK_PHC_FADJ_SHIFT 9
#define MOCK_PHC_FADJ_DENOMINATOR 15625ULL
/* The largest cycle_delta that timecounter_read_delta() can handle without a
* 64-bit overflow during the multiplication with cc->mult, given the max "adj"
* we permit, is ~8.3 seconds. Make sure readouts are more frequent than that.
*/
#define MOCK_PHC_REFRESH_INTERVAL (HZ * 5)
#define info_to_phc(d) container_of((d), struct mock_phc, info)
struct mock_phc {
struct ptp_clock_info info;
struct ptp_clock *clock;
struct timecounter tc;
struct cyclecounter cc;
spinlock_t lock;
};
static u64 mock_phc_cc_read(const struct cyclecounter *cc)
{
return ktime_get_raw_ns();
}
static int mock_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm)
{
struct mock_phc *phc = info_to_phc(info);
s64 adj;
adj = (s64)scaled_ppm << MOCK_PHC_FADJ_SHIFT;
adj = div_s64(adj, MOCK_PHC_FADJ_DENOMINATOR);
spin_lock(&phc->lock);
timecounter_read(&phc->tc);
phc->cc.mult = MOCK_PHC_CC_MULT + adj;
spin_unlock(&phc->lock);
return 0;
}
static int mock_phc_adjtime(struct ptp_clock_info *info, s64 delta)
{
struct mock_phc *phc = info_to_phc(info);
spin_lock(&phc->lock);
timecounter_adjtime(&phc->tc, delta);
spin_unlock(&phc->lock);
return 0;
}
static int mock_phc_settime64(struct ptp_clock_info *info,
const struct timespec64 *ts)
{
struct mock_phc *phc = info_to_phc(info);
u64 ns = timespec64_to_ns(ts);
spin_lock(&phc->lock);
timecounter_init(&phc->tc, &phc->cc, ns);
spin_unlock(&phc->lock);
return 0;
}
static int mock_phc_gettime64(struct ptp_clock_info *info, struct timespec64 *ts)
{
struct mock_phc *phc = info_to_phc(info);
u64 ns;
spin_lock(&phc->lock);
ns = timecounter_read(&phc->tc);
spin_unlock(&phc->lock);
*ts = ns_to_timespec64(ns);
return 0;
}
static long mock_phc_refresh(struct ptp_clock_info *info)
{
struct timespec64 ts;
mock_phc_gettime64(info, &ts);
return MOCK_PHC_REFRESH_INTERVAL;
}
int mock_phc_index(struct mock_phc *phc)
{
return ptp_clock_index(phc->clock);
}
EXPORT_SYMBOL_GPL(mock_phc_index);
struct mock_phc *mock_phc_create(struct device *dev)
{
struct mock_phc *phc;
int err;
phc = kzalloc(sizeof(*phc), GFP_KERNEL);
if (!phc) {
err = -ENOMEM;
goto out;
}
phc->info = (struct ptp_clock_info) {
.owner = THIS_MODULE,
.name = "Mock-up PTP clock",
.max_adj = MOCK_PHC_MAX_ADJ_PPB,
.adjfine = mock_phc_adjfine,
.adjtime = mock_phc_adjtime,
.gettime64 = mock_phc_gettime64,
.settime64 = mock_phc_settime64,
.do_aux_work = mock_phc_refresh,
};
phc->cc = (struct cyclecounter) {
.read = mock_phc_cc_read,
.mask = CYCLECOUNTER_MASK(64),
.mult = MOCK_PHC_CC_MULT,
.shift = MOCK_PHC_CC_SHIFT,
};
spin_lock_init(&phc->lock);
timecounter_init(&phc->tc, &phc->cc, 0);
phc->clock = ptp_clock_register(&phc->info, dev);
if (IS_ERR(phc->clock)) {
err = PTR_ERR(phc->clock);
goto out_free_phc;
}
ptp_schedule_worker(phc->clock, MOCK_PHC_REFRESH_INTERVAL);
return phc;
out_free_phc:
kfree(phc);
out:
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(mock_phc_create);
void mock_phc_destroy(struct mock_phc *phc)
{
ptp_clock_unregister(phc->clock);
kfree(phc);
}
EXPORT_SYMBOL_GPL(mock_phc_destroy);
MODULE_DESCRIPTION("Mock-up PTP Hardware Clock driver");
MODULE_LICENSE("GPL");
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Mock-up PTP Hardware Clock driver for virtual network devices
*
* Copyright 2023 NXP
*/
#ifndef _PTP_MOCK_H_
#define _PTP_MOCK_H_
struct device;
struct mock_phc;
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK_MOCK)
struct mock_phc *mock_phc_create(struct device *dev);
void mock_phc_destroy(struct mock_phc *phc);
int mock_phc_index(struct mock_phc *phc);
#else
static inline struct mock_phc *mock_phc_create(struct device *dev)
{
return NULL;
}
static inline void mock_phc_destroy(struct mock_phc *phc)
{
}
static inline int mock_phc_index(struct mock_phc *phc)
{
return -1;
}
#endif
#endif /* _PTP_MOCK_H_ */
......@@ -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;
......@@ -2145,25 +2142,32 @@ 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 Qdisc *old;
struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, ntx);
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(qdisc->dev_queue, qdisc);
dev_queue_qdisc = qdisc;
} else {
old = dev_graft_qdisc(qdisc->dev_queue, sch);
qdisc_refcount_inc(sch);
/* In software mode, attach the root taprio qdisc
* to all netdev TX queues, so that dev_qdisc_enqueue()
* goes through taprio_enqueue().
*/
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,
......@@ -2192,13 +2196,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;
......@@ -2436,12 +2450,14 @@ static int taprio_dump(struct Qdisc *sch, struct sk_buff *skb)
static struct Qdisc *taprio_leaf(struct Qdisc *sch, unsigned long cl)
{
struct netdev_queue *dev_queue = taprio_queue_get(sch, cl);
struct taprio_sched *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);
unsigned int ntx = cl - 1;
if (!dev_queue)
if (ntx >= dev->num_tx_queues)
return NULL;
return rtnl_dereference(dev_queue->qdisc_sleeping);
return q->qdiscs[ntx];
}
static unsigned long taprio_find(struct Qdisc *sch, u32 classid)
......@@ -2456,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;
}
......@@ -2470,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;
......
......@@ -96,10 +96,11 @@ CONFIG_NET_SCH_FIFO=y
CONFIG_NET_SCH_ETS=m
CONFIG_NET_SCH_RED=m
CONFIG_NET_SCH_FQ_PIE=m
CONFIG_NETDEVSIM=m
#
## Network testing
#
CONFIG_CAN=m
CONFIG_ATM=y
CONFIG_NETDEVSIM=m
CONFIG_PTP_1588_CLOCK_MOCK=m
#!/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
......@@ -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"
......@@ -156,5 +156,105 @@
"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"
]
},
{
"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"
]
}
]
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment