--- a/include/uapi/linux/pkt_sched.h
+++ b/include/uapi/linux/pkt_sched.h
-@@ -934,4 +934,75 @@ enum {
+@@ -934,4 +934,110 @@ enum {
#define TCA_CBS_MAX (__TCA_CBS_MAX - 1)
+ TCA_CAKE_MPU,
+ TCA_CAKE_INGRESS,
+ TCA_CAKE_ACK_FILTER,
++ TCA_CAKE_SPLIT_GSO,
+ __TCA_CAKE_MAX
+};
+#define TCA_CAKE_MAX (__TCA_CAKE_MAX - 1)
+
-+struct tc_cake_traffic_stats {
-+ __u32 packets;
-+ __u32 link_ms;
-+ __u64 bytes;
++enum {
++ __TCA_CAKE_STATS_INVALID,
++ TCA_CAKE_STATS_CAPACITY_ESTIMATE,
++ TCA_CAKE_STATS_MEMORY_LIMIT,
++ TCA_CAKE_STATS_MEMORY_USED,
++ TCA_CAKE_STATS_AVG_NETOFF,
++ TCA_CAKE_STATS_MIN_NETLEN,
++ TCA_CAKE_STATS_MAX_NETLEN,
++ TCA_CAKE_STATS_MIN_ADJLEN,
++ TCA_CAKE_STATS_MAX_ADJLEN,
++ TCA_CAKE_STATS_TIN_STATS,
++ __TCA_CAKE_STATS_MAX
+};
++#define TCA_CAKE_STATS_MAX (__TCA_CAKE_STATS_MAX - 1)
+
++enum {
++ __TCA_CAKE_TIN_STATS_INVALID,
++ TCA_CAKE_TIN_STATS_PAD,
++ TCA_CAKE_TIN_STATS_SENT_PACKETS,
++ TCA_CAKE_TIN_STATS_SENT_BYTES64,
++ TCA_CAKE_TIN_STATS_DROPPED_PACKETS,
++ TCA_CAKE_TIN_STATS_DROPPED_BYTES64,
++ TCA_CAKE_TIN_STATS_ACKS_DROPPED_PACKETS,
++ TCA_CAKE_TIN_STATS_ACKS_DROPPED_BYTES64,
++ TCA_CAKE_TIN_STATS_ECN_MARKED_PACKETS,
++ TCA_CAKE_TIN_STATS_ECN_MARKED_BYTES64,
++ TCA_CAKE_TIN_STATS_BACKLOG_PACKETS,
++ TCA_CAKE_TIN_STATS_BACKLOG_BYTES64,
++ TCA_CAKE_TIN_STATS_THRESHOLD_RATE,
++ TCA_CAKE_TIN_STATS_TARGET_US,
++ TCA_CAKE_TIN_STATS_INTERVAL_US,
++ TCA_CAKE_TIN_STATS_WAY_INDIRECT_HITS,
++ TCA_CAKE_TIN_STATS_WAY_MISSES,
++ TCA_CAKE_TIN_STATS_WAY_COLLISIONS,
++ TCA_CAKE_TIN_STATS_PEAK_DELAY_US,
++ TCA_CAKE_TIN_STATS_AVG_DELAY_US,
++ TCA_CAKE_TIN_STATS_BASE_DELAY_US,
++ TCA_CAKE_TIN_STATS_SPARSE_FLOWS,
++ TCA_CAKE_TIN_STATS_BULK_FLOWS,
++ TCA_CAKE_TIN_STATS_UNRESPONSIVE_FLOWS,
++ TCA_CAKE_TIN_STATS_MAX_SKBLEN,
++ TCA_CAKE_TIN_STATS_FLOW_QUANTUM,
++ __TCA_CAKE_TIN_STATS_MAX
++};
++#define TCA_CAKE_TIN_STATS_MAX (__TCA_CAKE_TIN_STATS_MAX - 1)
+#define TC_CAKE_MAX_TINS (8)
-+struct tc_cake_tin_stats {
-+
-+ __u32 threshold_rate;
-+ __u32 target_us;
-+ struct tc_cake_traffic_stats sent;
-+ struct tc_cake_traffic_stats dropped;
-+ struct tc_cake_traffic_stats ecn_marked;
-+ struct tc_cake_traffic_stats backlog;
-+ __u32 interval_us;
-+ __u32 way_indirect_hits;
-+ __u32 way_misses;
-+ __u32 way_collisions;
-+ __u32 peak_delay_us; /* ~= bulk flow delay */
-+ __u32 avge_delay_us;
-+ __u32 base_delay_us; /* ~= sparse flows delay */
-+ __u16 sparse_flows;
-+ __u16 bulk_flows;
-+ __u16 unresponse_flows;
-+ __u16 spare;
-+ __u32 max_skblen;
-+ struct tc_cake_traffic_stats ack_drops;
++
++enum {
++ CAKE_FLOW_NONE = 0,
++ CAKE_FLOW_SRC_IP,
++ CAKE_FLOW_DST_IP,
++ CAKE_FLOW_HOSTS, /* = CAKE_FLOW_SRC_IP | CAKE_FLOW_DST_IP */
++ CAKE_FLOW_FLOWS,
++ CAKE_FLOW_DUAL_SRC, /* = CAKE_FLOW_SRC_IP | CAKE_FLOW_FLOWS */
++ CAKE_FLOW_DUAL_DST, /* = CAKE_FLOW_DST_IP | CAKE_FLOW_FLOWS */
++ CAKE_FLOW_TRIPLE, /* = CAKE_FLOW_HOSTS | CAKE_FLOW_FLOWS */
++ CAKE_FLOW_MAX,
+};
+
-+struct tc_cake_xstats {
-+ __u16 version;
-+ __u16 tin_stats_size; /* == sizeof(struct tc_cake_tin_stats) */
-+ __u32 capacity_estimate;
-+ __u32 memory_limit;
-+ __u32 memory_used;
-+ __u8 tin_cnt;
-+ __u8 avg_trnoff;
-+ __u16 max_trnlen;
-+ __u16 max_adjlen;
-+ __u16 min_trnlen;
-+ __u16 min_adjlen;
-+
-+ __u16 spare1;
-+ __u32 spare2;
-+
-+ struct tc_cake_tin_stats tin_stats[0]; /* keep last */
++enum {
++ CAKE_DIFFSERV_DIFFSERV3 = 0,
++ CAKE_DIFFSERV_DIFFSERV4,
++ CAKE_DIFFSERV_DIFFSERV8,
++ CAKE_DIFFSERV_BESTEFFORT,
++ CAKE_DIFFSERV_PRECEDENCE,
++ CAKE_DIFFSERV_MAX
++};
++
++enum {
++ CAKE_ACK_NONE = 0,
++ CAKE_ACK_FILTER,
++ CAKE_ACK_AGGRESSIVE,
++ CAKE_ACK_MAX
+};
++
++enum {
++ CAKE_ATM_NONE = 0,
++ CAKE_ATM_ATM,
++ CAKE_ATM_PTM,
++ CAKE_ATM_MAX
++};
++
+
#endif
--- /dev/null
+++ b/man/man8/tc-cake.8
-@@ -0,0 +1,678 @@
+@@ -0,0 +1,632 @@
+.TH CAKE 8 "23 November 2017" "iproute2" "Linux"
+.SH NAME
-+CAKE \- COMMON Applications Kept Enhanced (CAKE)
++CAKE \- Common Applications Kept Enhanced (CAKE)
+.SH SYNOPSIS
+.B tc qdisc ... cake
+.br
+|
+.BR diffserv4
+|
-+.BR diffserv-llt
-+|
+.BR diffserv3*
+]
+.br
+ Enables legacy interpretation of TOS "Precedence" field. Use of this
+preset on the modern Internet is firmly discouraged.
+.PP
-+.B diffserv-llt
-+.br
-+ Provides a "Latency-Loss Tradeoff" implementation with five tins:
-+.br
-+ Low Loss (TOS1, TOS2), 100% threshold, increased Codel target.
-+.br
-+ Best Effort (general), 100% threshold, normal Codel target & interval.
-+.br
-+ Low Latency (TOS4, TOS5, VA, EF), 100% threshold, reduced Codel interval.
-+.br
-+ Bulk (CS1), 6.25% threshold, normal Codel target & interval.
-+.br
-+ Net Control (CS6, CS7), 6.25% threshold, increased Codel target & interval.
-+.PP
+.B diffserv4
+.br
+ Provides a general-purpose Diffserv implementation with four tins:
+.SH EXAMPLES
+# tc qdisc delete root dev eth0
+.br
-+# tc qdisc add root dev eth0 cake bandwidth 9500Kbit pppoe-ptm ether-vlan
++# tc qdisc add root dev eth0 cake bandwidth 100Mbit ethernet
+.br
+# tc -s qdisc show dev eth0
+.br
-+qdisc cake 8007: root refcnt 6 bandwidth 9500Kbit diffserv3 triple-isolate rtt 100.0ms ptm overhead 34 via-ethernet total_overhead 34 hard_header_len 14
-+ Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
++qdisc cake 1: dev eth0 root refcnt 2 bandwidth 100Mbit diffserv3 triple-isolate rtt 100.0ms noatm overhead 38 mpu 84
++ Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
+ backlog 0b 0p requeues 0
-+ memory used: 0b of 4Mb
-+ capacity estimate: 9500Kbit
-+.br
-+ Bulk Best Effort Voice
-+.br
-+ thresh 593744bit 9500Kbit 2375Kbit
-+.br
-+ target 30.6ms 5.0ms 7.6ms
-+.br
-+ interval 125.6ms 100.0ms 102.6ms
-+.br
-+ pk_delay 0us 0us 0us
-+.br
-+ av_delay 0us 0us 0us
-+.br
-+ sp_delay 0us 0us 0us
-+.br
-+ pkts 0 0 0
-+.br
-+ bytes 0 0 0
-+.br
-+ way_inds 0 0 0
-+.br
-+ way_miss 0 0 0
-+.br
-+ way_cols 0 0 0
-+.br
-+ drops 0 0 0
-+.br
-+ marks 0 0 0
-+.br
-+ ack_drop 0 0 0
-+.br
-+ sp_flows 0 0 0
-+.br
-+ bk_flows 0 0 0
-+.br
-+ un_flows 0 0 0
-+.br
-+ max_len 0 0 0
-+.br
++ memory used: 0b of 5000000b
++ capacity estimate: 100Mbit
++ min/max network layer size: 65535 / 0
++ min/max overhead-adjusted size: 65535 / 0
++ average network hdr offset: 0
++
++ Bulk Best Effort Voice
++ thresh 6250Kbit 100Mbit 25Mbit
++ target 5.0ms 5.0ms 5.0ms
++ interval 100.0ms 100.0ms 100.0ms
++ pk_delay 0us 0us 0us
++ av_delay 0us 0us 0us
++ sp_delay 0us 0us 0us
++ pkts 0 0 0
++ bytes 0 0 0
++ way_inds 0 0 0
++ way_miss 0 0 0
++ way_cols 0 0 0
++ drops 0 0 0
++ marks 0 0 0
++ ack_drop 0 0 0
++ sp_flows 0 0 0
++ bk_flows 0 0 0
++ un_flows 0 0 0
++ max_len 0 0 0
++ quantum 300 1514 762
+
+After some use:
+.br
+# tc -s qdisc show dev eth0
+
-+qdisc cake 8007: root refcnt 6 bandwidth 9500Kbit diffserv3 triple-isolate rtt 100.0ms ptm overhead 34 via-ethernet total_overhead 34 hard_header_len 14
-+ Sent 110769306 bytes 313857 pkt (dropped 18, overlimits 741791 requeues 0)
-+ backlog 0b 0p requeues 0
-+ memory used: 110488b of 4Mb
-+ capacity estimate: 9500Kbit
-+.br
-+ Bulk Best Effort Voice
-+.br
-+ thresh 593744bit 9500Kbit 2375Kbit
-+.br
-+ target 30.6ms 5.0ms 7.6ms
-+.br
-+ interval 125.6ms 100.0ms 102.6ms
-+.br
-+ pk_delay 16.0ms 545us 15us
-+.br
-+ av_delay 2.4ms 161us 3us
-+.br
-+ sp_delay 59us 1us 1us
-+.br
-+ pkts 32866 195815 85194
-+.br
-+ bytes 8132614 69517496 33122156
-+.br
-+ way_inds 0 29208 0
-+.br
-+ way_miss 7 173 17
-+.br
-+ way_cols 0 0 0
-+.br
-+ drops 10 7 1
-+.br
-+ marks 217 692 300
-+.br
-+ ack_drop 0 0 0
-+.br
-+ sp_flows 0 0 0
-+.br
-+ bk_flows 0 0 1
-+.br
-+ un_flows 0 0 0
-+.br
-+ max_len 3028 3012 3028
-+.br
++qdisc cake 1: root refcnt 2 bandwidth 100Mbit diffserv3 triple-isolate rtt 100.0ms noatm overhead 38 mpu 84
++ Sent 44709231 bytes 31931 pkt (dropped 45, overlimits 93782 requeues 0)
++ backlog 33308b 22p requeues 0
++ memory used: 292352b of 5000000b
++ capacity estimate: 100Mbit
++ min/max network layer size: 28 / 1500
++ min/max overhead-adjusted size: 84 / 1538
++ average network hdr offset: 14
++
++ Bulk Best Effort Voice
++ thresh 6250Kbit 100Mbit 25Mbit
++ target 5.0ms 5.0ms 5.0ms
++ interval 100.0ms 100.0ms 100.0ms
++ pk_delay 8.7ms 6.9ms 5.0ms
++ av_delay 4.9ms 5.3ms 3.8ms
++ sp_delay 727us 1.4ms 511us
++ pkts 2590 21271 8137
++ bytes 3081804 30302659 11426206
++ way_inds 0 46 0
++ way_miss 3 17 4
++ way_cols 0 0 0
++ drops 20 15 10
++ marks 0 0 0
++ ack_drop 0 0 0
++ sp_flows 2 4 1
++ bk_flows 1 2 1
++ un_flows 0 0 0
++ max_len 1514 1514 1514
++ quantum 300 1514 762
+
+.SH SEE ALSO
+.BR tc (8),
TCMODULES += q_hhf.o
--- /dev/null
+++ b/tc/q_cake.c
-@@ -0,0 +1,770 @@
+@@ -0,0 +1,749 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Common Applications Kept Enhanced -- CAKE
+ *
+ * Copyright (C) 2014-2018 Jonathan Morton <chromatix99@gmail.com>
+ * Copyright (C) 2017-2018 Toke Høiland-Jørgensen <toke@toke.dk>
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions
-+ * are met:
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions, and the following disclaimer,
-+ * without modification.
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in the
-+ * documentation and/or other materials provided with the distribution.
-+ * 3. The names of the authors may not be used to endorse or promote products
-+ * derived from this software without specific prior written permission.
-+ *
-+ * Alternatively, provided that this notice is retained in full, this
-+ * software may be distributed under the terms of the GNU General
-+ * Public License ("GPL") version 2, in which case the provisions of the
-+ * GPL apply INSTEAD OF those given above.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-+ * DAMAGE.
-+ *
+ */
+
+#include <stddef.h>
+#include "utils.h"
+#include "tc_util.h"
+
++struct cake_preset {
++ char *name;
++ unsigned int target;
++ unsigned int interval;
++};
++
++static struct cake_preset presets[] = {
++ {"datacentre", 5, 100},
++ {"lan", 50, 1000},
++ {"metro", 500, 10000},
++ {"regional", 1500, 30000},
++ {"internet", 5000, 100000},
++ {"oceanic", 15000, 300000},
++ {"satellite", 50000, 1000000},
++ {"interplanetary", 50000000, 1000000000},
++};
++
++
++static struct cake_preset *find_preset(char *argv)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(presets); i++)
++ if (!strcmp(argv, presets[i].name))
++ return &presets[i];
++ return NULL;
++}
++
+static void explain(void)
+{
+ fprintf(stderr,
+"Usage: ... cake [ bandwidth RATE | unlimited* | autorate_ingress ]\n"
+" [ rtt TIME | datacentre | lan | metro | regional |\n"
+" internet* | oceanic | satellite | interplanetary ]\n"
-+" [ besteffort | diffserv8 | diffserv4 | diffserv-llt |\n"
-+" diffserv3* ]\n"
++" [ besteffort | diffserv8 | diffserv4 | diffserv3* ]\n"
+" [ flowblind | srchost | dsthost | hosts | flows |\n"
+" dual-srchost | dual-dsthost | triple-isolate* ]\n"
+" [ nat | nonat* ]\n"
+ int ingress = -1;
+ int ack_filter = -1;
+ struct rtattr *tail;
++ struct cake_preset *preset, *preset_set = NULL;
+
+ while (argc > 0) {
+ if (strcmp(*argv, "bandwidth") == 0) {
+ target = interval / 20;
+ if(!target)
+ target = 1;
-+ } else if (strcmp(*argv, "datacentre") == 0) {
-+ interval = 100;
-+ target = 5;
-+ } else if (strcmp(*argv, "lan") == 0) {
-+ interval = 1000;
-+ target = 50;
-+ } else if (strcmp(*argv, "metro") == 0) {
-+ interval = 10000;
-+ target = 500;
-+ } else if (strcmp(*argv, "regional") == 0) {
-+ interval = 30000;
-+ target = 1500;
-+ } else if (strcmp(*argv, "internet") == 0) {
-+ interval = 100000;
-+ target = 5000;
-+ } else if (strcmp(*argv, "oceanic") == 0) {
-+ interval = 300000;
-+ target = 15000;
-+ } else if (strcmp(*argv, "satellite") == 0) {
-+ interval = 1000000;
-+ target = 50000;
-+ } else if (strcmp(*argv, "interplanetary") == 0) {
-+ interval = 1000000000;
-+ target = 50000000;
++ } else if ((preset = find_preset(*argv))) {
++ if (preset_set)
++ duparg(*argv, preset_set->name);
++ preset_set = preset;
++ target = preset->target;
++ interval = preset->interval;
+
+ } else if (strcmp(*argv, "besteffort") == 0) {
-+ diffserv = 1;
++ diffserv = CAKE_DIFFSERV_BESTEFFORT;
+ } else if (strcmp(*argv, "precedence") == 0) {
-+ diffserv = 2;
++ diffserv = CAKE_DIFFSERV_PRECEDENCE;
+ } else if (strcmp(*argv, "diffserv8") == 0) {
-+ diffserv = 3;
++ diffserv = CAKE_DIFFSERV_DIFFSERV8;
+ } else if (strcmp(*argv, "diffserv4") == 0) {
-+ diffserv = 4;
++ diffserv = CAKE_DIFFSERV_DIFFSERV4;
+ } else if (strcmp(*argv, "diffserv") == 0) {
-+ diffserv = 4;
-+ } else if (strcmp(*argv, "diffserv-llt") == 0) {
-+ diffserv = 5;
++ diffserv = CAKE_DIFFSERV_DIFFSERV4;
+ } else if (strcmp(*argv, "diffserv3") == 0) {
-+ diffserv = 6;
++ diffserv = CAKE_DIFFSERV_DIFFSERV3;
+
+ } else if (strcmp(*argv, "nowash") == 0) {
+ wash = 0;
+ wash = 1;
+
+ } else if (strcmp(*argv, "flowblind") == 0) {
-+ flowmode = 0;
++ flowmode = CAKE_FLOW_NONE;
+ } else if (strcmp(*argv, "srchost") == 0) {
-+ flowmode = 1;
++ flowmode = CAKE_FLOW_SRC_IP;
+ } else if (strcmp(*argv, "dsthost") == 0) {
-+ flowmode = 2;
++ flowmode = CAKE_FLOW_DST_IP;
+ } else if (strcmp(*argv, "hosts") == 0) {
-+ flowmode = 3;
++ flowmode = CAKE_FLOW_HOSTS;
+ } else if (strcmp(*argv, "flows") == 0) {
-+ flowmode = 4;
++ flowmode = CAKE_FLOW_FLOWS;
+ } else if (strcmp(*argv, "dual-srchost") == 0) {
-+ flowmode = 5;
++ flowmode = CAKE_FLOW_DUAL_SRC;
+ } else if (strcmp(*argv, "dual-dsthost") == 0) {
-+ flowmode = 6;
++ flowmode = CAKE_FLOW_DUAL_DST;
+ } else if (strcmp(*argv, "triple-isolate") == 0) {
-+ flowmode = 7;
++ flowmode = CAKE_FLOW_TRIPLE;
+
+ } else if (strcmp(*argv, "nat") == 0) {
+ nat = 1;
+ nat = 0;
+
+ } else if (strcmp(*argv, "ptm") == 0) {
-+ atm = 2;
++ atm = CAKE_ATM_PTM;
+ } else if (strcmp(*argv, "atm") == 0) {
-+ atm = 1;
++ atm = CAKE_ATM_ATM;
+ } else if (strcmp(*argv, "noatm") == 0) {
-+ atm = 0;
++ atm = CAKE_ATM_NONE;
+
+ } else if (strcmp(*argv, "raw") == 0) {
-+ atm = 0;
++ atm = CAKE_ATM_NONE;
+ overhead = 0;
+ overhead_set = true;
+ overhead_override = true;
+ * one whole ATM cell plus ATM framing.
+ * A safe choice if the actual overhead is unknown.
+ */
-+ atm = 1;
++ atm = CAKE_ATM_ATM;
+ overhead = 48;
+ overhead_set = true;
+
+ /* Various ADSL framing schemes, all over ATM cells */
+ } else if (strcmp(*argv, "ipoa-vcmux") == 0) {
-+ atm = 1;
++ atm = CAKE_ATM_ATM;
+ overhead += 8;
+ overhead_set = true;
+ } else if (strcmp(*argv, "ipoa-llcsnap") == 0) {
-+ atm = 1;
++ atm = CAKE_ATM_ATM;
+ overhead += 16;
+ overhead_set = true;
+ } else if (strcmp(*argv, "bridged-vcmux") == 0) {
-+ atm = 1;
++ atm = CAKE_ATM_ATM;
+ overhead += 24;
+ overhead_set = true;
+ } else if (strcmp(*argv, "bridged-llcsnap") == 0) {
-+ atm = 1;
++ atm = CAKE_ATM_ATM;
+ overhead += 32;
+ overhead_set = true;
+ } else if (strcmp(*argv, "pppoa-vcmux") == 0) {
-+ atm = 1;
++ atm = CAKE_ATM_ATM;
+ overhead += 10;
+ overhead_set = true;
+ } else if (strcmp(*argv, "pppoa-llc") == 0) {
-+ atm = 1;
++ atm = CAKE_ATM_ATM;
+ overhead += 14;
+ overhead_set = true;
+ } else if (strcmp(*argv, "pppoe-vcmux") == 0) {
-+ atm = 1;
++ atm = CAKE_ATM_ATM;
+ overhead += 32;
+ overhead_set = true;
+ } else if (strcmp(*argv, "pppoe-llcsnap") == 0) {
-+ atm = 1;
++ atm = CAKE_ATM_ATM;
+ overhead += 40;
+ overhead_set = true;
+
+ * + 1B Start of Frame (S) + 1B End of Frame (Ck)
+ * + 2B TC-CRC (PTM-FCS) = 30B
+ */
-+ atm = 2;
++ atm = CAKE_ATM_PTM;
+ overhead += 30;
+ overhead_set = true;
+ } else if (strcmp(*argv, "bridged-ptm") == 0) {
+ * + 1B Start of Frame (S) + 1B End of Frame (Ck)
+ * + 2B TC-CRC (PTM-FCS) = 22B
+ */
-+ atm = 2;
++ atm = CAKE_ATM_PTM;
+ overhead += 22;
+ overhead_set = true;
+
+ * but not interframe gap or preamble.
+ */
+ } else if (strcmp(*argv, "docsis") == 0) {
-+ atm = 0;
++ atm = CAKE_ATM_NONE;
+ overhead += 18;
+ overhead_set = true;
+ mpu = 64;
+ ingress = 0;
+
+ } else if (strcmp(*argv, "no-ack-filter") == 0) {
-+ ack_filter = 0;
++ ack_filter = CAKE_ACK_NONE;
+ } else if (strcmp(*argv, "ack-filter") == 0) {
-+ ack_filter = 0x0200;
++ ack_filter = CAKE_ACK_FILTER;
+ } else if (strcmp(*argv, "ack-filter-aggressive") == 0) {
-+ ack_filter = 0x0600;
++ ack_filter = CAKE_ACK_AGGRESSIVE;
+
+ } else if (strcmp(*argv, "memlimit") == 0) {
+ NEXT_ARG();
+ int wash = 0;
+ int ingress = 0;
+ int ack_filter = 0;
++ int split_gso = 0;
+ SPRINT_BUF(b1);
+ SPRINT_BUF(b2);
+
+ RTA_PAYLOAD(tb[TCA_CAKE_DIFFSERV_MODE]) >= sizeof(__u32)) {
+ diffserv = rta_getattr_u32(tb[TCA_CAKE_DIFFSERV_MODE]);
+ switch(diffserv) {
-+ case 1:
-+ print_string(PRINT_ANY, "diffserv", "%s ", "besteffort");
++ case CAKE_DIFFSERV_DIFFSERV3:
++ print_string(PRINT_ANY, "diffserv", "%s ", "diffserv3");
+ break;
-+ case 2:
-+ print_string(PRINT_ANY, "diffserv", "%s ", "precedence");
++ case CAKE_DIFFSERV_DIFFSERV4:
++ print_string(PRINT_ANY, "diffserv", "%s ", "diffserv4");
+ break;
-+ case 3:
++ case CAKE_DIFFSERV_DIFFSERV8:
+ print_string(PRINT_ANY, "diffserv", "%s ", "diffserv8");
+ break;
-+ case 4:
-+ print_string(PRINT_ANY, "diffserv", "%s ", "diffserv4");
-+ break;
-+ case 5:
-+ print_string(PRINT_ANY, "diffserv", "%s ", "diffserv-llt");
++ case CAKE_DIFFSERV_BESTEFFORT:
++ print_string(PRINT_ANY, "diffserv", "%s ", "besteffort");
+ break;
-+ case 6:
-+ print_string(PRINT_ANY, "diffserv", "%s ", "diffserv3");
++ case CAKE_DIFFSERV_PRECEDENCE:
++ print_string(PRINT_ANY, "diffserv", "%s ", "precedence");
+ break;
+ default:
+ print_string(PRINT_ANY, "diffserv", "(?diffserv?) ", "unknown");
+ if (tb[TCA_CAKE_FLOW_MODE] &&
+ RTA_PAYLOAD(tb[TCA_CAKE_FLOW_MODE]) >= sizeof(__u32)) {
+ flowmode = rta_getattr_u32(tb[TCA_CAKE_FLOW_MODE]);
-+ nat = !!(flowmode & 64);
-+ flowmode &= ~64;
+ switch(flowmode) {
-+ case 0:
++ case CAKE_FLOW_NONE:
+ print_string(PRINT_ANY, "flowmode", "%s ", "flowblind");
+ break;
-+ case 1:
++ case CAKE_FLOW_SRC_IP:
+ print_string(PRINT_ANY, "flowmode", "%s ", "srchost");
+ break;
-+ case 2:
++ case CAKE_FLOW_DST_IP:
+ print_string(PRINT_ANY, "flowmode", "%s ", "dsthost");
+ break;
-+ case 3:
++ case CAKE_FLOW_HOSTS:
+ print_string(PRINT_ANY, "flowmode", "%s ", "hosts");
+ break;
-+ case 4:
++ case CAKE_FLOW_FLOWS:
+ print_string(PRINT_ANY, "flowmode", "%s ", "flows");
+ break;
-+ case 5:
++ case CAKE_FLOW_DUAL_SRC:
+ print_string(PRINT_ANY, "flowmode", "%s ", "dual-srchost");
+ break;
-+ case 6:
++ case CAKE_FLOW_DUAL_DST:
+ print_string(PRINT_ANY, "flowmode", "%s ", "dual-dsthost");
+ break;
-+ case 7:
++ case CAKE_FLOW_TRIPLE:
+ print_string(PRINT_ANY, "flowmode", "%s ", "triple-isolate");
+ break;
+ default:
+ break;
+ };
+
-+ if(nat)
-+ print_string(PRINT_FP, NULL, "nat ", NULL);
-+ print_bool(PRINT_JSON, "nat", NULL, nat);
+ }
++
++ if (tb[TCA_CAKE_NAT] &&
++ RTA_PAYLOAD(tb[TCA_CAKE_NAT]) >= sizeof(__u32)) {
++ nat = rta_getattr_u32(tb[TCA_CAKE_NAT]);
++ }
++
++ if(nat)
++ print_string(PRINT_FP, NULL, "nat ", NULL);
++ print_bool(PRINT_JSON, "nat", NULL, nat);
++
+ if (tb[TCA_CAKE_WASH] &&
+ RTA_PAYLOAD(tb[TCA_CAKE_WASH]) >= sizeof(__u32)) {
+ wash = rta_getattr_u32(tb[TCA_CAKE_WASH]);
+ atm = rta_getattr_u32(tb[TCA_CAKE_ATM]);
+ }
+ if (tb[TCA_CAKE_OVERHEAD] &&
-+ RTA_PAYLOAD(tb[TCA_CAKE_OVERHEAD]) >= sizeof(__u32)) {
-+ overhead = rta_getattr_u32(tb[TCA_CAKE_OVERHEAD]);
++ RTA_PAYLOAD(tb[TCA_CAKE_OVERHEAD]) >= sizeof(__s32)) {
++ overhead = *(__s32 *) RTA_DATA(tb[TCA_CAKE_OVERHEAD]);
+ }
+ if (tb[TCA_CAKE_MPU] &&
+ RTA_PAYLOAD(tb[TCA_CAKE_MPU]) >= sizeof(__u32)) {
+ RTA_PAYLOAD(tb[TCA_CAKE_ACK_FILTER]) >= sizeof(__u32)) {
+ ack_filter = rta_getattr_u32(tb[TCA_CAKE_ACK_FILTER]);
+ }
++ if (tb[TCA_CAKE_SPLIT_GSO] &&
++ RTA_PAYLOAD(tb[TCA_CAKE_SPLIT_GSO]) >= sizeof(__u32)) {
++ split_gso = rta_getattr_u32(tb[TCA_CAKE_SPLIT_GSO]);
++ }
+ if (tb[TCA_CAKE_RAW]) {
+ raw = 1;
+ }
+ print_string(PRINT_FP, NULL, "ingress ", NULL);
+ print_bool(PRINT_JSON, "ingress", NULL, ingress);
+
-+ if (ack_filter == 0x0600)
++ if (ack_filter == CAKE_ACK_AGGRESSIVE)
+ print_string(PRINT_ANY, "ack-filter", "ack-filter-%s ", "aggressive");
-+ else if (ack_filter)
++ else if (ack_filter == CAKE_ACK_FILTER)
+ print_string(PRINT_ANY, "ack-filter", "ack-filter ", "enabled");
+ else
+ print_string(PRINT_JSON, "ack-filter", NULL, "disabled");
+
++ if (split_gso)
++ print_string(PRINT_FP, NULL, "split-gso ", NULL);
++ print_bool(PRINT_JSON, "split_gso", NULL, split_gso);
++
+ if (interval)
+ print_string(PRINT_FP, NULL, "rtt %s ", sprint_time(interval, b2));
+ print_uint(PRINT_JSON, "rtt", NULL, interval);
+ print_string(PRINT_FP, NULL, "raw ", NULL);
+ print_bool(PRINT_JSON, "raw", NULL, raw);
+
-+ if (atm == 1)
++ if (atm == CAKE_ATM_ATM)
+ print_string(PRINT_ANY, "atm", "%s ", "atm");
-+ else if (atm == 2)
++ else if (atm == CAKE_ATM_PTM)
+ print_string(PRINT_ANY, "atm", "%s ", "ptm");
+ else if (!raw)
+ print_string(PRINT_ANY, "atm", "%s ", "noatm");
+ print_uint(PRINT_ANY, "overhead", "overhead %d ", overhead);
+
+ if (mpu)
-+ print_uint(PRINT_ANY, "mpu", "mpu %d ", mpu);
++ print_uint(PRINT_ANY, "mpu", "mpu %u ", mpu);
+
+ if (memlimit) {
+ print_uint(PRINT_JSON, "memlimit", NULL, memlimit);
+ return 0;
+}
+
-+#define FOR_EACH_TIN(xstats, tst, i) \
-+ for(tst = xstats->tin_stats, i = 0; \
-+ i < xstats->tin_cnt; \
-+ i++, tst = ((void *) xstats->tin_stats) + xstats->tin_stats_size * i)
-+
-+static void cake_print_json_tin(struct tc_cake_tin_stats *tst)
++static void cake_print_json_tin(struct rtattr **tstat)
+{
++#define PRINT_TSTAT_JSON(type, name, attr) if (tstat[TCA_CAKE_TIN_STATS_ ## attr]) \
++ print_uint(PRINT_JSON, name, NULL, \
++ rta_getattr_ ## type((struct rtattr *)tstat[TCA_CAKE_TIN_STATS_ ## attr]))
++
+ open_json_object(NULL);
-+ print_uint(PRINT_JSON, "threshold_rate", NULL, tst->threshold_rate);
-+ print_uint(PRINT_JSON, "target", NULL, tst->target_us);
-+ print_uint(PRINT_JSON, "interval", NULL, tst->interval_us);
-+ print_uint(PRINT_JSON, "peak_delay", NULL, tst->peak_delay_us);
-+ print_uint(PRINT_JSON, "average_delay", NULL, tst->avge_delay_us);
-+ print_uint(PRINT_JSON, "base_delay", NULL, tst->base_delay_us);
-+ print_uint(PRINT_JSON, "sent_packets", NULL, tst->sent.packets);
-+ print_uint(PRINT_JSON, "sent_bytes", NULL, tst->sent.bytes);
-+ print_uint(PRINT_JSON, "way_indirect_hits", NULL, tst->way_indirect_hits);
-+ print_uint(PRINT_JSON, "way_misses", NULL, tst->way_misses);
-+ print_uint(PRINT_JSON, "way_collisions", NULL, tst->way_collisions);
-+ print_uint(PRINT_JSON, "drops", NULL, tst->dropped.packets);
-+ print_uint(PRINT_JSON, "ecn_mark", NULL, tst->ecn_marked.packets);
-+ print_uint(PRINT_JSON, "ack_drops", NULL, tst->ack_drops.packets);
-+ print_uint(PRINT_JSON, "sparse_flows", NULL, tst->sparse_flows);
-+ print_uint(PRINT_JSON, "bulk_flows", NULL, tst->bulk_flows);
-+ print_uint(PRINT_JSON, "unresponsive_flows", NULL, tst->unresponse_flows);
-+ print_uint(PRINT_JSON, "max_pkt_len", NULL, tst->max_skblen);
++ PRINT_TSTAT_JSON(u32, "threshold_rate", THRESHOLD_RATE);
++ PRINT_TSTAT_JSON(u32, "target_us", TARGET_US);
++ PRINT_TSTAT_JSON(u32, "interval_us", INTERVAL_US);
++ PRINT_TSTAT_JSON(u32, "peak_delay_us", PEAK_DELAY_US);
++ PRINT_TSTAT_JSON(u32, "avg_delay_us", AVG_DELAY_US);
++ PRINT_TSTAT_JSON(u32, "base_delay_us", BASE_DELAY_US);
++ PRINT_TSTAT_JSON(u32, "sent_packets", SENT_PACKETS);
++ PRINT_TSTAT_JSON(u64, "sent_bytes", SENT_BYTES64);
++ PRINT_TSTAT_JSON(u32, "way_indirect_hits", WAY_INDIRECT_HITS);
++ PRINT_TSTAT_JSON(u32, "way_misses", WAY_MISSES);
++ PRINT_TSTAT_JSON(u32, "way_collisions", WAY_COLLISIONS);
++ PRINT_TSTAT_JSON(u32, "drops", DROPPED_PACKETS);
++ PRINT_TSTAT_JSON(u32, "ecn_mark", ECN_MARKED_PACKETS);
++ PRINT_TSTAT_JSON(u32, "ack_drops", ACKS_DROPPED_PACKETS);
++ PRINT_TSTAT_JSON(u32, "sparse_flows", SPARSE_FLOWS);
++ PRINT_TSTAT_JSON(u32, "bulk_flows", BULK_FLOWS);
++ PRINT_TSTAT_JSON(u32, "unresponsive_flows", UNRESPONSIVE_FLOWS);
++ PRINT_TSTAT_JSON(u32, "max_pkt_len", MAX_SKBLEN);
++ PRINT_TSTAT_JSON(u32, "flow_quantum", FLOW_QUANTUM);
+ close_json_object();
++
++#undef PRINT_TSTAT_JSON
+}
+
+static int cake_print_xstats(struct qdisc_util *qu, FILE *f,
+ struct rtattr *xstats)
+{
-+ struct tc_cake_xstats *stnc;
-+ struct tc_cake_tin_stats *tst;
+ SPRINT_BUF(b1);
++ struct rtattr *st[TCA_CAKE_STATS_MAX + 1];
+ int i;
+
+ if (xstats == NULL)
+ return 0;
+
-+ if (RTA_PAYLOAD(xstats) < sizeof(*stnc))
-+ return -1;
-+
-+ stnc = RTA_DATA(xstats);
-+
-+ if (stnc->version < 0x101 ||
-+ RTA_PAYLOAD(xstats) < (sizeof(struct tc_cake_xstats) +
-+ stnc->tin_stats_size * stnc->tin_cnt))
-+ return -1;
-+
-+ print_uint(PRINT_JSON, "memory_used", NULL, stnc->memory_used);
-+ print_uint(PRINT_JSON, "memory_limit", NULL, stnc->memory_limit);
-+ print_uint(PRINT_JSON, "capacity_estimate", NULL, stnc->capacity_estimate);
-+
-+ print_string(PRINT_FP, NULL, " memory used: %s",
-+ sprint_size(stnc->memory_used, b1));
-+ print_string(PRINT_FP, NULL, " of %s\n",
-+ sprint_size(stnc->memory_limit, b1));
-+ print_string(PRINT_FP, NULL, " capacity estimate: %s\n",
-+ sprint_rate(stnc->capacity_estimate, b1));
-+
-+ print_uint(PRINT_ANY, "min_transport_size", " min/max transport layer size: %10u",
-+ stnc->min_trnlen);
-+ print_uint(PRINT_ANY, "max_transport_size", " /%8u\n", stnc->max_trnlen);
-+ print_uint(PRINT_ANY, "min_adj_size", " min/max overhead-adjusted size: %8u",
-+ stnc->min_adjlen);
-+ print_uint(PRINT_ANY, "max_adj_size", " /%8u\n", stnc->max_adjlen);
-+ print_uint(PRINT_ANY, "avg_hdr_offset", " average transport hdr offset: %10u\n\n",
-+ stnc->avg_trnoff);
-+
-+ if (is_json_context()) {
-+ open_json_array(PRINT_JSON, "tins");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ cake_print_json_tin(tst);
-+ close_json_array(PRINT_JSON, NULL);
-+ return 0;
++#define GET_STAT_U32(attr) rta_getattr_u32(st[TCA_CAKE_STATS_ ## attr])
++
++ parse_rtattr_nested(st, TCA_CAKE_STATS_MAX, xstats);
++
++ if (st[TCA_CAKE_STATS_MEMORY_USED] &&
++ st[TCA_CAKE_STATS_MEMORY_LIMIT]) {
++ print_string(PRINT_FP, NULL, " memory used: %s",
++ sprint_size(GET_STAT_U32(MEMORY_USED), b1));
++
++ print_string(PRINT_FP, NULL, " of %s\n",
++ sprint_size(GET_STAT_U32(MEMORY_LIMIT), b1));
++
++ print_uint(PRINT_JSON, "memory_used", NULL,
++ GET_STAT_U32(MEMORY_USED));
++ print_uint(PRINT_JSON, "memory_limit", NULL,
++ GET_STAT_U32(MEMORY_LIMIT));
++ }
++
++ if (st[TCA_CAKE_STATS_CAPACITY_ESTIMATE]) {
++ print_string(PRINT_FP, NULL, " capacity estimate: %s\n",
++ sprint_rate(GET_STAT_U32(CAPACITY_ESTIMATE), b1));
++ print_uint(PRINT_JSON, "capacity_estimate", NULL,
++ GET_STAT_U32(CAPACITY_ESTIMATE));
+ }
+
++ if (st[TCA_CAKE_STATS_MIN_NETLEN] &&
++ st[TCA_CAKE_STATS_MAX_NETLEN]) {
++ print_uint(PRINT_ANY, "min_network_size",
++ " min/max network layer size: %8u",
++ GET_STAT_U32(MIN_NETLEN));
++ print_uint(PRINT_ANY, "max_network_size",
++ " /%8u\n", GET_STAT_U32(MAX_NETLEN));
++ }
++
++ if (st[TCA_CAKE_STATS_MIN_ADJLEN] &&
++ st[TCA_CAKE_STATS_MAX_ADJLEN]) {
++ print_uint(PRINT_ANY, "min_adj_size",
++ " min/max overhead-adjusted size: %8u",
++ GET_STAT_U32(MIN_ADJLEN));
++ print_uint(PRINT_ANY, "max_adj_size",
++ " /%8u\n", GET_STAT_U32(MAX_ADJLEN));
++ }
++
++ if (st[TCA_CAKE_STATS_AVG_NETOFF])
++ print_uint(PRINT_ANY, "avg_hdr_offset",
++ " average network hdr offset: %8u\n\n",
++ GET_STAT_U32(AVG_NETOFF));
++
++#undef GET_STAT_U32
++
++ if (st[TCA_CAKE_STATS_TIN_STATS]) {
++ struct rtattr *tins[TC_CAKE_MAX_TINS + 1];
++ struct rtattr *tstat[TC_CAKE_MAX_TINS][TCA_CAKE_TIN_STATS_MAX + 1];
++ int num_tins = 0;
++
++ parse_rtattr_nested(tins, TC_CAKE_MAX_TINS, st[TCA_CAKE_STATS_TIN_STATS]);
++
++ for (i = 1; i <= TC_CAKE_MAX_TINS && tins[i]; i++) {
++ parse_rtattr_nested(tstat[i-1], TCA_CAKE_TIN_STATS_MAX, tins[i]);
++ num_tins++;
++ }
++
++ if (!num_tins)
++ return 0;
++
++ if (is_json_context()) {
++ open_json_array(PRINT_JSON, "tins");
++ for (i = 0; i < num_tins; i++)
++ cake_print_json_tin(tstat[i]);
++ close_json_array(PRINT_JSON, NULL);
++
++ return 0;
++ }
++
++
++ switch(num_tins) {
++ case 3:
++ fprintf(f, " Bulk Best Effort Voice\n");
++ break;
++
++ case 4:
++ fprintf(f, " Bulk Best Effort Video Voice\n");
++ break;
+
-+ switch(stnc->tin_cnt) {
-+ case 3:
-+ fprintf(f, " Bulk Best Effort Voice\n");
-+ break;
-+
-+ case 4:
-+ fprintf(f, " Bulk Best Effort Video Voice\n");
-+ break;
-+
-+ case 5:
-+ fprintf(f, " Low Loss Best Effort Low Delay Bulk Net Control\n");
-+ break;
-+
-+ default:
-+ fprintf(f, " ");
-+ for(i=0; i < stnc->tin_cnt; i++)
-+ fprintf(f, " Tin %u", i);
-+ fprintf(f, "\n");
-+ };
-+
-+ fprintf(f, " thresh ");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12s", sprint_rate(tst->threshold_rate, b1));
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " target ");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12s", sprint_time(tst->target_us, b1));
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " interval");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12s", sprint_time(tst->interval_us, b1));
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " pk_delay");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12s", sprint_time(tst->peak_delay_us, b1));
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " av_delay");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12s", sprint_time(tst->avge_delay_us, b1));
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " sp_delay");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12s", sprint_time(tst->base_delay_us, b1));
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " pkts ");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12u", tst->sent.packets);
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " bytes ");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12llu", tst->sent.bytes);
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " way_inds");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12u", tst->way_indirect_hits);
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " way_miss");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12u", tst->way_misses);
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " way_cols");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12u", tst->way_collisions);
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " drops ");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12u", tst->dropped.packets);
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " marks ");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12u", tst->ecn_marked.packets);
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " ack_drop");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12u", tst->ack_drops.packets);
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " sp_flows");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12u", tst->sparse_flows);
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " bk_flows");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12u", tst->bulk_flows);
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " un_flows");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12u", tst->unresponse_flows);
-+ fprintf(f, "\n");
-+
-+ fprintf(f, " max_len ");
-+ FOR_EACH_TIN(stnc, tst, i)
-+ fprintf(f, " %12u", tst->max_skblen);
-+ fprintf(f, "\n");
++ default:
++ fprintf(f, " ");
++ for(i=0; i < num_tins; i++)
++ fprintf(f, " Tin %u", i);
++ fprintf(f, "\n");
++ };
+
++#define GET_TSTAT(i, attr) (tstat[i][TCA_CAKE_TIN_STATS_ ## attr])
++#define PRINT_TSTAT(name, attr, fmts, val) do { \
++ if (GET_TSTAT(0, attr)) { \
++ fprintf(f, name); \
++ for (i = 0; i < num_tins; i++) \
++ fprintf(f, " %12" fmts, val); \
++ fprintf(f, "\n"); \
++ } \
++ } while (0)
++
++#define SPRINT_TSTAT(pfunc, name, attr) PRINT_TSTAT( \
++ name, attr, "s", sprint_ ## pfunc( \
++ rta_getattr_u32(GET_TSTAT(i, attr)), b1))
++
++#define PRINT_TSTAT_U32(name, attr) PRINT_TSTAT( \
++ name, attr, "u", rta_getattr_u32(GET_TSTAT(i, attr)))
++
++#define PRINT_TSTAT_U64(name, attr) PRINT_TSTAT( \
++ name, attr, "llu", rta_getattr_u64(GET_TSTAT(i, attr)))
++
++ SPRINT_TSTAT(rate, " thresh ", THRESHOLD_RATE);
++ SPRINT_TSTAT(time, " target ", TARGET_US);
++ SPRINT_TSTAT(time, " interval", INTERVAL_US);
++ SPRINT_TSTAT(time, " pk_delay", PEAK_DELAY_US);
++ SPRINT_TSTAT(time, " av_delay", AVG_DELAY_US);
++ SPRINT_TSTAT(time, " sp_delay", BASE_DELAY_US);
++
++ PRINT_TSTAT_U32(" pkts ", SENT_PACKETS);
++ PRINT_TSTAT_U64(" bytes ", SENT_BYTES64);
++
++ PRINT_TSTAT_U32(" way_inds", WAY_INDIRECT_HITS);
++ PRINT_TSTAT_U32(" way_miss", WAY_MISSES);
++ PRINT_TSTAT_U32(" way_cols", WAY_COLLISIONS);
++ PRINT_TSTAT_U32(" drops ", DROPPED_PACKETS);
++ PRINT_TSTAT_U32(" marks ", ECN_MARKED_PACKETS);
++ PRINT_TSTAT_U32(" ack_drop", ACKS_DROPPED_PACKETS);
++ PRINT_TSTAT_U32(" sp_flows", SPARSE_FLOWS);
++ PRINT_TSTAT_U32(" bk_flows", BULK_FLOWS);
++ PRINT_TSTAT_U32(" un_flows", UNRESPONSIVE_FLOWS);
++ PRINT_TSTAT_U32(" max_len ", MAX_SKBLEN);
++ PRINT_TSTAT_U32(" quantum ", FLOW_QUANTUM);
++
++#undef GET_STAT
++#undef PRINT_TSTAT
++#undef SPRINT_TSTAT
++#undef PRINT_TSTAT_U32
++#undef PRINT_TSTAT_U64
++ }
+ return 0;
+}
+
+ .print_qopt = cake_print_opt,
+ .print_xstats = cake_print_xstats,
+};
+--- a/tc/q_ingress.c
++++ b/tc/q_ingress.c
+@@ -40,7 +40,7 @@ static int ingress_parse_opt(struct qdis
+ static int ingress_print_opt(struct qdisc_util *qu, FILE *f,
+ struct rtattr *opt)
+ {
+- fprintf(f, "---------------- ");
++ print_string(PRINT_FP, NULL, "---------------- ", NULL);
+ return 0;
+ }
+