From: Ido Schimmel Date: Tue, 4 Feb 2025 12:31:43 +0000 (+0200) Subject: tc_util: Add support for 64-bit hardware packets counter X-Git-Tag: v6.15.0~6^2~16 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e7638a027a05817583e62a432cb88079a981b871;p=thirdparty%2Fiproute2.git tc_util: Add support for 64-bit hardware packets counter The netlink nest that carriers tc action statistics looks as follows: [TCA_ACT_STATS] [TCA_STATS_BASIC] [TCA_STATS_BASIC_HW] Where 'TCA_STATS_BASIC' carries the combined software and hardware packets (32-bits) and bytes (64-bit) counters and 'TCA_STATS_BASIC_HW' carries the hardware statistics. When the number of packets exceeds 0xffffffff, the kernel emits the 'TCA_STATS_PKT64' attribute: [TCA_ACT_STATS] [TCA_STATS_BASIC] [TCA_STATS_PKT64] [TCA_STATS_BASIC_HW] [TCA_STATS_PKT64] This layout is not ideal as the only way for user space to know what each 'TCA_STATS_PKT64' attribute carries is to check which attribute precedes it, which is exactly what some applications are doing [1]. Do the same in iproute2 so that users with existing kernels could read the 64-bit hardware packets counter of tc actions instead of reading the truncated 32-bit counter. Before: $ tc -s filter show dev swp2 ingress filter protocol all pref 1 flower chain 0 filter protocol all pref 1 flower chain 0 handle 0x1 skip_sw in_hw in_hw_count 1 action order 1: mirred (Egress Redirect to device swp1) stolen index 1 ref 1 bind 1 installed 47 sec used 23 sec Action statistics: Sent 368689092544 bytes 5760767071 pkt (dropped 0, overlimits 0 requeues 0) Sent software 0 bytes 0 pkt Sent hardware 368689092544 bytes 1465799775 pkt backlog 0b 0p requeues 0 used_hw_stats immediate Where 5760767071 - 1465799775 = 0x100000000 After: $ tc -s filter show dev swp2 ingress filter protocol all pref 1 flower chain 0 filter protocol all pref 1 flower chain 0 handle 0x1 skip_sw in_hw in_hw_count 1 action order 1: mirred (Egress Redirect to device swp1) stolen index 1 ref 1 bind 1 installed 71 sec used 47 sec Action statistics: Sent 368689092544 bytes 5760767071 pkt (dropped 0, overlimits 0 requeues 0) Sent software 0 bytes 0 pkt Sent hardware 368689092544 bytes 5760767071 pkt backlog 0b 0p requeues 0 used_hw_stats immediate [1] https://github.com/openvswitch/ovs/commit/006e1c6dbfbadf474c17c8fa1ea358918d371588 Reported-by: Joe Botha Reviewed-by: Petr Machata Signed-off-by: Ido Schimmel Signed-off-by: David Ahern --- diff --git a/tc/tc_util.c b/tc/tc_util.c index cf89fb7c..ff0ac170 100644 --- a/tc/tc_util.c +++ b/tc/tc_util.c @@ -665,7 +665,8 @@ void print_tm(const struct tcf_t *tm) tm->expires / hz); } -static void print_tcstats_basic_hw(struct rtattr **tbs, const char *prefix) +static void print_tcstats_basic_hw(struct rtattr **tbs, const char *prefix, + __u64 packets64, __u64 packets64_hw) { struct gnet_stats_basic bs_hw; @@ -674,8 +675,9 @@ static void print_tcstats_basic_hw(struct rtattr **tbs, const char *prefix) memcpy(&bs_hw, RTA_DATA(tbs[TCA_STATS_BASIC_HW]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC_HW]), sizeof(bs_hw))); + packets64_hw = packets64_hw ? : bs_hw.packets; - if (bs_hw.bytes == 0 && bs_hw.packets == 0) + if (bs_hw.bytes == 0 && packets64_hw == 0) return; if (tbs[TCA_STATS_BASIC]) { @@ -684,15 +686,16 @@ static void print_tcstats_basic_hw(struct rtattr **tbs, const char *prefix) memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]), sizeof(bs))); + packets64 = packets64 ? : bs.packets; - if (bs.bytes >= bs_hw.bytes && bs.packets >= bs_hw.packets) { + if (bs.bytes >= bs_hw.bytes && packets64 >= packets64_hw) { print_nl(); print_string(PRINT_FP, NULL, "%s", prefix); print_lluint(PRINT_ANY, "sw_bytes", "Sent software %llu bytes", bs.bytes - bs_hw.bytes); - print_uint(PRINT_ANY, "sw_packets", " %u pkt", - bs.packets - bs_hw.packets); + print_lluint(PRINT_ANY, "sw_packets", " %llu pkt", + packets64 - packets64_hw); } } @@ -700,21 +703,40 @@ static void print_tcstats_basic_hw(struct rtattr **tbs, const char *prefix) print_string(PRINT_FP, NULL, "%s", prefix); print_lluint(PRINT_ANY, "hw_bytes", "Sent hardware %llu bytes", bs_hw.bytes); - print_uint(PRINT_ANY, "hw_packets", " %u pkt", bs_hw.packets); + print_lluint(PRINT_ANY, "hw_packets", " %llu pkt", packets64_hw); +} + +static void parse_packets64(const struct rtattr *nest, __u64 *p_packets64, + __u64 *p_packets64_hw) +{ + unsigned short prev_type = __TCA_STATS_MAX; + const struct rtattr *pos; + + /* 'TCA_STATS_PKT64' can appear twice in the 'TCA_ACT_STATS' nest. + * Whether the attribute carries the combined or hardware only + * statistics depends on the attribute that precedes it in the nest. + */ + rtattr_for_each_nested(pos, nest) { + if (pos->rta_type == TCA_STATS_PKT64 && + prev_type == TCA_STATS_BASIC) + *p_packets64 = rta_getattr_u64(pos); + else if (pos->rta_type == TCA_STATS_PKT64 && + prev_type == TCA_STATS_BASIC_HW) + *p_packets64_hw = rta_getattr_u64(pos); + prev_type = pos->rta_type; + } } void print_tcstats2_attr(struct rtattr *rta, const char *prefix, struct rtattr **xstats) { struct rtattr *tbs[TCA_STATS_MAX + 1]; + __u64 packets64 = 0, packets64_hw = 0; parse_rtattr_nested(tbs, TCA_STATS_MAX, rta); + parse_packets64(rta, &packets64, &packets64_hw); if (tbs[TCA_STATS_BASIC]) { struct gnet_stats_basic bs = {0}; - __u64 packets64 = 0; - - if (tbs[TCA_STATS_PKT64]) - packets64 = rta_getattr_u64(tbs[TCA_STATS_PKT64]); memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]), sizeof(bs))); @@ -740,7 +762,7 @@ void print_tcstats2_attr(struct rtattr *rta, const char *prefix, struct rtattr * } if (tbs[TCA_STATS_BASIC_HW]) - print_tcstats_basic_hw(tbs, prefix); + print_tcstats_basic_hw(tbs, prefix, packets64, packets64_hw); if (tbs[TCA_STATS_RATE_EST64]) { struct gnet_stats_rate_est64 re = {0};