summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>2018-05-06 18:12:48 +0000
committerJohn Crispin <john@phrozen.org>2018-05-07 08:09:12 +0200
commit080fb7a3fbb63e78bb5ae75012f352c5e2a5473d (patch)
treec442096787b711a08131d63fc3decbe3dd9808f9
parentad5af37ca793b8c065d40a10054fd24b88705180 (diff)
downloadopenwrt-080fb7a3fbb63e78bb5ae75012f352c5e2a5473d.tar.gz
openwrt-080fb7a3fbb63e78bb5ae75012f352c5e2a5473d.tar.bz2
openwrt-080fb7a3fbb63e78bb5ae75012f352c5e2a5473d.zip
iproute2: import latest cake
The sender domain has a DMARC Reject/Quarantine policy which disallows sending mailing list messages using the original "From" header. To mitigate this problem, the original message has been wrapped automatically by the mailing list software. Bearing fruits of the latest upstreaming efforts on cake. Changes: diffserv-llt dropped. The paper describing this DSCP allocation has gone stale and doesn't appear used. The userspace to kernel netlink messages for cake have been reworked in a backwards incompatible way, so tc & cake must be bumped together this once. Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
-rw-r--r--package/network/utils/iproute2/Makefile2
-rw-r--r--package/network/utils/iproute2/patches/950-add-cake-to-tc.patch869
2 files changed, 425 insertions, 446 deletions
diff --git a/package/network/utils/iproute2/Makefile b/package/network/utils/iproute2/Makefile
index f5a9db321d..88a4851748 100644
--- a/package/network/utils/iproute2/Makefile
+++ b/package/network/utils/iproute2/Makefile
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=iproute2
PKG_VERSION:=4.16.0
-PKG_RELEASE:=2
+PKG_RELEASE:=3
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
PKG_SOURCE_URL:=@KERNEL/linux/utils/net/iproute2
diff --git a/package/network/utils/iproute2/patches/950-add-cake-to-tc.patch b/package/network/utils/iproute2/patches/950-add-cake-to-tc.patch
index c8f70da132..3c2cdaaac3 100644
--- a/package/network/utils/iproute2/patches/950-add-cake-to-tc.patch
+++ b/package/network/utils/iproute2/patches/950-add-cake-to-tc.patch
@@ -1,6 +1,6 @@
--- 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)
@@ -22,66 +22,101 @@
+ 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
@@ -120,8 +155,6 @@
+|
+.BR diffserv4
+|
-+.BR diffserv-llt
-+|
+.BR diffserv3*
+]
+.br
@@ -578,20 +611,6 @@
+ 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:
@@ -646,103 +665,73 @@
+.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),
@@ -769,44 +758,13 @@
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>
@@ -823,14 +781,41 @@
+#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"
@@ -863,6 +848,7 @@
+ 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) {
@@ -889,45 +875,25 @@
+ 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;
@@ -935,21 +901,21 @@
+ 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;
@@ -957,14 +923,14 @@
+ 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;
@@ -974,41 +940,41 @@
+ * 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;
+
@@ -1020,7 +986,7 @@
+ * + 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) {
@@ -1029,7 +995,7 @@
+ * + 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;
+
@@ -1065,7 +1031,7 @@
+ * 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;
@@ -1095,11 +1061,11 @@
+ 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();
@@ -1176,6 +1142,7 @@
+ int wash = 0;
+ int ingress = 0;
+ int ack_filter = 0;
++ int split_gso = 0;
+ SPRINT_BUF(b1);
+ SPRINT_BUF(b2);
+
@@ -1205,23 +1172,20 @@
+ 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");
@@ -1231,31 +1195,29 @@
+ 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:
@@ -1263,10 +1225,17 @@
+ 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]);
@@ -1276,8 +1245,8 @@
+ 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)) {
@@ -1291,6 +1260,10 @@
+ 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;
+ }
@@ -1307,13 +1280,17 @@
+ 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);
@@ -1322,9 +1299,9 @@
+ 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");
@@ -1332,7 +1309,7 @@
+ 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);
@@ -1342,195 +1319,186 @@
+ 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;
+}
+
@@ -1540,3 +1508,14 @@
+ .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;
+ }
+