--- iproute2-6.14.0/ip/ipmonitor.c 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/ip/ipmonitor.c 2025-05-26 16:19:09.000000000 +0100
@@ -5,6 +5,7 @@
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*/
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -328,38 +329,46 @@
if (lmask & IPMON_LNEXTHOP &&
rtnl_add_nl_group(&rth, RTNLGRP_NEXTHOP) < 0) {
- fprintf(stderr, "Failed to add nexthop group to list\n");
- exit(1);
+ if (errno != EINVAL) {
+ fprintf(stderr, "Failed to add nexthop group to list\n");
+ exit(1);
+ }
}
if (lmask & IPMON_LSTATS &&
rtnl_add_nl_group(&rth, RTNLGRP_STATS) < 0 &&
nmask & IPMON_LSTATS) {
- fprintf(stderr, "Failed to add stats group to list\n");
- exit(1);
+ if (errno != EINVAL) {
+ fprintf(stderr, "Failed to add stats group to list\n"); + exit(1);
+ }
}
if (lmask & IPMON_LMADDR) {
if ((!preferred_family || preferred_family == AF_INET) &&
rtnl_add_nl_group(&rth, RTNLGRP_IPV4_MCADDR) < 0) {
- fprintf(stderr,
- "Failed to add ipv4 mcaddr group to list\n");
- exit(1);
+ if (errno != EINVAL) {
+ fprintf(stderr, "Failed to add ipv4 mcaddr group to list\n");
+ exit(1);
+ }
}
if ((!preferred_family || preferred_family == AF_INET6) &&
rtnl_add_nl_group(&rth, RTNLGRP_IPV6_MCADDR) < 0) {
- fprintf(stderr,
- "Failed to add ipv6 mcaddr group to list\n");
- exit(1);
+ if (errno != EINVAL) {
+ fprintf(stderr,
+ "Failed to add ipv6 mcaddr group to list\n");
+ exit(1);
+ }
}
}
if (lmask & IPMON_LACADDR) {
if ((!preferred_family || preferred_family == AF_INET6) &&
rtnl_add_nl_group(&rth, RTNLGRP_IPV6_ACADDR) < 0) {
- fprintf(stderr,
- "Failed to add ipv6 acaddr group to list\n");
- exit(1);
+ if (errno != EINVAL) {
+ fprintf(stderr, "Failed to add ipv6 acaddr group to list\n");
+ exit(1);
+ }
}
}
diff -Nru iproute2-6.14.0/ip/iproute.c iproute2-6.15.0/ip/iproute.c
--- iproute2-6.14.0/ip/iproute.c 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/ip/iproute.c 2025-05-26 16:19:09.000000000 +0100
@@ -1729,7 +1729,10 @@
if (filter.cloned) {
if (family != AF_INET6) {
- iproute_flush_cache();
+ ret = iproute_flush_cache();
+ if (ret < 0)
+ return ret;
+
if (show_stats)
printf("*** IPv4 routing cache is flushed.\n");
}
diff -Nru iproute2-6.14.0/ip/iprule.c iproute2-6.15.0/ip/iprule.c
--- iproute2-6.14.0/ip/iprule.c 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/ip/iprule.c 2025-05-26 16:19:09.000000000 +0100
@@ -23,6 +23,9 @@
#include "ip_common.h"
#include "json_print.h"
+#define PORT_MAX_MASK 0xFFFF
+#define DSCP_MAX_MASK 0x3F
+
enum list_action {
IPRULE_LIST,
IPRULE_FLUSH,
@@ -44,9 +47,9 @@
" [ iif STRING ] [ oif STRING ] [ pref NUMBER ] [ l3mdev ]\n"
" [ uidrange NUMBER-NUMBER ]\n"
" [ ipproto PROTOCOL ]\n"
- " [ sport [ NUMBER | NUMBER-NUMBER ]\n"
- " [ dport [ NUMBER | NUMBER-NUMBER ] ]\n"
- " [ dscp DSCP ] [ flowlabel FLOWLABEL[/MASK] ]\n"
+ " [ sport [ NUMBER[/MASK] | NUMBER-NUMBER ]\n"
+ " [ dport [ NUMBER[/MASK] | NUMBER-NUMBER ] ]\n"
+ " [ dscp DSCP[/MASK] ] [ flowlabel FLOWLABEL[/MASK] ]\n"
"ACTION := [ table TABLE_ID ]\n"
" [ protocol PROTO ]\n"
" [ nat ADDRESS ]\n"
@@ -80,6 +83,7 @@
int protocolmask;
struct fib_rule_port_range sport;
struct fib_rule_port_range dport;
+ __u16 sport_mask, dport_mask;
__u8 ipproto;
} filter;
@@ -186,8 +190,9 @@
return false;
}
- if (filter.sport.start) {
+ if (filter.sport_mask) {
const struct fib_rule_port_range *r;
+ __u16 sport_mask = PORT_MAX_MASK;
if (!tb[FRA_SPORT_RANGE])
return false;
@@ -196,10 +201,16 @@
if (r->start != filter.sport.start ||
r->end != filter.sport.end)
return false;
+
+ if (tb[FRA_SPORT_MASK])
+ sport_mask = rta_getattr_u16(tb[FRA_SPORT_MASK]);
+ if (filter.sport_mask != sport_mask)
+ return false;
}
- if (filter.dport.start) {
+ if (filter.dport_mask) {
const struct fib_rule_port_range *r;
+ __u16 dport_mask = PORT_MAX_MASK;
if (!tb[FRA_DPORT_RANGE])
return false;
@@ -208,6 +219,11 @@
if (r->start != filter.dport.start ||
r->end != filter.dport.end)
return false;
+
+ if (tb[FRA_DPORT_MASK])
+ dport_mask = rta_getattr_u16(tb[FRA_DPORT_MASK]);
+ if (filter.dport_mask != dport_mask)
+ return false;
}
if (filter.tun_id) {
@@ -223,14 +239,21 @@
}
if (filter.dscpmask) {
- if (tb[FRA_DSCP]) {
- __u8 dscp = rta_getattr_u8(tb[FRA_DSCP]);
+ __u8 dscp_mask = DSCP_MAX_MASK;
+ __u8 dscp;
- if (filter.dscp != dscp)
- return false;
- } else {
+ if (!tb[FRA_DSCP])
+ return false;
+
+ dscp = rta_getattr_u8(tb[FRA_DSCP]);
+ if (filter.dscp != dscp)
+ return false;
+
+ if (tb[FRA_DSCP_MASK])
+ dscp_mask = rta_getattr_u8(tb[FRA_DSCP_MASK]);
+
+ if (filter.dscpmask != dscp_mask)
return false;
- }
}
if (filter.flowlabel_mask) {
@@ -390,7 +413,26 @@
struct fib_rule_port_range *r = RTA_DATA(tb[FRA_SPORT_RANGE]);
if (r->start == r->end) {
- print_uint(PRINT_ANY, "sport", " sport %u", r->start); + if (tb[FRA_SPORT_MASK]) {
+ __u16 mask;
+
+ mask = rta_getattr_u16(tb[FRA_SPORT_MASK]);
+ print_uint(PRINT_JSON, "sport", NULL, r->start);
+ print_0xhex(PRINT_JSON, "sport_mask", NULL,
+ mask);
+ if (mask == PORT_MAX_MASK) {
+ print_uint(PRINT_FP, NULL, " sport %u", + r->start);
+ } else {
+ print_0xhex(PRINT_FP, NULL,
+ " sport %#x", r->start);
+ print_0xhex(PRINT_FP, NULL, "/%#x",
+ mask);
+ }
+ } else {
+ print_uint(PRINT_ANY, "sport", " sport %u",
+ r->start);
+ }
} else {
print_uint(PRINT_ANY, "sport_start", " sport %u",
r->start);
@@ -402,7 +444,26 @@
struct fib_rule_port_range *r = RTA_DATA(tb[FRA_DPORT_RANGE]);
if (r->start == r->end) {
- print_uint(PRINT_ANY, "dport", " dport %u", r->start); + if (tb[FRA_DPORT_MASK]) {
+ __u16 mask;
+
+ mask = rta_getattr_u16(tb[FRA_DPORT_MASK]);
+ print_uint(PRINT_JSON, "dport", NULL, r->start);
+ print_0xhex(PRINT_JSON, "dport_mask", NULL,
+ mask);
+ if (mask == 0xFFFF) {
+ print_uint(PRINT_FP, NULL, " dport %u", + r->start);
+ } else {
+ print_0xhex(PRINT_FP, NULL,
+ " dport %#x", r->start);
+ print_0xhex(PRINT_FP, NULL, "/%#x",
+ mask);
+ }
+ } else {
+ print_uint(PRINT_ANY, "dport", " dport %u",
+ r->start);
+ }
} else {
print_uint(PRINT_ANY, "dport_start", " dport %u",
r->start);
@@ -499,8 +560,24 @@
if (tb[FRA_DSCP]) {
__u8 dscp = rta_getattr_u8(tb[FRA_DSCP]);
- print_string(PRINT_ANY, "dscp", " dscp %s",
- rtnl_dscp_n2a(dscp, b1, sizeof(b1)));
+ if (tb[FRA_DSCP_MASK]) {
+ __u8 mask = rta_getattr_u8(tb[FRA_DSCP_MASK]);
+
+ print_string(PRINT_JSON, "dscp", NULL,
+ rtnl_dscp_n2a(dscp, b1, sizeof(b1)));
+ print_0xhex(PRINT_JSON, "dscp_mask", NULL, mask);
+ if (mask == DSCP_MAX_MASK) {
+ print_string(PRINT_FP, NULL, " dscp %s",
+ rtnl_dscp_n2a(dscp, b1,
+ sizeof(b1)));
+ } else {
+ print_0xhex(PRINT_FP, NULL, " dscp %#x", dscp); + print_0xhex(PRINT_FP, NULL, "/%#x", mask);
+ }
+ } else {
+ print_string(PRINT_ANY, "dscp", " dscp %s",
+ rtnl_dscp_n2a(dscp, b1, sizeof(b1)));
+ }
}
/* The kernel will either provide both attributes, or none */
@@ -600,6 +677,55 @@
return 0;
}
+static void iprule_port_parse(char *arg, struct fib_rule_port_range *r,
+ __u16 *mask)
+{
+ char *sep;
+
+ *mask = PORT_MAX_MASK;
+
+ sep = strchr(arg, '-');
+ if (sep) {
+ *sep = '\0';
+
+ if (get_u16(&r->start, arg, 0))
+ invarg("invalid port range start", arg);
+
+ if (get_u16(&r->end, sep + 1, 0))
+ invarg("invalid port range end", sep + 1);
+
+ return;
+ }
+
+ sep = strchr(arg, '/');
+ if (sep) {
+ *sep = '\0';
+
+ if (get_u16(mask, sep + 1, 0))
+ invarg("invalid mask", sep + 1);
+ }
+
+ if (get_u16(&r->start, arg, 0))
+ invarg("invalid port", arg);
+
+ r->end = r->start;
+}
+
+static void iprule_dscp_parse(char *arg, __u32 *dscp, __u32 *mask)
+{
+ char *slash;
+
+ *mask = DSCP_MAX_MASK;
+
+ slash = strchr(arg, '/');
+ if (slash != NULL)
+ *slash = '\0';
+ if (rtnl_dscp_a2n(dscp, arg))
+ invarg("invalid dscp", arg);
+ if (slash && get_u32(mask, slash + 1, 0))
+ invarg("invalid dscp mask", slash + 1);
+}
+
static void iprule_flowlabel_parse(char *arg, __u32 *flowlabel,
__u32 *flowlabel_mask)
{
@@ -746,35 +872,17 @@
invarg("Invalid \"ipproto\" value\n", *argv);
filter.ipproto = ipproto;
} else if (strcmp(*argv, "sport") == 0) {
- struct fib_rule_port_range r;
- int ret;
-
NEXT_ARG();
- ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
- if (ret == 1)
- r.end = r.start;
- else if (ret != 2)
- invarg("invalid port range\n", *argv);
- filter.sport = r;
+ iprule_port_parse(*argv, &filter.sport,
+ &filter.sport_mask);
} else if (strcmp(*argv, "dport") == 0) {
- struct fib_rule_port_range r;
- int ret;
-
NEXT_ARG();
- ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
- if (ret == 1)
- r.end = r.start;
- else if (ret != 2)
- invarg("invalid dport range\n", *argv);
- filter.dport = r;
+ iprule_port_parse(*argv, &filter.dport,
+ &filter.dport_mask);
} else if (strcmp(*argv, "dscp") == 0) {
- __u32 dscp;
-
NEXT_ARG();
- if (rtnl_dscp_a2n(&dscp, *argv))
- invarg("invalid dscp\n", *argv);
- filter.dscp = dscp;
- filter.dscpmask = 1;
+ iprule_dscp_parse(*argv, &filter.dscp,
+ &filter.dscpmask);
} else if (strcmp(*argv, "flowlabel") == 0) {
NEXT_ARG();
@@ -1036,35 +1144,35 @@
addattr8(&req.n, sizeof(req), FRA_IP_PROTO, ipproto);
} else if (strcmp(*argv, "sport") == 0) {
struct fib_rule_port_range r;
- int ret = 0;
+ __u16 sport_mask;
NEXT_ARG();
- ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
- if (ret == 1)
- r.end = r.start;
- else if (ret != 2)
- invarg("invalid port range\n", *argv);
+ iprule_port_parse(*argv, &r, &sport_mask);
addattr_l(&req.n, sizeof(req), FRA_SPORT_RANGE, &r,
sizeof(r));
+ if (sport_mask != PORT_MAX_MASK)
+ addattr16(&req.n, sizeof(req), FRA_SPORT_MASK, + sport_mask);
} else if (strcmp(*argv, "dport") == 0) {
struct fib_rule_port_range r;
- int ret = 0;
+ __u16 dport_mask;
NEXT_ARG();
- ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
- if (ret == 1)
- r.end = r.start;
- else if (ret != 2)
- invarg("invalid dport range\n", *argv);
+ iprule_port_parse(*argv, &r, &dport_mask);
addattr_l(&req.n, sizeof(req), FRA_DPORT_RANGE, &r,
sizeof(r));
+ if (dport_mask != PORT_MAX_MASK)
+ addattr16(&req.n, sizeof(req), FRA_DPORT_MASK, + dport_mask);
} else if (strcmp(*argv, "dscp") == 0) {
- __u32 dscp;
+ __u32 dscp, dscp_mask;
NEXT_ARG();
- if (rtnl_dscp_a2n(&dscp, *argv))
- invarg("invalid dscp\n", *argv);
+ iprule_dscp_parse(*argv, &dscp, &dscp_mask);
addattr8(&req.n, sizeof(req), FRA_DSCP, dscp);
+ if (dscp_mask != DSCP_MAX_MASK)
+ addattr8(&req.n, sizeof(req), FRA_DSCP_MASK,
+ dscp_mask);
} else if (strcmp(*argv, "flowlabel") == 0) {
__u32 flowlabel, flowlabel_mask;
diff -Nru iproute2-6.14.0/ip/ipxfrm.c iproute2-6.15.0/ip/ipxfrm.c
--- iproute2-6.14.0/ip/ipxfrm.c 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/ip/ipxfrm.c 2025-05-26 16:19:09.000000000 +0100
@@ -351,7 +351,10 @@
t = (long)time;
tp = localtime(&t);
- strftime(str, sizeof(str), "%Y-%m-%d %T", tp);
+ if (!tp)
+ strcpy(str, "invalid-time");
+ else
+ strftime(str, sizeof(str), "%Y-%m-%d %T", tp);
}
return str;
diff -Nru iproute2-6.14.0/lib/color.c iproute2-6.15.0/lib/color.c
--- iproute2-6.14.0/lib/color.c 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/lib/color.c 2025-05-26 16:19:09.000000000 +0100
@@ -81,6 +81,18 @@
set_color_palette();
}
+int default_color_opt(void)
+{
+ const char *no_color;
+
+ /* If NO_COLOR has a non-empty value, coloured output is never wanted */
+ no_color = getenv("NO_COLOR");
+ if (no_color && *no_color)
+ return COLOR_OPT_NEVER;
+
+ return CONF_COLOR;
+}
+
bool check_enable_color(int color, int json)
{
if (json || color == COLOR_OPT_NEVER)
diff -Nru iproute2-6.14.0/lib/utils.c iproute2-6.15.0/lib/utils.c
--- iproute2-6.14.0/lib/utils.c 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/lib/utils.c 2025-05-26 16:19:09.000000000 +0100
@@ -304,10 +304,6 @@
if (res == ULLONG_MAX && errno == ERANGE)
return -1;
- /* in case ULL is 128 bits */
- if (res > 0xFFFFFFFFFFFFFFFFULL)
- return -1;
-
*val = res;
return 0;
}
@@ -399,8 +395,6 @@
return -1;
if ((res == LLONG_MIN || res == LLONG_MAX) && errno == ERANGE)
return -1;
- if (res > INT64_MAX || res < INT64_MIN)
- return -1;
*val = res;
return 0;
diff -Nru iproute2-6.14.0/MAINTAINERS iproute2-6.15.0/MAINTAINERS
--- iproute2-6.14.0/MAINTAINERS 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/MAINTAINERS 2025-05-26 16:19:09.000000000 +0100
@@ -26,7 +26,7 @@
L: netdev@vger.kernel.org
Ethernet Bridging - bridge
-M: Roopa Prabhu <roopa@nvidia.com>
+M: Ido Schimmel <idosch@nvidia.com>
M: Nikolay Aleksandrov <razor@blackwall.org>
L: bridge@lists.linux-foundation.org (moderated for non-subscribers)
F: bridge/*
diff -Nru iproute2-6.14.0/man/man8/ip-link.8.in iproute2-6.15.0/man/man8/ip-link.8.in
--- iproute2-6.14.0/man/man8/ip-link.8.in 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/man/man8/ip-link.8.in 2025-05-26 16:19:09.000000000 +0100
@@ -882,10 +882,14 @@
[
.BI mode " MODE "
] [
+.BI scrub " SCRUB "
+] [
.I "POLICY "
] [
.BR peer
[
+.BI scrub " SCRUB "
+] [
.I "POLICY "
] [
.I "NAME "
@@ -899,6 +903,17 @@
as possible values. Default option is "l3".
.sp
+.BI scrub " SCRUB"
+- specifies the scrub behavior of the netkit device with "default" and +"none" as possible values. With "default" the device zeroes the +skb->{mark,priority} fields before invoking the attached BPF program
+when its peer device resides in a different network namespace. With
+"none" the device leaves clearing skb->{mark,priority} up to the BPF +program. Default option is "default". Specifying scrub before the peer +option refers to the primary device, after the peer option refers to
+the peer device.
+
+.sp
.I "POLICY"
- specifies the default device policy when no BPF programs are attached
with "forward" and "blackhole" as possible values. Default option is
diff -Nru iproute2-6.14.0/man/man8/ip-rule.8.in iproute2-6.15.0/man/man8/ip-rule.8.in
--- iproute2-6.14.0/man/man8/ip-rule.8.in 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/man/man8/ip-rule.8.in 2025-05-26 16:19:09.000000000 +0100
@@ -37,7 +37,7 @@
.B tos
.IR TOS " ] [ "
.B dscp
-.IR DSCP " ] [ "
+.IR DSCP\fR[\fB/\fIMASK "] ] [ "
.B fwmark
.IR FWMARK\fR[\fB/\fIMASK "] ] [ "
.B iif
@@ -52,10 +52,10 @@
.B ipproto
.IR PROTOCOL " ] [ "
.BR sport " [ "
-.IR NUMBER " | "
+.IR NUMBER\fR[\fB/\fIMASK "] | "
.IR NUMBER "-" NUMBER " ] ] [ "
.BR dport " [ "
-.IR NUMBER " | "
+.IR NUMBER\fR[\fB/\fIMASK "] | "
.IR NUMBER "-" NUMBER " ] ] [ "
.B tun_id
.IR TUN_ID " ] [ "
@@ -239,9 +239,10 @@
select the TOS value to match.
.TP
-.BI dscp " DSCP"
-select the DSCP value to match. DSCP values can be written either directly as
-numeric values (valid values are 0-63), or using symbolic names specified in +.BI dscp " DSCP\fR[\fB/\fIMASK\fR]"
+select the DSCP value to match with an optional mask. DSCP values can be +written either directly as numeric values (valid values are 0-63), or using +symbolic names specified in
.BR @SYSCONF_USR_DIR@/rt_dsfield " or " @SYSCONF_ETC_DIR@/rt_dsfield
(has precedence if exists).
However, note that the file specifies full 8-bit dsfield values, whereas
@@ -270,12 +271,14 @@
select the ip protocol value to match.
.TP
-.BI sport " NUMBER | NUMBER-NUMBER"
-select the source port value to match. supports port range.
+.BI sport " NUMBER\fR[\fB/\fIMASK\fR] | NUMBER-NUMBER"
+select the source port value to match with an optional mask. Supports port +range.
.TP
-.BI dport " NUMBER | NUMBER-NUMBER"
-select the destination port value to match. supports port range.
+.BI dport " NUMBER\fR[\fB/\fIMASK\fR] | NUMBER-NUMBER"
+select the destination port value to match with an optional mask. Supports port
+range.
.TP
.BI priority " PREFERENCE"
diff -Nru iproute2-6.14.0/man/man8/rdma-statistic.8 iproute2-6.15.0/man/man8/rdma-statistic.8
--- iproute2-6.14.0/man/man8/rdma-statistic.8 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/man/man8/rdma-statistic.8 2025-05-26 16:19:09.000000000 +0100
@@ -39,6 +39,7 @@
.B auto
.RI "{ " CRITERIA " | "
.BR off " }"
+.B [ optional-counters | on/off ]
.ti -8
.B rdma statistic
@@ -180,6 +181,11 @@
On device mlx5_2 port 1, for each new user QP bind it with a counter automatically. Per counter for QPs with same qp type.
.RE
.PP
+rdma statistic qp set link mlx5_2/1 auto type on optional-counters on
+.RS 4
+On device mlx5_2 port 1, for each new user QP bind it with a counter automatically. Per counter for QPs with same qp type. Whilst also binding the currently enabled optional-counters.
+.RE
+.PP
rdma statistic qp set link mlx5_2/1 auto pid on
.RS 4
On device mlx5_2 port 1, for each new user QP bind it with a counter automatically. Per counter for QPs with same pid.
diff -Nru iproute2-6.14.0/misc/nstat.c iproute2-6.15.0/misc/nstat.c
--- iproute2-6.14.0/misc/nstat.c 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/misc/nstat.c 2025-05-26 16:19:09.000000000 +0100
@@ -218,6 +218,10 @@
p = next;
}
n = db;
+ if (n == NULL) {
+ fprintf(stderr, "Error: Invalid input – line has ':' but no entries. Add values after ':'.\n");
+ exit(-2);
+ }
nread = getline(&buf, &buflen, fp);
if (nread == -1) {
fprintf(stderr, "%s:%d: error parsing history file\n", diff -Nru iproute2-6.14.0/rdma/include/uapi/rdma/rdma_netlink.h iproute2-6.15.0/rdma/include/uapi/rdma/rdma_netlink.h
--- iproute2-6.14.0/rdma/include/uapi/rdma/rdma_netlink.h 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/rdma/include/uapi/rdma/rdma_netlink.h 2025-05-26 16:19:09.000000000 +0100
@@ -580,6 +580,8 @@
RDMA_NLDEV_ATTR_EVENT_TYPE, /* u8 */
RDMA_NLDEV_SYS_ATTR_MONITOR_MODE, /* u8 */
+
+ RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED, /* u8 */
/*
* Always the end
*/
diff -Nru iproute2-6.14.0/rdma/stat.c iproute2-6.15.0/rdma/stat.c
--- iproute2-6.14.0/rdma/stat.c 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/rdma/stat.c 2025-05-26 16:19:09.000000000 +0100
@@ -7,6 +7,7 @@
#include "rdma.h"
#include "res.h"
#include "stat.h"
+#include "utils.h"
#include <inttypes.h>
static int stat_help(struct rd *rd)
@@ -62,7 +63,8 @@
{ NULL },
};
-static int prepare_auto_mode_str(uint32_t mask, char *output, int len) +static int prepare_auto_mode_str(uint32_t mask, bool opcnt, char *output,
+ int len)
{
char s[] = "qp auto";
int i, outlen = strlen(s);
@@ -90,6 +92,10 @@
if (outlen + strlen(" on") >= len)
return -EINVAL;
strcat(output, " on");
+
+ strcat(output, " optional-counters ");
+ strcat(output, (opcnt) ? "on" : "off");
+
} else {
if (outlen + strlen(" off") >= len)
return -EINVAL;
@@ -104,6 +110,7 @@
struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
uint32_t mode = 0, mask = 0;
char output[128] = {};
+ bool opcnt = false;
uint32_t idx, port;
const char *name;
@@ -126,7 +133,10 @@
if (!tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK])
return MNL_CB_ERROR;
mask = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]);
- prepare_auto_mode_str(mask, output, sizeof(output));
+ if (tb[RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED])
+ opcnt = mnl_attr_get_u8(
+ tb[RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED]);
+ prepare_auto_mode_str(mask, opcnt, output, sizeof(output));
} else {
snprintf(output, sizeof(output), "qp auto off");
}
@@ -351,6 +361,7 @@
{ .name = "lqpn", .is_number = true },
{ .name = "pid", .is_number = true },
{ .name = "qp-type", .is_number = false },
+ { .name = "optional-counters", .is_number = false },
};
static int stat_qp_show_one_link(struct rd *rd)
@@ -395,9 +406,37 @@
return rd_exec_cmd(rd, cmds, "parameter");
}
+static bool stat_get_on_off(struct rd *rd, const char *arg, int *ret)
+{
+ bool value = false;
+
+ if (strcmpx(rd_argv(rd), arg) != 0) {
+ *ret = -EINVAL;
+ return false;
+ }
+
+ rd_arg_inc(rd);
+
+ if (rd_is_multiarg(rd)) {
+ pr_err("The parameter %s shouldn't include range\n", arg);
+ *ret = EINVAL;
+ return false;
+ }
+
+ value = parse_on_off(arg, rd_argv(rd), ret);
+ if (*ret)
+ return false;
+
+ rd_arg_inc(rd);
+
+ return value;
+}
+
static int stat_qp_set_link_auto_sendmsg(struct rd *rd, uint32_t mask)
{
uint32_t seq;
+ bool opcnt;
+ int ret;
rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
&seq, (NLM_F_REQUEST | NLM_F_ACK));
@@ -408,6 +447,13 @@
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
RDMA_COUNTER_MODE_AUTO);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask);
+ if (rd_argc(rd)) {
+ opcnt = stat_get_on_off(rd, "optional-counters", &ret);
+ if (ret)
+ return ret;
+ mnl_attr_put_u8(rd->nlh, RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED,
+ opcnt);
+ }
return rd_sendrecv_msg(rd, seq);
}
diff -Nru iproute2-6.14.0/rdma/utils.c iproute2-6.15.0/rdma/utils.c
--- iproute2-6.14.0/rdma/utils.c 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/rdma/utils.c 2025-05-26 16:19:09.000000000 +0100
@@ -479,6 +479,7 @@
[RDMA_NLDEV_ATTR_PARENT_NAME] = MNL_TYPE_STRING,
[RDMA_NLDEV_ATTR_EVENT_TYPE] = MNL_TYPE_U8,
[RDMA_NLDEV_SYS_ATTR_MONITOR_MODE] = MNL_TYPE_U8,
+ [RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED] = MNL_TYPE_U8,
};
static int rd_attr_check(const struct nlattr *attr, int *typep)
diff -Nru iproute2-6.14.0/tc/tc.c iproute2-6.15.0/tc/tc.c
--- iproute2-6.14.0/tc/tc.c 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/tc/tc.c 2025-05-26 16:19:09.000000000 +0100
@@ -254,7 +254,7 @@
{
const char *libbpf_version;
char *batch_file = NULL;
- int color = CONF_COLOR;
+ int color = default_color_opt();
int ret;
while (argc > 1) {
diff -Nru iproute2-6.14.0/tc/tc_core.c iproute2-6.15.0/tc/tc_core.c
--- iproute2-6.14.0/tc/tc_core.c 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/tc/tc_core.c 2025-05-26 16:19:09.000000000 +0100
@@ -23,12 +23,12 @@
static double tick_in_usec = 1;
static double clock_factor = 1;
-static unsigned int tc_core_time2tick(unsigned int time)
+static double tc_core_time2tick(double time)
{
return time * tick_in_usec;
}
-unsigned int tc_core_tick2time(unsigned int tick)
+double tc_core_tick2time(double tick)
{
return tick / tick_in_usec;
}
@@ -45,7 +45,7 @@
unsigned int tc_calc_xmittime(__u64 rate, unsigned int size)
{
- return tc_core_time2tick(TIME_UNITS_PER_SEC*((double)size/(double)rate));
+ return ceil(tc_core_time2tick(TIME_UNITS_PER_SEC*((double)size/(double)rate)));
}
unsigned int tc_calc_xmitsize(__u64 rate, unsigned int ticks)
diff -Nru iproute2-6.14.0/tc/tc_core.h iproute2-6.15.0/tc/tc_core.h
--- iproute2-6.14.0/tc/tc_core.h 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/tc/tc_core.h 2025-05-26 16:19:09.000000000 +0100
@@ -12,7 +12,7 @@
};
-unsigned tc_core_tick2time(unsigned tick);
+double tc_core_tick2time(double tick);
unsigned tc_core_time2ktime(unsigned time);
unsigned tc_core_ktime2time(unsigned ktime);
unsigned tc_calc_xmittime(__u64 rate, unsigned size);
diff -Nru iproute2-6.14.0/tc/tc_util.c iproute2-6.15.0/tc/tc_util.c
--- iproute2-6.14.0/tc/tc_util.c 2025-03-24 16:04:44.000000000 +0000
+++ iproute2-6.15.0/tc/tc_util.c 2025-05-26 16:19:09.000000000 +0100
@@ -665,7 +665,8 @@
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 @@
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 @@
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 @@
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 @@
}
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};
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 490 |
Nodes: | 16 (2 / 14) |
Uptime: | 59:41:51 |
Calls: | 9,676 |
Files: | 13,719 |
Messages: | 6,171,352 |