Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: David S. Miller <davem@davemloft.net>

+284 -64
+9 -5
drivers/net/dsa/hirschmann/hellcreek.c
··· 1885 1885 case TC_SETUP_QDISC_TAPRIO: { 1886 1886 struct tc_taprio_qopt_offload *taprio = type_data; 1887 1887 1888 - if (!hellcreek_validate_schedule(hellcreek, taprio)) 1889 - return -EOPNOTSUPP; 1888 + switch (taprio->cmd) { 1889 + case TAPRIO_CMD_REPLACE: 1890 + if (!hellcreek_validate_schedule(hellcreek, taprio)) 1891 + return -EOPNOTSUPP; 1890 1892 1891 - if (taprio->enable) 1892 1893 return hellcreek_port_set_schedule(ds, port, taprio); 1893 - 1894 - return hellcreek_port_del_schedule(ds, port); 1894 + case TAPRIO_CMD_DESTROY: 1895 + return hellcreek_port_del_schedule(ds, port); 1896 + default: 1897 + return -EOPNOTSUPP; 1898 + } 1895 1899 } 1896 1900 default: 1897 1901 return -EOPNOTSUPP;
+3 -1
drivers/net/dsa/ocelot/felix_vsc9959.c
··· 1411 1411 1412 1412 mutex_lock(&ocelot->tas_lock); 1413 1413 1414 - if (!taprio->enable) { 1414 + if (taprio->cmd == TAPRIO_CMD_DESTROY) { 1415 1415 ocelot_port_mqprio(ocelot, port, &taprio->mqprio); 1416 1416 ocelot_rmw_rix(ocelot, 0, QSYS_TAG_CONFIG_ENABLE, 1417 1417 QSYS_TAG_CONFIG, port); ··· 1423 1423 1424 1424 mutex_unlock(&ocelot->tas_lock); 1425 1425 return 0; 1426 + } else if (taprio->cmd != TAPRIO_CMD_REPLACE) { 1427 + return -EOPNOTSUPP; 1426 1428 } 1427 1429 1428 1430 ret = ocelot_port_mqprio(ocelot, port, &taprio->mqprio);
+5 -2
drivers/net/dsa/sja1105/sja1105_tas.c
··· 516 516 /* Can't change an already configured port (must delete qdisc first). 517 517 * Can't delete the qdisc from an unconfigured port. 518 518 */ 519 - if (!!tas_data->offload[port] == admin->enable) 519 + if ((!!tas_data->offload[port] && admin->cmd == TAPRIO_CMD_REPLACE) || 520 + (!tas_data->offload[port] && admin->cmd == TAPRIO_CMD_DESTROY)) 520 521 return -EINVAL; 521 522 522 - if (!admin->enable) { 523 + if (admin->cmd == TAPRIO_CMD_DESTROY) { 523 524 taprio_offload_free(tas_data->offload[port]); 524 525 tas_data->offload[port] = NULL; 525 526 ··· 529 528 return rc; 530 529 531 530 return sja1105_static_config_reload(priv, SJA1105_SCHEDULING); 531 + } else if (admin->cmd != TAPRIO_CMD_REPLACE) { 532 + return -EOPNOTSUPP; 532 533 } 533 534 534 535 /* The cycle time extension is the amount of time the last cycle from
+6 -6
drivers/net/ethernet/engleder/tsnep_selftests.c
··· 329 329 int retval; 330 330 331 331 memset(&qopt, 0, sizeof(qopt)); 332 - qopt.enable = 0; 332 + qopt.cmd = TAPRIO_CMD_DESTROY; 333 333 retval = tsnep_tc_setup(adapter->netdev, TC_SETUP_QDISC_TAPRIO, &qopt); 334 334 if (retval) 335 335 return false; ··· 360 360 for (i = 0; i < 255; i++) 361 361 qopt->entries[i].command = TC_TAPRIO_CMD_SET_GATES; 362 362 363 - qopt->enable = 1; 363 + qopt->cmd = TAPRIO_CMD_REPLACE; 364 364 qopt->base_time = ktime_set(0, 0); 365 365 qopt->cycle_time = 1500000; 366 366 qopt->cycle_time_extension = 0; ··· 382 382 if (!run_taprio(adapter, qopt, 100)) 383 383 goto failed; 384 384 385 - qopt->enable = 1; 385 + qopt->cmd = TAPRIO_CMD_REPLACE; 386 386 qopt->base_time = ktime_set(0, 0); 387 387 qopt->cycle_time = 411854; 388 388 qopt->cycle_time_extension = 0; ··· 406 406 if (!run_taprio(adapter, qopt, 100)) 407 407 goto failed; 408 408 409 - qopt->enable = 1; 409 + qopt->cmd = TAPRIO_CMD_REPLACE; 410 410 qopt->base_time = ktime_set(0, 0); 411 411 delay_base_time(adapter, qopt, 12); 412 412 qopt->cycle_time = 125000; ··· 457 457 for (i = 0; i < 255; i++) 458 458 qopt->entries[i].command = TC_TAPRIO_CMD_SET_GATES; 459 459 460 - qopt->enable = 1; 460 + qopt->cmd = TAPRIO_CMD_REPLACE; 461 461 qopt->base_time = ktime_set(0, 0); 462 462 qopt->cycle_time = 100000; 463 463 qopt->cycle_time_extension = 0; ··· 610 610 for (i = 0; i < 255; i++) 611 611 qopt->entries[i].command = TC_TAPRIO_CMD_SET_GATES; 612 612 613 - qopt->enable = 1; 613 + qopt->cmd = TAPRIO_CMD_REPLACE; 614 614 qopt->base_time = ktime_set(0, 0); 615 615 qopt->cycle_time = 100000; 616 616 qopt->cycle_time_extension = 50000;
+3 -1
drivers/net/ethernet/engleder/tsnep_tc.c
··· 325 325 if (!adapter->gate_control) 326 326 return -EOPNOTSUPP; 327 327 328 - if (!qopt->enable) { 328 + if (qopt->cmd == TAPRIO_CMD_DESTROY) { 329 329 /* disable gate control if active */ 330 330 mutex_lock(&adapter->gate_control_lock); 331 331 ··· 337 337 mutex_unlock(&adapter->gate_control_lock); 338 338 339 339 return 0; 340 + } else if (qopt->cmd != TAPRIO_CMD_REPLACE) { 341 + return -EOPNOTSUPP; 340 342 } 341 343 342 344 retval = tsnep_validate_gcl(qopt);
+2 -1
drivers/net/ethernet/freescale/enetc/enetc.c
··· 2624 2624 priv->tx_ring[i]->prio); 2625 2625 } 2626 2626 2627 - static void enetc_reset_tc_mqprio(struct net_device *ndev) 2627 + void enetc_reset_tc_mqprio(struct net_device *ndev) 2628 2628 { 2629 2629 struct enetc_ndev_priv *priv = netdev_priv(ndev); 2630 2630 struct enetc_hw *hw = &priv->si->hw; ··· 2649 2649 2650 2650 enetc_change_preemptible_tcs(priv, 0); 2651 2651 } 2652 + EXPORT_SYMBOL_GPL(enetc_reset_tc_mqprio); 2652 2653 2653 2654 int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data) 2654 2655 {
+1
drivers/net/ethernet/freescale/enetc/enetc.h
··· 429 429 void enetc_set_features(struct net_device *ndev, netdev_features_t features); 430 430 int enetc_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd); 431 431 int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data); 432 + void enetc_reset_tc_mqprio(struct net_device *ndev); 432 433 int enetc_setup_bpf(struct net_device *ndev, struct netdev_bpf *bpf); 433 434 int enetc_xdp_xmit(struct net_device *ndev, int num_frames, 434 435 struct xdp_frame **frames, u32 flags);
+86 -24
drivers/net/ethernet/freescale/enetc/enetc_qos.c
··· 43 43 enetc_port_wr(hw, ENETC_PMR, (tmp & ~ENETC_PMR_PSPEED_MASK) | pspeed); 44 44 } 45 45 46 - static int enetc_setup_taprio(struct net_device *ndev, 46 + static int enetc_setup_taprio(struct enetc_ndev_priv *priv, 47 47 struct tc_taprio_qopt_offload *admin_conf) 48 48 { 49 - struct enetc_ndev_priv *priv = netdev_priv(ndev); 50 49 struct enetc_hw *hw = &priv->si->hw; 51 50 struct enetc_cbd cbd = {.cmd = 0}; 52 51 struct tgs_gcl_conf *gcl_config; ··· 59 60 int err; 60 61 int i; 61 62 63 + /* TSD and Qbv are mutually exclusive in hardware */ 64 + for (i = 0; i < priv->num_tx_rings; i++) 65 + if (priv->tx_ring[i]->tsd_enable) 66 + return -EBUSY; 67 + 62 68 if (admin_conf->num_entries > enetc_get_max_gcl_len(hw)) 63 69 return -EINVAL; 64 - gcl_len = admin_conf->num_entries; 65 - 66 - tge = enetc_rd(hw, ENETC_PTGCR); 67 - if (!admin_conf->enable) { 68 - enetc_wr(hw, ENETC_PTGCR, tge & ~ENETC_PTGCR_TGE); 69 - enetc_reset_ptcmsdur(hw); 70 - 71 - priv->active_offloads &= ~ENETC_F_QBV; 72 - 73 - return 0; 74 - } 75 70 76 71 if (admin_conf->cycle_time > U32_MAX || 77 72 admin_conf->cycle_time_extension > U32_MAX) ··· 75 82 * control BD descriptor. 76 83 */ 77 84 gcl_config = &cbd.gcl_conf; 85 + gcl_len = admin_conf->num_entries; 78 86 79 87 data_size = struct_size(gcl_data, entry, gcl_len); 80 88 tmp = enetc_cbd_alloc_data_mem(priv->si, &cbd, data_size, ··· 109 115 cbd.cls = BDCR_CMD_PORT_GCL; 110 116 cbd.status_flags = 0; 111 117 118 + tge = enetc_rd(hw, ENETC_PTGCR); 112 119 enetc_wr(hw, ENETC_PTGCR, tge | ENETC_PTGCR_TGE); 113 120 114 121 err = enetc_send_cmd(priv->si, &cbd); ··· 127 132 return 0; 128 133 } 129 134 130 - int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data) 135 + static void enetc_reset_taprio(struct enetc_ndev_priv *priv) 131 136 { 132 - struct tc_taprio_qopt_offload *taprio = type_data; 137 + struct enetc_hw *hw = &priv->si->hw; 138 + u32 val; 139 + 140 + val = enetc_rd(hw, ENETC_PTGCR); 141 + enetc_wr(hw, ENETC_PTGCR, val & ~ENETC_PTGCR_TGE); 142 + enetc_reset_ptcmsdur(hw); 143 + 144 + priv->active_offloads &= ~ENETC_F_QBV; 145 + } 146 + 147 + static void enetc_taprio_destroy(struct net_device *ndev) 148 + { 133 149 struct enetc_ndev_priv *priv = netdev_priv(ndev); 134 - int err, i; 135 150 136 - /* TSD and Qbv are mutually exclusive in hardware */ 151 + enetc_reset_taprio(priv); 152 + enetc_reset_tc_mqprio(ndev); 153 + } 154 + 155 + static void enetc_taprio_stats(struct net_device *ndev, 156 + struct tc_taprio_qopt_stats *stats) 157 + { 158 + struct enetc_ndev_priv *priv = netdev_priv(ndev); 159 + u64 window_drops = 0; 160 + int i; 161 + 137 162 for (i = 0; i < priv->num_tx_rings; i++) 138 - if (priv->tx_ring[i]->tsd_enable) 139 - return -EBUSY; 163 + window_drops += priv->tx_ring[i]->stats.win_drop; 140 164 141 - err = enetc_setup_tc_mqprio(ndev, &taprio->mqprio); 165 + stats->window_drops = window_drops; 166 + } 167 + 168 + static void enetc_taprio_tc_stats(struct net_device *ndev, 169 + struct tc_taprio_qopt_tc_stats *tc_stats) 170 + { 171 + struct tc_taprio_qopt_stats *stats = &tc_stats->stats; 172 + struct enetc_ndev_priv *priv = netdev_priv(ndev); 173 + int tc = tc_stats->tc; 174 + u64 window_drops = 0; 175 + int i; 176 + 177 + for (i = 0; i < priv->num_tx_rings; i++) 178 + if (priv->tx_ring[i]->prio == tc) 179 + window_drops += priv->tx_ring[i]->stats.win_drop; 180 + 181 + stats->window_drops = window_drops; 182 + } 183 + 184 + static int enetc_taprio_replace(struct net_device *ndev, 185 + struct tc_taprio_qopt_offload *offload) 186 + { 187 + struct enetc_ndev_priv *priv = netdev_priv(ndev); 188 + int err; 189 + 190 + err = enetc_setup_tc_mqprio(ndev, &offload->mqprio); 142 191 if (err) 143 192 return err; 144 193 145 - err = enetc_setup_taprio(ndev, taprio); 146 - if (err) { 147 - taprio->mqprio.qopt.num_tc = 0; 148 - enetc_setup_tc_mqprio(ndev, &taprio->mqprio); 194 + err = enetc_setup_taprio(priv, offload); 195 + if (err) 196 + enetc_reset_tc_mqprio(ndev); 197 + 198 + return err; 199 + } 200 + 201 + int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data) 202 + { 203 + struct tc_taprio_qopt_offload *offload = type_data; 204 + int err = 0; 205 + 206 + switch (offload->cmd) { 207 + case TAPRIO_CMD_REPLACE: 208 + err = enetc_taprio_replace(ndev, offload); 209 + break; 210 + case TAPRIO_CMD_DESTROY: 211 + enetc_taprio_destroy(ndev); 212 + break; 213 + case TAPRIO_CMD_STATS: 214 + enetc_taprio_stats(ndev, &offload->stats); 215 + break; 216 + case TAPRIO_CMD_TC_STATS: 217 + enetc_taprio_tc_stats(ndev, &offload->tc_stats); 218 + break; 219 + default: 220 + err = -EOPNOTSUPP; 149 221 } 150 222 151 223 return err;
+11 -2
drivers/net/ethernet/intel/igc/igc_main.c
··· 6113 6113 size_t n; 6114 6114 int i; 6115 6115 6116 - adapter->qbv_enable = qopt->enable; 6116 + switch (qopt->cmd) { 6117 + case TAPRIO_CMD_REPLACE: 6118 + adapter->qbv_enable = true; 6119 + break; 6120 + case TAPRIO_CMD_DESTROY: 6121 + adapter->qbv_enable = false; 6122 + break; 6123 + default: 6124 + return -EOPNOTSUPP; 6125 + } 6117 6126 6118 - if (!qopt->enable) 6127 + if (!adapter->qbv_enable) 6119 6128 return igc_tsn_clear_schedule(adapter); 6120 6129 6121 6130 if (qopt->base_time < 0)
+8 -2
drivers/net/ethernet/microchip/lan966x/lan966x_tc.c
··· 21 21 static int lan966x_tc_setup_qdisc_taprio(struct lan966x_port *port, 22 22 struct tc_taprio_qopt_offload *taprio) 23 23 { 24 - return taprio->enable ? lan966x_taprio_add(port, taprio) : 25 - lan966x_taprio_del(port); 24 + switch (taprio->cmd) { 25 + case TAPRIO_CMD_REPLACE: 26 + return lan966x_taprio_add(port, taprio); 27 + case TAPRIO_CMD_DESTROY: 28 + return lan966x_taprio_del(port); 29 + default: 30 + return -EOPNOTSUPP; 31 + } 26 32 } 27 33 28 34 static int lan966x_tc_setup_qdisc_tbf(struct lan966x_port *port,
+5 -2
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
··· 966 966 return -EOPNOTSUPP; 967 967 } 968 968 969 - if (!qopt->enable) 969 + if (qopt->cmd == TAPRIO_CMD_DESTROY) 970 970 goto disable; 971 + else if (qopt->cmd != TAPRIO_CMD_REPLACE) 972 + return -EOPNOTSUPP; 973 + 971 974 if (qopt->num_entries >= dep) 972 975 return -EINVAL; 973 976 if (!qopt->cycle_time) ··· 991 988 992 989 mutex_lock(&priv->plat->est->lock); 993 990 priv->plat->est->gcl_size = size; 994 - priv->plat->est->enable = qopt->enable; 991 + priv->plat->est->enable = qopt->cmd == TAPRIO_CMD_REPLACE; 995 992 mutex_unlock(&priv->plat->est->lock); 996 993 997 994 for (i = 0; i < size; i++) {
+8 -3
drivers/net/ethernet/ti/am65-cpsw-qos.c
··· 450 450 451 451 am65_cpsw_est_update_state(ndev); 452 452 453 - if (!est_new->taprio.enable) { 453 + if (est_new->taprio.cmd == TAPRIO_CMD_DESTROY) { 454 454 am65_cpsw_stop_est(ndev); 455 455 return ret; 456 456 } ··· 476 476 am65_cpsw_est_set_sched_list(ndev, est_new); 477 477 am65_cpsw_port_est_assign_buf_num(ndev, est_new->buf); 478 478 479 - am65_cpsw_est_set(ndev, est_new->taprio.enable); 479 + am65_cpsw_est_set(ndev, est_new->taprio.cmd == TAPRIO_CMD_REPLACE); 480 480 481 481 if (tact == TACT_PROG) { 482 482 ret = am65_cpsw_timer_set(ndev, est_new); ··· 520 520 am65_cpsw_cp_taprio(taprio, &est_new->taprio); 521 521 ret = am65_cpsw_configure_taprio(ndev, est_new); 522 522 if (!ret) { 523 - if (taprio->enable) { 523 + if (taprio->cmd == TAPRIO_CMD_REPLACE) { 524 524 devm_kfree(&ndev->dev, port->qos.est_admin); 525 525 526 526 port->qos.est_admin = est_new; ··· 564 564 static int am65_cpsw_setup_taprio(struct net_device *ndev, void *type_data) 565 565 { 566 566 struct am65_cpsw_port *port = am65_ndev_to_port(ndev); 567 + struct tc_taprio_qopt_offload *taprio = type_data; 567 568 struct am65_cpsw_common *common = port->common; 569 + 570 + if (taprio->cmd != TAPRIO_CMD_REPLACE && 571 + taprio->cmd != TAPRIO_CMD_DESTROY) 572 + return -EOPNOTSUPP; 568 573 569 574 if (!IS_ENABLED(CONFIG_TI_AM65_CPSW_TAS)) 570 575 return -ENODEV;
+45 -9
include/net/pkt_sched.h
··· 185 185 bool broken_mqprio:1; 186 186 }; 187 187 188 + enum tc_taprio_qopt_cmd { 189 + TAPRIO_CMD_REPLACE, 190 + TAPRIO_CMD_DESTROY, 191 + TAPRIO_CMD_STATS, 192 + TAPRIO_CMD_TC_STATS, 193 + }; 194 + 195 + /** 196 + * struct tc_taprio_qopt_stats - IEEE 802.1Qbv statistics 197 + * @window_drops: Frames that were dropped because they were too large to be 198 + * transmitted in any of the allotted time windows (open gates) for their 199 + * traffic class. 200 + * @tx_overruns: Frames still being transmitted by the MAC after the 201 + * transmission gate associated with their traffic class has closed. 202 + * Equivalent to `12.29.1.1.2 TransmissionOverrun` from 802.1Q-2018. 203 + */ 204 + struct tc_taprio_qopt_stats { 205 + u64 window_drops; 206 + u64 tx_overruns; 207 + }; 208 + 209 + struct tc_taprio_qopt_tc_stats { 210 + int tc; 211 + struct tc_taprio_qopt_stats stats; 212 + }; 213 + 188 214 struct tc_taprio_sched_entry { 189 215 u8 command; /* TC_TAPRIO_CMD_* */ 190 216 ··· 220 194 }; 221 195 222 196 struct tc_taprio_qopt_offload { 223 - struct tc_mqprio_qopt_offload mqprio; 224 - struct netlink_ext_ack *extack; 225 - u8 enable; 226 - ktime_t base_time; 227 - u64 cycle_time; 228 - u64 cycle_time_extension; 229 - u32 max_sdu[TC_MAX_QUEUE]; 197 + enum tc_taprio_qopt_cmd cmd; 230 198 231 - size_t num_entries; 232 - struct tc_taprio_sched_entry entries[]; 199 + union { 200 + /* TAPRIO_CMD_STATS */ 201 + struct tc_taprio_qopt_stats stats; 202 + /* TAPRIO_CMD_TC_STATS */ 203 + struct tc_taprio_qopt_tc_stats tc_stats; 204 + /* TAPRIO_CMD_REPLACE */ 205 + struct { 206 + struct tc_mqprio_qopt_offload mqprio; 207 + struct netlink_ext_ack *extack; 208 + ktime_t base_time; 209 + u64 cycle_time; 210 + u64 cycle_time_extension; 211 + u32 max_sdu[TC_MAX_QUEUE]; 212 + 213 + size_t num_entries; 214 + struct tc_taprio_sched_entry entries[]; 215 + }; 216 + }; 233 217 }; 234 218 235 219 #if IS_ENABLED(CONFIG_NET_SCH_TAPRIO)
+10
include/uapi/linux/pkt_sched.h
··· 1260 1260 }; 1261 1261 1262 1262 enum { 1263 + TCA_TAPRIO_OFFLOAD_STATS_PAD = 1, /* u64 */ 1264 + TCA_TAPRIO_OFFLOAD_STATS_WINDOW_DROPS, /* u64 */ 1265 + TCA_TAPRIO_OFFLOAD_STATS_TX_OVERRUNS, /* u64 */ 1266 + 1267 + /* add new constants above here */ 1268 + __TCA_TAPRIO_OFFLOAD_STATS_CNT, 1269 + TCA_TAPRIO_OFFLOAD_STATS_MAX = (__TCA_TAPRIO_OFFLOAD_STATS_CNT - 1) 1270 + }; 1271 + 1272 + enum { 1263 1273 TCA_TAPRIO_ATTR_UNSPEC, 1264 1274 TCA_TAPRIO_ATTR_PRIOMAP, /* struct tc_mqprio_qopt */ 1265 1275 TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST, /* nested of entry */
+82 -6
net/sched/sch_taprio.c
··· 27 27 #include <net/sock.h> 28 28 #include <net/tcp.h> 29 29 30 + #define TAPRIO_STAT_NOT_SET (~0ULL) 31 + 30 32 #include "sch_mqprio_lib.h" 31 33 32 34 static LIST_HEAD(taprio_list); ··· 1526 1524 "Not enough memory for enabling offload mode"); 1527 1525 return -ENOMEM; 1528 1526 } 1529 - offload->enable = 1; 1527 + offload->cmd = TAPRIO_CMD_REPLACE; 1530 1528 offload->extack = extack; 1531 1529 mqprio_qopt_reconstruct(dev, &offload->mqprio.qopt); 1532 1530 offload->mqprio.extack = extack; ··· 1574 1572 "Not enough memory to disable offload mode"); 1575 1573 return -ENOMEM; 1576 1574 } 1577 - offload->enable = 0; 1575 + offload->cmd = TAPRIO_CMD_DESTROY; 1578 1576 1579 1577 err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TAPRIO, offload); 1580 1578 if (err < 0) { ··· 2291 2289 return -EMSGSIZE; 2292 2290 } 2293 2291 2292 + static int taprio_put_stat(struct sk_buff *skb, u64 val, u16 attrtype) 2293 + { 2294 + if (val == TAPRIO_STAT_NOT_SET) 2295 + return 0; 2296 + if (nla_put_u64_64bit(skb, attrtype, val, TCA_TAPRIO_OFFLOAD_STATS_PAD)) 2297 + return -EMSGSIZE; 2298 + return 0; 2299 + } 2300 + 2301 + static int taprio_dump_xstats(struct Qdisc *sch, struct gnet_dump *d, 2302 + struct tc_taprio_qopt_offload *offload, 2303 + struct tc_taprio_qopt_stats *stats) 2304 + { 2305 + struct net_device *dev = qdisc_dev(sch); 2306 + const struct net_device_ops *ops; 2307 + struct sk_buff *skb = d->skb; 2308 + struct nlattr *xstats; 2309 + int err; 2310 + 2311 + ops = qdisc_dev(sch)->netdev_ops; 2312 + 2313 + /* FIXME I could use qdisc_offload_dump_helper(), but that messes 2314 + * with sch->flags depending on whether the device reports taprio 2315 + * stats, and I'm not sure whether that's a good idea, considering 2316 + * that stats are optional to the offload itself 2317 + */ 2318 + if (!ops->ndo_setup_tc) 2319 + return 0; 2320 + 2321 + memset(stats, 0xff, sizeof(*stats)); 2322 + 2323 + err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TAPRIO, offload); 2324 + if (err == -EOPNOTSUPP) 2325 + return 0; 2326 + if (err) 2327 + return err; 2328 + 2329 + xstats = nla_nest_start(skb, TCA_STATS_APP); 2330 + if (!xstats) 2331 + goto err; 2332 + 2333 + if (taprio_put_stat(skb, stats->window_drops, 2334 + TCA_TAPRIO_OFFLOAD_STATS_WINDOW_DROPS) || 2335 + taprio_put_stat(skb, stats->tx_overruns, 2336 + TCA_TAPRIO_OFFLOAD_STATS_TX_OVERRUNS)) 2337 + goto err_cancel; 2338 + 2339 + nla_nest_end(skb, xstats); 2340 + 2341 + return 0; 2342 + 2343 + err_cancel: 2344 + nla_nest_cancel(skb, xstats); 2345 + err: 2346 + return -EMSGSIZE; 2347 + } 2348 + 2349 + static int taprio_dump_stats(struct Qdisc *sch, struct gnet_dump *d) 2350 + { 2351 + struct tc_taprio_qopt_offload offload = { 2352 + .cmd = TAPRIO_CMD_STATS, 2353 + }; 2354 + 2355 + return taprio_dump_xstats(sch, d, &offload, &offload.stats); 2356 + } 2357 + 2294 2358 static int taprio_dump(struct Qdisc *sch, struct sk_buff *skb) 2295 2359 { 2296 2360 struct taprio_sched *q = qdisc_priv(sch); ··· 2456 2388 __acquires(d->lock) 2457 2389 { 2458 2390 struct netdev_queue *dev_queue = taprio_queue_get(sch, cl); 2391 + struct Qdisc *child = dev_queue->qdisc_sleeping; 2392 + struct tc_taprio_qopt_offload offload = { 2393 + .cmd = TAPRIO_CMD_TC_STATS, 2394 + .tc_stats = { 2395 + .tc = cl - 1, 2396 + }, 2397 + }; 2459 2398 2460 - sch = dev_queue->qdisc_sleeping; 2461 - if (gnet_stats_copy_basic(d, NULL, &sch->bstats, true) < 0 || 2462 - qdisc_qstats_copy(d, sch) < 0) 2399 + if (gnet_stats_copy_basic(d, NULL, &child->bstats, true) < 0 || 2400 + qdisc_qstats_copy(d, child) < 0) 2463 2401 return -1; 2464 - return 0; 2402 + 2403 + return taprio_dump_xstats(sch, d, &offload, &offload.tc_stats.stats); 2465 2404 } 2466 2405 2467 2406 static void taprio_walk(struct Qdisc *sch, struct qdisc_walker *arg) ··· 2515 2440 .dequeue = taprio_dequeue, 2516 2441 .enqueue = taprio_enqueue, 2517 2442 .dump = taprio_dump, 2443 + .dump_stats = taprio_dump_stats, 2518 2444 .owner = THIS_MODULE, 2519 2445 }; 2520 2446