Commit 60cbd38b authored by David S. Miller's avatar David S. Miller

Merge branch 'xstats-for-tc-taprio'

Vladimir Oltean says:

====================
xstats for tc-taprio

As a result of this discussion:
https://lore.kernel.org/intel-wired-lan/20230411055543.24177-1-muhammad.husaini.zulkifli@intel.com/

it became apparent that tc-taprio should make an effort to standardize
statistics counters related to the 802.1Qbv scheduling as implemented
by the NIC. I'm presenting here one counter suggested by the standard,
and one counter defined by the NXP ENETC controller from LS1028A. Both
counters are reported globally and per traffic class - drivers get
different callbacks for reporting both of these, and get to choose what
to report in both cases.

The iproute2 counterpart is available here for testing:
https://github.com/vladimiroltean/iproute2/commits/taprio-xstats
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5dedf5c4 4802fca8
......@@ -1885,13 +1885,17 @@ static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port,
case TC_SETUP_QDISC_TAPRIO: {
struct tc_taprio_qopt_offload *taprio = type_data;
switch (taprio->cmd) {
case TAPRIO_CMD_REPLACE:
if (!hellcreek_validate_schedule(hellcreek, taprio))
return -EOPNOTSUPP;
if (taprio->enable)
return hellcreek_port_set_schedule(ds, port, taprio);
case TAPRIO_CMD_DESTROY:
return hellcreek_port_del_schedule(ds, port);
default:
return -EOPNOTSUPP;
}
}
default:
return -EOPNOTSUPP;
......
......@@ -1411,7 +1411,7 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,
mutex_lock(&ocelot->tas_lock);
if (!taprio->enable) {
if (taprio->cmd == TAPRIO_CMD_DESTROY) {
ocelot_port_mqprio(ocelot, port, &taprio->mqprio);
ocelot_rmw_rix(ocelot, 0, QSYS_TAG_CONFIG_ENABLE,
QSYS_TAG_CONFIG, port);
......@@ -1423,6 +1423,8 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,
mutex_unlock(&ocelot->tas_lock);
return 0;
} else if (taprio->cmd != TAPRIO_CMD_REPLACE) {
return -EOPNOTSUPP;
}
ret = ocelot_port_mqprio(ocelot, port, &taprio->mqprio);
......
......@@ -516,10 +516,11 @@ int sja1105_setup_tc_taprio(struct dsa_switch *ds, int port,
/* Can't change an already configured port (must delete qdisc first).
* Can't delete the qdisc from an unconfigured port.
*/
if (!!tas_data->offload[port] == admin->enable)
if ((!!tas_data->offload[port] && admin->cmd == TAPRIO_CMD_REPLACE) ||
(!tas_data->offload[port] && admin->cmd == TAPRIO_CMD_DESTROY))
return -EINVAL;
if (!admin->enable) {
if (admin->cmd == TAPRIO_CMD_DESTROY) {
taprio_offload_free(tas_data->offload[port]);
tas_data->offload[port] = NULL;
......@@ -528,6 +529,8 @@ int sja1105_setup_tc_taprio(struct dsa_switch *ds, int port,
return rc;
return sja1105_static_config_reload(priv, SJA1105_SCHEDULING);
} else if (admin->cmd != TAPRIO_CMD_REPLACE) {
return -EOPNOTSUPP;
}
/* The cycle time extension is the amount of time the last cycle from
......
......@@ -329,7 +329,7 @@ static bool disable_taprio(struct tsnep_adapter *adapter)
int retval;
memset(&qopt, 0, sizeof(qopt));
qopt.enable = 0;
qopt.cmd = TAPRIO_CMD_DESTROY;
retval = tsnep_tc_setup(adapter->netdev, TC_SETUP_QDISC_TAPRIO, &qopt);
if (retval)
return false;
......@@ -360,7 +360,7 @@ static bool tsnep_test_taprio(struct tsnep_adapter *adapter)
for (i = 0; i < 255; i++)
qopt->entries[i].command = TC_TAPRIO_CMD_SET_GATES;
qopt->enable = 1;
qopt->cmd = TAPRIO_CMD_REPLACE;
qopt->base_time = ktime_set(0, 0);
qopt->cycle_time = 1500000;
qopt->cycle_time_extension = 0;
......@@ -382,7 +382,7 @@ static bool tsnep_test_taprio(struct tsnep_adapter *adapter)
if (!run_taprio(adapter, qopt, 100))
goto failed;
qopt->enable = 1;
qopt->cmd = TAPRIO_CMD_REPLACE;
qopt->base_time = ktime_set(0, 0);
qopt->cycle_time = 411854;
qopt->cycle_time_extension = 0;
......@@ -406,7 +406,7 @@ static bool tsnep_test_taprio(struct tsnep_adapter *adapter)
if (!run_taprio(adapter, qopt, 100))
goto failed;
qopt->enable = 1;
qopt->cmd = TAPRIO_CMD_REPLACE;
qopt->base_time = ktime_set(0, 0);
delay_base_time(adapter, qopt, 12);
qopt->cycle_time = 125000;
......@@ -457,7 +457,7 @@ static bool tsnep_test_taprio_change(struct tsnep_adapter *adapter)
for (i = 0; i < 255; i++)
qopt->entries[i].command = TC_TAPRIO_CMD_SET_GATES;
qopt->enable = 1;
qopt->cmd = TAPRIO_CMD_REPLACE;
qopt->base_time = ktime_set(0, 0);
qopt->cycle_time = 100000;
qopt->cycle_time_extension = 0;
......@@ -610,7 +610,7 @@ static bool tsnep_test_taprio_extension(struct tsnep_adapter *adapter)
for (i = 0; i < 255; i++)
qopt->entries[i].command = TC_TAPRIO_CMD_SET_GATES;
qopt->enable = 1;
qopt->cmd = TAPRIO_CMD_REPLACE;
qopt->base_time = ktime_set(0, 0);
qopt->cycle_time = 100000;
qopt->cycle_time_extension = 50000;
......
......@@ -325,7 +325,7 @@ static int tsnep_taprio(struct tsnep_adapter *adapter,
if (!adapter->gate_control)
return -EOPNOTSUPP;
if (!qopt->enable) {
if (qopt->cmd == TAPRIO_CMD_DESTROY) {
/* disable gate control if active */
mutex_lock(&adapter->gate_control_lock);
......@@ -337,6 +337,8 @@ static int tsnep_taprio(struct tsnep_adapter *adapter,
mutex_unlock(&adapter->gate_control_lock);
return 0;
} else if (qopt->cmd != TAPRIO_CMD_REPLACE) {
return -EOPNOTSUPP;
}
retval = tsnep_validate_gcl(qopt);
......
......@@ -2624,7 +2624,7 @@ static void enetc_debug_tx_ring_prios(struct enetc_ndev_priv *priv)
priv->tx_ring[i]->prio);
}
static void enetc_reset_tc_mqprio(struct net_device *ndev)
void enetc_reset_tc_mqprio(struct net_device *ndev)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct enetc_hw *hw = &priv->si->hw;
......@@ -2649,6 +2649,7 @@ static void enetc_reset_tc_mqprio(struct net_device *ndev)
enetc_change_preemptible_tcs(priv, 0);
}
EXPORT_SYMBOL_GPL(enetc_reset_tc_mqprio);
int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data)
{
......
......@@ -429,6 +429,7 @@ struct net_device_stats *enetc_get_stats(struct net_device *ndev);
void enetc_set_features(struct net_device *ndev, netdev_features_t features);
int enetc_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd);
int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data);
void enetc_reset_tc_mqprio(struct net_device *ndev);
int enetc_setup_bpf(struct net_device *ndev, struct netdev_bpf *bpf);
int enetc_xdp_xmit(struct net_device *ndev, int num_frames,
struct xdp_frame **frames, u32 flags);
......
......@@ -43,10 +43,9 @@ void enetc_sched_speed_set(struct enetc_ndev_priv *priv, int speed)
enetc_port_wr(hw, ENETC_PMR, (tmp & ~ENETC_PMR_PSPEED_MASK) | pspeed);
}
static int enetc_setup_taprio(struct net_device *ndev,
static int enetc_setup_taprio(struct enetc_ndev_priv *priv,
struct tc_taprio_qopt_offload *admin_conf)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct enetc_hw *hw = &priv->si->hw;
struct enetc_cbd cbd = {.cmd = 0};
struct tgs_gcl_conf *gcl_config;
......@@ -60,19 +59,13 @@ static int enetc_setup_taprio(struct net_device *ndev,
int err;
int i;
/* TSD and Qbv are mutually exclusive in hardware */
for (i = 0; i < priv->num_tx_rings; i++)
if (priv->tx_ring[i]->tsd_enable)
return -EBUSY;
if (admin_conf->num_entries > enetc_get_max_gcl_len(hw))
return -EINVAL;
gcl_len = admin_conf->num_entries;
tge = enetc_rd(hw, ENETC_PTGCR);
if (!admin_conf->enable) {
enetc_wr(hw, ENETC_PTGCR, tge & ~ENETC_PTGCR_TGE);
enetc_reset_ptcmsdur(hw);
priv->active_offloads &= ~ENETC_F_QBV;
return 0;
}
if (admin_conf->cycle_time > U32_MAX ||
admin_conf->cycle_time_extension > U32_MAX)
......@@ -82,6 +75,7 @@ static int enetc_setup_taprio(struct net_device *ndev,
* control BD descriptor.
*/
gcl_config = &cbd.gcl_conf;
gcl_len = admin_conf->num_entries;
data_size = struct_size(gcl_data, entry, gcl_len);
tmp = enetc_cbd_alloc_data_mem(priv->si, &cbd, data_size,
......@@ -115,6 +109,7 @@ static int enetc_setup_taprio(struct net_device *ndev,
cbd.cls = BDCR_CMD_PORT_GCL;
cbd.status_flags = 0;
tge = enetc_rd(hw, ENETC_PTGCR);
enetc_wr(hw, ENETC_PTGCR, tge | ENETC_PTGCR_TGE);
err = enetc_send_cmd(priv->si, &cbd);
......@@ -132,25 +127,92 @@ static int enetc_setup_taprio(struct net_device *ndev,
return 0;
}
int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data)
static void enetc_reset_taprio(struct enetc_ndev_priv *priv)
{
struct enetc_hw *hw = &priv->si->hw;
u32 val;
val = enetc_rd(hw, ENETC_PTGCR);
enetc_wr(hw, ENETC_PTGCR, val & ~ENETC_PTGCR_TGE);
enetc_reset_ptcmsdur(hw);
priv->active_offloads &= ~ENETC_F_QBV;
}
static void enetc_taprio_destroy(struct net_device *ndev)
{
struct tc_taprio_qopt_offload *taprio = type_data;
struct enetc_ndev_priv *priv = netdev_priv(ndev);
int err, i;
/* TSD and Qbv are mutually exclusive in hardware */
enetc_reset_taprio(priv);
enetc_reset_tc_mqprio(ndev);
}
static void enetc_taprio_stats(struct net_device *ndev,
struct tc_taprio_qopt_stats *stats)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
u64 window_drops = 0;
int i;
for (i = 0; i < priv->num_tx_rings; i++)
if (priv->tx_ring[i]->tsd_enable)
return -EBUSY;
window_drops += priv->tx_ring[i]->stats.win_drop;
stats->window_drops = window_drops;
}
static void enetc_taprio_tc_stats(struct net_device *ndev,
struct tc_taprio_qopt_tc_stats *tc_stats)
{
struct tc_taprio_qopt_stats *stats = &tc_stats->stats;
struct enetc_ndev_priv *priv = netdev_priv(ndev);
int tc = tc_stats->tc;
u64 window_drops = 0;
int i;
for (i = 0; i < priv->num_tx_rings; i++)
if (priv->tx_ring[i]->prio == tc)
window_drops += priv->tx_ring[i]->stats.win_drop;
stats->window_drops = window_drops;
}
static int enetc_taprio_replace(struct net_device *ndev,
struct tc_taprio_qopt_offload *offload)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
int err;
err = enetc_setup_tc_mqprio(ndev, &taprio->mqprio);
err = enetc_setup_tc_mqprio(ndev, &offload->mqprio);
if (err)
return err;
err = enetc_setup_taprio(ndev, taprio);
if (err) {
taprio->mqprio.qopt.num_tc = 0;
enetc_setup_tc_mqprio(ndev, &taprio->mqprio);
err = enetc_setup_taprio(priv, offload);
if (err)
enetc_reset_tc_mqprio(ndev);
return err;
}
int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data)
{
struct tc_taprio_qopt_offload *offload = type_data;
int err = 0;
switch (offload->cmd) {
case TAPRIO_CMD_REPLACE:
err = enetc_taprio_replace(ndev, offload);
break;
case TAPRIO_CMD_DESTROY:
enetc_taprio_destroy(ndev);
break;
case TAPRIO_CMD_STATS:
enetc_taprio_stats(ndev, &offload->stats);
break;
case TAPRIO_CMD_TC_STATS:
enetc_taprio_tc_stats(ndev, &offload->tc_stats);
break;
default:
err = -EOPNOTSUPP;
}
return err;
......
......@@ -6113,9 +6113,18 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter,
size_t n;
int i;
adapter->qbv_enable = qopt->enable;
switch (qopt->cmd) {
case TAPRIO_CMD_REPLACE:
adapter->qbv_enable = true;
break;
case TAPRIO_CMD_DESTROY:
adapter->qbv_enable = false;
break;
default:
return -EOPNOTSUPP;
}
if (!qopt->enable)
if (!adapter->qbv_enable)
return igc_tsn_clear_schedule(adapter);
if (qopt->base_time < 0)
......
......@@ -21,8 +21,14 @@ static int lan966x_tc_setup_qdisc_mqprio(struct lan966x_port *port,
static int lan966x_tc_setup_qdisc_taprio(struct lan966x_port *port,
struct tc_taprio_qopt_offload *taprio)
{
return taprio->enable ? lan966x_taprio_add(port, taprio) :
lan966x_taprio_del(port);
switch (taprio->cmd) {
case TAPRIO_CMD_REPLACE:
return lan966x_taprio_add(port, taprio);
case TAPRIO_CMD_DESTROY:
return lan966x_taprio_del(port);
default:
return -EOPNOTSUPP;
}
}
static int lan966x_tc_setup_qdisc_tbf(struct lan966x_port *port,
......
......@@ -966,8 +966,11 @@ static int tc_setup_taprio(struct stmmac_priv *priv,
return -EOPNOTSUPP;
}
if (!qopt->enable)
if (qopt->cmd == TAPRIO_CMD_DESTROY)
goto disable;
else if (qopt->cmd != TAPRIO_CMD_REPLACE)
return -EOPNOTSUPP;
if (qopt->num_entries >= dep)
return -EINVAL;
if (!qopt->cycle_time)
......@@ -988,7 +991,7 @@ static int tc_setup_taprio(struct stmmac_priv *priv,
mutex_lock(&priv->plat->est->lock);
priv->plat->est->gcl_size = size;
priv->plat->est->enable = qopt->enable;
priv->plat->est->enable = qopt->cmd == TAPRIO_CMD_REPLACE;
mutex_unlock(&priv->plat->est->lock);
for (i = 0; i < size; i++) {
......
......@@ -450,7 +450,7 @@ static int am65_cpsw_configure_taprio(struct net_device *ndev,
am65_cpsw_est_update_state(ndev);
if (!est_new->taprio.enable) {
if (est_new->taprio.cmd == TAPRIO_CMD_DESTROY) {
am65_cpsw_stop_est(ndev);
return ret;
}
......@@ -476,7 +476,7 @@ static int am65_cpsw_configure_taprio(struct net_device *ndev,
am65_cpsw_est_set_sched_list(ndev, est_new);
am65_cpsw_port_est_assign_buf_num(ndev, est_new->buf);
am65_cpsw_est_set(ndev, est_new->taprio.enable);
am65_cpsw_est_set(ndev, est_new->taprio.cmd == TAPRIO_CMD_REPLACE);
if (tact == TACT_PROG) {
ret = am65_cpsw_timer_set(ndev, est_new);
......@@ -520,7 +520,7 @@ static int am65_cpsw_set_taprio(struct net_device *ndev, void *type_data)
am65_cpsw_cp_taprio(taprio, &est_new->taprio);
ret = am65_cpsw_configure_taprio(ndev, est_new);
if (!ret) {
if (taprio->enable) {
if (taprio->cmd == TAPRIO_CMD_REPLACE) {
devm_kfree(&ndev->dev, port->qos.est_admin);
port->qos.est_admin = est_new;
......@@ -564,8 +564,13 @@ static void am65_cpsw_est_link_up(struct net_device *ndev, int link_speed)
static int am65_cpsw_setup_taprio(struct net_device *ndev, void *type_data)
{
struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
struct tc_taprio_qopt_offload *taprio = type_data;
struct am65_cpsw_common *common = port->common;
if (taprio->cmd != TAPRIO_CMD_REPLACE &&
taprio->cmd != TAPRIO_CMD_DESTROY)
return -EOPNOTSUPP;
if (!IS_ENABLED(CONFIG_TI_AM65_CPSW_TAS))
return -ENODEV;
......
......@@ -185,6 +185,32 @@ struct tc_taprio_caps {
bool broken_mqprio:1;
};
enum tc_taprio_qopt_cmd {
TAPRIO_CMD_REPLACE,
TAPRIO_CMD_DESTROY,
TAPRIO_CMD_STATS,
TAPRIO_CMD_TC_STATS,
};
/**
* struct tc_taprio_qopt_stats - IEEE 802.1Qbv statistics
* @window_drops: Frames that were dropped because they were too large to be
* transmitted in any of the allotted time windows (open gates) for their
* traffic class.
* @tx_overruns: Frames still being transmitted by the MAC after the
* transmission gate associated with their traffic class has closed.
* Equivalent to `12.29.1.1.2 TransmissionOverrun` from 802.1Q-2018.
*/
struct tc_taprio_qopt_stats {
u64 window_drops;
u64 tx_overruns;
};
struct tc_taprio_qopt_tc_stats {
int tc;
struct tc_taprio_qopt_stats stats;
};
struct tc_taprio_sched_entry {
u8 command; /* TC_TAPRIO_CMD_* */
......@@ -194,9 +220,17 @@ struct tc_taprio_sched_entry {
};
struct tc_taprio_qopt_offload {
enum tc_taprio_qopt_cmd cmd;
union {
/* TAPRIO_CMD_STATS */
struct tc_taprio_qopt_stats stats;
/* TAPRIO_CMD_TC_STATS */
struct tc_taprio_qopt_tc_stats tc_stats;
/* TAPRIO_CMD_REPLACE */
struct {
struct tc_mqprio_qopt_offload mqprio;
struct netlink_ext_ack *extack;
u8 enable;
ktime_t base_time;
u64 cycle_time;
u64 cycle_time_extension;
......@@ -204,6 +238,8 @@ struct tc_taprio_qopt_offload {
size_t num_entries;
struct tc_taprio_sched_entry entries[];
};
};
};
#if IS_ENABLED(CONFIG_NET_SCH_TAPRIO)
......
......@@ -1259,6 +1259,16 @@ enum {
TCA_TAPRIO_TC_ENTRY_MAX = (__TCA_TAPRIO_TC_ENTRY_CNT - 1)
};
enum {
TCA_TAPRIO_OFFLOAD_STATS_PAD = 1, /* u64 */
TCA_TAPRIO_OFFLOAD_STATS_WINDOW_DROPS, /* u64 */
TCA_TAPRIO_OFFLOAD_STATS_TX_OVERRUNS, /* u64 */
/* add new constants above here */
__TCA_TAPRIO_OFFLOAD_STATS_CNT,
TCA_TAPRIO_OFFLOAD_STATS_MAX = (__TCA_TAPRIO_OFFLOAD_STATS_CNT - 1)
};
enum {
TCA_TAPRIO_ATTR_UNSPEC,
TCA_TAPRIO_ATTR_PRIOMAP, /* struct tc_mqprio_qopt */
......
......@@ -27,6 +27,8 @@
#include <net/sock.h>
#include <net/tcp.h>
#define TAPRIO_STAT_NOT_SET (~0ULL)
#include "sch_mqprio_lib.h"
static LIST_HEAD(taprio_list);
......@@ -1524,7 +1526,7 @@ static int taprio_enable_offload(struct net_device *dev,
"Not enough memory for enabling offload mode");
return -ENOMEM;
}
offload->enable = 1;
offload->cmd = TAPRIO_CMD_REPLACE;
offload->extack = extack;
mqprio_qopt_reconstruct(dev, &offload->mqprio.qopt);
offload->mqprio.extack = extack;
......@@ -1572,7 +1574,7 @@ static int taprio_disable_offload(struct net_device *dev,
"Not enough memory to disable offload mode");
return -ENOMEM;
}
offload->enable = 0;
offload->cmd = TAPRIO_CMD_DESTROY;
err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TAPRIO, offload);
if (err < 0) {
......@@ -2289,6 +2291,72 @@ static int taprio_dump_tc_entries(struct sk_buff *skb,
return -EMSGSIZE;
}
static int taprio_put_stat(struct sk_buff *skb, u64 val, u16 attrtype)
{
if (val == TAPRIO_STAT_NOT_SET)
return 0;
if (nla_put_u64_64bit(skb, attrtype, val, TCA_TAPRIO_OFFLOAD_STATS_PAD))
return -EMSGSIZE;
return 0;
}
static int taprio_dump_xstats(struct Qdisc *sch, struct gnet_dump *d,
struct tc_taprio_qopt_offload *offload,
struct tc_taprio_qopt_stats *stats)
{
struct net_device *dev = qdisc_dev(sch);
const struct net_device_ops *ops;
struct sk_buff *skb = d->skb;
struct nlattr *xstats;
int err;
ops = qdisc_dev(sch)->netdev_ops;
/* FIXME I could use qdisc_offload_dump_helper(), but that messes
* with sch->flags depending on whether the device reports taprio
* stats, and I'm not sure whether that's a good idea, considering
* that stats are optional to the offload itself
*/
if (!ops->ndo_setup_tc)
return 0;
memset(stats, 0xff, sizeof(*stats));
err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TAPRIO, offload);
if (err == -EOPNOTSUPP)
return 0;
if (err)
return err;
xstats = nla_nest_start(skb, TCA_STATS_APP);
if (!xstats)
goto err;
if (taprio_put_stat(skb, stats->window_drops,
TCA_TAPRIO_OFFLOAD_STATS_WINDOW_DROPS) ||
taprio_put_stat(skb, stats->tx_overruns,
TCA_TAPRIO_OFFLOAD_STATS_TX_OVERRUNS))
goto err_cancel;
nla_nest_end(skb, xstats);
return 0;
err_cancel:
nla_nest_cancel(skb, xstats);
err:
return -EMSGSIZE;
}
static int taprio_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
{
struct tc_taprio_qopt_offload offload = {
.cmd = TAPRIO_CMD_STATS,
};
return taprio_dump_xstats(sch, d, &offload, &offload.stats);
}
static int taprio_dump(struct Qdisc *sch, struct sk_buff *skb)
{
struct taprio_sched *q = qdisc_priv(sch);
......@@ -2388,12 +2456,19 @@ static int taprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
__acquires(d->lock)
{
struct netdev_queue *dev_queue = taprio_queue_get(sch, cl);
struct Qdisc *child = dev_queue->qdisc_sleeping;
struct tc_taprio_qopt_offload offload = {
.cmd = TAPRIO_CMD_TC_STATS,
.tc_stats = {
.tc = cl - 1,
},
};
sch = dev_queue->qdisc_sleeping;
if (gnet_stats_copy_basic(d, NULL, &sch->bstats, true) < 0 ||
qdisc_qstats_copy(d, sch) < 0)
if (gnet_stats_copy_basic(d, NULL, &child->bstats, true) < 0 ||
qdisc_qstats_copy(d, child) < 0)
return -1;
return 0;
return taprio_dump_xstats(sch, d, &offload, &offload.tc_stats.stats);
}
static void taprio_walk(struct Qdisc *sch, struct qdisc_walker *arg)
......@@ -2440,6 +2515,7 @@ static struct Qdisc_ops taprio_qdisc_ops __read_mostly = {
.dequeue = taprio_dequeue,
.enqueue = taprio_enqueue,
.dump = taprio_dump,
.dump_stats = taprio_dump_stats,
.owner = THIS_MODULE,
};
......
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