* [PATCH iproute2-next v2 1/6] netshaper: Extract parse_scope() and parse_rate() helpers
2026-05-09 2:23 [PATCH iproute2-next v2 0/6] netshaper: Extend netshaper support Mohsin Bashir
@ 2026-05-09 2:23 ` Mohsin Bashir
2026-05-09 2:23 ` [PATCH iproute2-next v2 2/6] netshaper: Add bw-min and weight parameter support Mohsin Bashir
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Mohsin Bashir @ 2026-05-09 2:23 UTC (permalink / raw)
To: netdev; +Cc: dsahern, stephen, pabeni, kuba, ernis, mohsin.bashr
From: Mohsin Bashir <hmohsin@meta.com>
Add parse_scope() and parse_rate() helpers in preparation for
adding the group command which will need scope and rate parsing
at various places in the code. This patch will help to avoid
code duplication.
Verify that the tool works as before:
./netshaper set dev eth2 handle scope queue id 0 bw-max 100kbit
./netshaper delete dev eth2 handle scope queue id 0 bw-max 100kbit
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Mohsin Bashir <hmohsin@meta.com>
---
include/utils.h | 1 +
netshaper/netshaper.c | 63 ++++++++++++++++++++++---------------------
2 files changed, 34 insertions(+), 30 deletions(-)
diff --git a/include/utils.h b/include/utils.h
index e4e318e2..f0d45bad 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -260,6 +260,7 @@ unsigned int print_name_and_link(const char *fmt,
__attribute__((format(printf, 1, 0)));
+#define BITS_PER_BYTE 8
#define BIT(nr) (UINT64_C(1) << (nr))
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
diff --git a/netshaper/netshaper.c b/netshaper/netshaper.c
index 47fb805e..f90ceb11 100644
--- a/netshaper/netshaper.c
+++ b/netshaper/netshaper.c
@@ -47,6 +47,26 @@ static const char *net_shaper_scope_names[NET_SHAPER_SCOPE_MAX + 1] = {
"node"
};
+static int parse_scope(const char *str)
+{
+ for (int i = 1; i <= NET_SHAPER_SCOPE_MAX; i++) {
+ if (strcmp(str, net_shaper_scope_names[i]) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static int parse_rate(const char *str, __u64 *rate_bps)
+{
+ if (get_rate64(rate_bps, str)) {
+ fprintf(stderr, "Invalid rate value \"%s\"\n", str);
+ return -1;
+ }
+ /* get_rate64 returns bytes/sec, convert to bits/sec */
+ *rate_bps *= BITS_PER_BYTE;
+ return 0;
+}
+
static void print_netshaper_attrs(struct nlmsghdr *answer)
{
struct genlmsghdr *ghdr = NLMSG_DATA(answer);
@@ -117,12 +137,8 @@ static int do_cmd(int argc, char **argv, int cmd)
ifindex = ll_name_to_index(*argv);
} else if (strcmp(*argv, "bw-max") == 0) {
NEXT_ARG();
- if (get_rate64(&bw_max_bps, *argv)) {
- fprintf(stderr, "Invalid bw-max value\n");
+ if (parse_rate(*argv, &bw_max_bps))
return -1;
- }
- /* Convert Bps to bps */
- bw_max_bps *= 8;
} else if (strcmp(*argv, "handle") == 0) {
handle_present = true;
NEXT_ARG();
@@ -134,34 +150,24 @@ static int do_cmd(int argc, char **argv, int cmd)
}
NEXT_ARG();
- if (strcmp(*argv, "netdev") == 0) {
- handle_scope = NET_SHAPER_SCOPE_NETDEV;
- /* For netdev scope, id is optional - check if next arg is "id" */
+ handle_scope = parse_scope(*argv);
+ if (handle_scope < 0) {
+ fprintf(stderr, "Invalid scope \"%s\"\n", *argv);
+ return -1;
+ }
+
+ if (handle_scope == NET_SHAPER_SCOPE_NETDEV) {
+ /* For netdev scope, id is optional */
if (argc > 1 && strcmp(argv[1], "id") == 0) {
- NEXT_ARG(); /* move to "id" */
- NEXT_ARG(); /* move to id value */
+ NEXT_ARG();
+ NEXT_ARG();
if (get_unsigned(&handle_id, *argv, 10)) {
fprintf(stderr, "Invalid handle id\n");
return -1;
}
}
- } else if (strcmp(*argv, "queue") == 0) {
- handle_scope = NET_SHAPER_SCOPE_QUEUE;
- /* For queue scope, id is required */
- NEXT_ARG();
- if (strcmp(*argv, "id") != 0) {
- fprintf(stderr, "What is \"%s\"\n", *argv);
- usage();
- return -1;
- }
- NEXT_ARG();
- if (get_unsigned(&handle_id, *argv, 10)) {
- fprintf(stderr, "Invalid handle id\n");
- return -1;
- }
- } else if (strcmp(*argv, "node") == 0) {
- handle_scope = NET_SHAPER_SCOPE_NODE;
- /* For node scope, id is required */
+ } else {
+ /* For queue/node scope, id is required */
NEXT_ARG();
if (strcmp(*argv, "id") != 0) {
fprintf(stderr, "What is \"%s\"\n", *argv);
@@ -173,9 +179,6 @@ static int do_cmd(int argc, char **argv, int cmd)
fprintf(stderr, "Invalid handle id\n");
return -1;
}
- } else {
- fprintf(stderr, "Invalid scope\n");
- return -1;
}
} else {
fprintf(stderr, "What is \"%s\"\n", *argv);
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH iproute2-next v2 2/6] netshaper: Add bw-min and weight parameter support
2026-05-09 2:23 [PATCH iproute2-next v2 0/6] netshaper: Extend netshaper support Mohsin Bashir
2026-05-09 2:23 ` [PATCH iproute2-next v2 1/6] netshaper: Extract parse_scope() and parse_rate() helpers Mohsin Bashir
@ 2026-05-09 2:23 ` Mohsin Bashir
2026-05-09 2:23 ` [PATCH iproute2-next v2 3/6] netshaper: Extend show output with parent, bw-min and weight Mohsin Bashir
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Mohsin Bashir @ 2026-05-09 2:23 UTC (permalink / raw)
To: netdev; +Cc: dsahern, stephen, pabeni, kuba, ernis, mohsin.bashr
From: Mohsin Bashir <hmohsin@meta.com>
Add bw-min and weight parameters to the set command. Previously
only bw-max was supported. Use boolean flags (has_bw_min,
has_bw_max, has_weight) to track which parameters were provided,
so that zero-valued attrs like bw-max 0 are correctly sent.
Only emit rate and weight attrs for the set command. Add device
name validation and only send handle id when explicitly provided.
Update usage text to reflect the new parameters.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Mohsin Bashir <hmohsin@meta.com>
---
netshaper/netshaper.c | 59 ++++++++++++++++++++++++++++++++++---------
1 file changed, 47 insertions(+), 12 deletions(-)
diff --git a/netshaper/netshaper.c b/netshaper/netshaper.c
index f90ceb11..186076a1 100644
--- a/netshaper/netshaper.c
+++ b/netshaper/netshaper.c
@@ -33,11 +33,13 @@ static void usage(void)
"OPTIONS := { -V[ersion] | -c[olor] | -help }\n"
"COMMAND := { set | get | delete } dev DEVNAME\n"
" handle scope HANDLE_SCOPE [id HANDLE_ID]\n"
- " [bw-max BW_MAX]\n"
+ " [bw-min BW_MIN] [bw-max BW_MAX] [weight WEIGHT]\n"
+ "\n"
"Where: DEVNAME := STRING\n"
" HANDLE_SCOPE := { netdev | queue | node }\n"
" HANDLE_ID := UINT (required for queue/node, optional for netdev)\n"
- " BW_MAX := UINT{ kbit | mbit | gbit }\n");
+ " BW_MAX/BW_MIN := UINT{ kbit | mbit | gbit }\n"
+ " WEIGHT := UINT\n");
}
static const char *net_shaper_scope_names[NET_SHAPER_SCOPE_MAX + 1] = {
@@ -123,22 +125,40 @@ static int do_cmd(int argc, char **argv, int cmd)
GENL_REQUEST(req, 1024, genl_family, 0, NET_SHAPER_FAMILY_VERSION, cmd,
NLM_F_REQUEST | NLM_F_ACK);
- struct nlmsghdr *answer;
- __u64 bw_max_bps = 0;
- int ifindex = -1;
+ bool has_bw_min = false, has_bw_max = false, has_weight = false;
int handle_scope = NET_SHAPER_SCOPE_UNSPEC;
- __u32 handle_id = 0;
+ __u64 bw_min_bps = 0, bw_max_bps = 0;
+ __u32 handle_id = 0, weight = 0;
bool handle_present = false;
- int err;
+ bool has_handle_id = false;
+ struct nlmsghdr *answer;
+ int err, ifindex = -1;
while (argc > 0) {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
ifindex = ll_name_to_index(*argv);
+ if (ifindex == 0) {
+ fprintf(stderr, "Device \"%s\" not found\n", *argv);
+ return -1;
+ }
+ } else if (strcmp(*argv, "bw-min") == 0) {
+ NEXT_ARG();
+ if (parse_rate(*argv, &bw_min_bps))
+ return -1;
+ has_bw_min = true;
} else if (strcmp(*argv, "bw-max") == 0) {
NEXT_ARG();
if (parse_rate(*argv, &bw_max_bps))
return -1;
+ has_bw_max = true;
+ } else if (strcmp(*argv, "weight") == 0) {
+ NEXT_ARG();
+ if (get_unsigned(&weight, *argv, 10)) {
+ fprintf(stderr, "Invalid weight value\n");
+ return -1;
+ }
+ has_weight = true;
} else if (strcmp(*argv, "handle") == 0) {
handle_present = true;
NEXT_ARG();
@@ -165,6 +185,7 @@ static int do_cmd(int argc, char **argv, int cmd)
fprintf(stderr, "Invalid handle id\n");
return -1;
}
+ has_handle_id = true;
}
} else {
/* For queue/node scope, id is required */
@@ -179,6 +200,7 @@ static int do_cmd(int argc, char **argv, int cmd)
fprintf(stderr, "Invalid handle id\n");
return -1;
}
+ has_handle_id = true;
}
} else {
fprintf(stderr, "What is \"%s\"\n", *argv);
@@ -195,19 +217,32 @@ static int do_cmd(int argc, char **argv, int cmd)
if (!handle_present)
missarg("handle");
- if (cmd == NET_SHAPER_CMD_SET && bw_max_bps == 0)
- missarg("bw-max");
+ if (cmd == NET_SHAPER_CMD_SET && !has_bw_min && !has_bw_max && !has_weight)
+ missarg("bw-min, bw-max, or weight");
addattr32(&req.n, sizeof(req), NET_SHAPER_A_IFINDEX, ifindex);
struct rtattr *handle = addattr_nest(&req.n, sizeof(req),
NET_SHAPER_A_HANDLE | NLA_F_NESTED);
addattr32(&req.n, sizeof(req), NET_SHAPER_A_HANDLE_SCOPE, handle_scope);
- addattr32(&req.n, sizeof(req), NET_SHAPER_A_HANDLE_ID, handle_id);
+ if (has_handle_id)
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_HANDLE_ID, handle_id);
addattr_nest_end(&req.n, handle);
- if (cmd == NET_SHAPER_CMD_SET)
- addattr64(&req.n, sizeof(req), NET_SHAPER_A_BW_MAX, bw_max_bps);
+ if (cmd == NET_SHAPER_CMD_SET) {
+ if (has_bw_min)
+ addattr64(&req.n, sizeof(req), NET_SHAPER_A_BW_MIN,
+ bw_min_bps);
+ if (has_bw_max)
+ addattr64(&req.n, sizeof(req), NET_SHAPER_A_BW_MAX,
+ bw_max_bps);
+ if (has_weight)
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_WEIGHT,
+ weight);
+ if (has_bw_min || has_bw_max)
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_METRIC,
+ NET_SHAPER_METRIC_BPS);
+ }
err = rtnl_talk(&gen_rth, &req.n, &answer);
if (err < 0) {
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH iproute2-next v2 3/6] netshaper: Extend show output with parent, bw-min and weight
2026-05-09 2:23 [PATCH iproute2-next v2 0/6] netshaper: Extend netshaper support Mohsin Bashir
2026-05-09 2:23 ` [PATCH iproute2-next v2 1/6] netshaper: Extract parse_scope() and parse_rate() helpers Mohsin Bashir
2026-05-09 2:23 ` [PATCH iproute2-next v2 2/6] netshaper: Add bw-min and weight parameter support Mohsin Bashir
@ 2026-05-09 2:23 ` Mohsin Bashir
2026-05-09 2:23 ` [PATCH iproute2-next v2 4/6] netshaper: Extract struct shaper_args and parse_shaper_arg() helper Mohsin Bashir
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Mohsin Bashir @ 2026-05-09 2:23 UTC (permalink / raw)
To: netdev; +Cc: dsahern, stephen, pabeni, kuba, ernis, mohsin.bashr
From: Mohsin Bashir <hmohsin@meta.com>
Extend print_netshaper_attrs() to display parent scope/id, bw-min,
and weight fields in the show output. Replace the switch-based
iteration with direct attribute checks for cleaner output formatting.
Use print_rate() for bandwidth display so that sub-Mbps rates
like 100kbit render correctly instead of truncating to 0 mbps.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Mohsin Bashir <hmohsin@meta.com>
---
netshaper/netshaper.c | 100 +++++++++++++++++++++++++++---------------
1 file changed, 65 insertions(+), 35 deletions(-)
diff --git a/netshaper/netshaper.c b/netshaper/netshaper.c
index 186076a1..83bfd2f6 100644
--- a/netshaper/netshaper.c
+++ b/netshaper/netshaper.c
@@ -71,53 +71,83 @@ static int parse_rate(const char *str, __u64 *rate_bps)
static void print_netshaper_attrs(struct nlmsghdr *answer)
{
- struct genlmsghdr *ghdr = NLMSG_DATA(answer);
+ struct rtattr *handle_tb[NET_SHAPER_A_HANDLE_MAX + 1] = {};
+ struct rtattr *parent_tb[NET_SHAPER_A_HANDLE_MAX + 1] = {};
int len = answer->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
+ struct genlmsghdr *ghdr = NLMSG_DATA(answer);
struct rtattr *tb[NET_SHAPER_A_MAX + 1] = {};
- struct rtattr *handle_tb[NET_SHAPER_A_HANDLE_MAX + 1] = {};
- __u32 bw_max_mbps, scope, id;
- __u64 bw_max_bps;
+ __u32 scope, id;
int ifindex;
parse_rtattr_flags(tb, NET_SHAPER_A_MAX,
(struct rtattr *)((char *)ghdr + GENL_HDRLEN),
len, NLA_F_NESTED);
- for (int i = 1; i <= NET_SHAPER_A_MAX; ++i) {
- if (!tb[i])
- continue;
- switch (i) {
- case NET_SHAPER_A_BW_MAX:
- bw_max_bps = rta_getattr_uint(tb[i]);
- bw_max_mbps = (bw_max_bps / 1000000);
+ if (tb[NET_SHAPER_A_IFINDEX]) {
+ ifindex = rta_getattr_u32(tb[NET_SHAPER_A_IFINDEX]);
+ print_color_string(PRINT_ANY, COLOR_IFNAME, "dev",
+ "dev: %s ", ll_index_to_name(ifindex));
+ }
- print_uint(PRINT_ANY, "bw-max", "bw-max: %u mbps\n",
- bw_max_mbps);
- break;
- case NET_SHAPER_A_IFINDEX:
- ifindex = rta_getattr_u32(tb[i]);
- print_color_string(PRINT_ANY, COLOR_IFNAME, "dev",
- "dev: %s\n",
- ll_index_to_name(ifindex));
- break;
- case NET_SHAPER_A_HANDLE:
- parse_rtattr_nested(handle_tb, NET_SHAPER_A_HANDLE_MAX,
- tb[NET_SHAPER_A_HANDLE]);
- if (handle_tb[NET_SHAPER_A_HANDLE_SCOPE]) {
- scope = rta_getattr_u32(handle_tb[NET_SHAPER_A_HANDLE_SCOPE]);
- print_string(PRINT_ANY, "scope",
- "scope: %s\n",
+ if (tb[NET_SHAPER_A_HANDLE]) {
+ parse_rtattr_nested(handle_tb, NET_SHAPER_A_HANDLE_MAX,
+ tb[NET_SHAPER_A_HANDLE]);
+ if (handle_tb[NET_SHAPER_A_HANDLE_SCOPE]) {
+ scope = rta_getattr_u32(handle_tb[NET_SHAPER_A_HANDLE_SCOPE]);
+ if (scope <= NET_SHAPER_SCOPE_MAX)
+ print_string(PRINT_ANY, "scope", "scope %s ",
net_shaper_scope_names[scope]);
- }
- if (handle_tb[NET_SHAPER_A_HANDLE_ID]) {
- id = rta_getattr_u32(handle_tb[NET_SHAPER_A_HANDLE_ID]);
- print_uint(PRINT_ANY, "id", "id: %u\n", id);
- }
- break;
- default:
- break;
+ else
+ print_uint(PRINT_ANY, "scope", "scope %u ", scope);
+ }
+ if (handle_tb[NET_SHAPER_A_HANDLE_ID]) {
+ id = rta_getattr_u32(handle_tb[NET_SHAPER_A_HANDLE_ID]);
+ print_uint(PRINT_ANY, "id", "id %u ", id);
}
}
+
+ if (tb[NET_SHAPER_A_PARENT]) {
+ parse_rtattr_nested(parent_tb, NET_SHAPER_A_HANDLE_MAX,
+ tb[NET_SHAPER_A_PARENT]);
+ if (parent_tb[NET_SHAPER_A_HANDLE_SCOPE]) {
+ scope = rta_getattr_u32(parent_tb[NET_SHAPER_A_HANDLE_SCOPE]);
+ if (scope <= NET_SHAPER_SCOPE_MAX)
+ print_string(PRINT_ANY, "parent-scope",
+ "parent scope %s ",
+ net_shaper_scope_names[scope]);
+ else
+ print_uint(PRINT_ANY, "parent-scope",
+ "parent scope %u ", scope);
+ }
+ if (parent_tb[NET_SHAPER_A_HANDLE_ID]) {
+ id = rta_getattr_u32(parent_tb[NET_SHAPER_A_HANDLE_ID]);
+ print_uint(PRINT_ANY, "parent-id", "id %u ", id);
+ }
+ }
+
+ if (tb[NET_SHAPER_A_BW_MAX]) {
+ __u64 bw = rta_getattr_uint(tb[NET_SHAPER_A_BW_MAX]);
+
+ print_string(PRINT_FP, NULL, "bw-max ", NULL);
+ print_rate(false, PRINT_ANY, "bw-max", "%s ",
+ bw / BITS_PER_BYTE);
+ }
+
+ if (tb[NET_SHAPER_A_BW_MIN]) {
+ __u64 bw = rta_getattr_uint(tb[NET_SHAPER_A_BW_MIN]);
+
+ print_string(PRINT_FP, NULL, "bw-min ", NULL);
+ print_rate(false, PRINT_ANY, "bw-min", "%s ",
+ bw / BITS_PER_BYTE);
+ }
+
+ if (tb[NET_SHAPER_A_WEIGHT]) {
+ __u32 weight = rta_getattr_u32(tb[NET_SHAPER_A_WEIGHT]);
+
+ print_uint(PRINT_ANY, "weight", "weight %u ", weight);
+ }
+
+ print_nl();
}
static int do_cmd(int argc, char **argv, int cmd)
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH iproute2-next v2 4/6] netshaper: Extract struct shaper_args and parse_shaper_arg() helper
2026-05-09 2:23 [PATCH iproute2-next v2 0/6] netshaper: Extend netshaper support Mohsin Bashir
` (2 preceding siblings ...)
2026-05-09 2:23 ` [PATCH iproute2-next v2 3/6] netshaper: Extend show output with parent, bw-min and weight Mohsin Bashir
@ 2026-05-09 2:23 ` Mohsin Bashir
2026-05-09 2:23 ` [PATCH iproute2-next v2 5/6] netshaper: Add group command for creating scheduling hierarchies Mohsin Bashir
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Mohsin Bashir @ 2026-05-09 2:23 UTC (permalink / raw)
To: netdev; +Cc: dsahern, stephen, pabeni, kuba, ernis, mohsin.bashr
From: Mohsin Bashir <hmohsin@meta.com>
Extract common shaper parameters (dev, bw-min, bw-max, weight) into
struct shaper_args and add parse_shaper_arg() to parse them. This
avoids duplicating argument parsing when the group command is added
in the next patch.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Mohsin Bashir <hmohsin@meta.com>
---
netshaper/netshaper.c | 111 +++++++++++++++++++++++++++---------------
1 file changed, 72 insertions(+), 39 deletions(-)
diff --git a/netshaper/netshaper.c b/netshaper/netshaper.c
index 83bfd2f6..9ccd09f4 100644
--- a/netshaper/netshaper.c
+++ b/netshaper/netshaper.c
@@ -69,6 +69,54 @@ static int parse_rate(const char *str, __u64 *rate_bps)
return 0;
}
+struct shaper_args {
+ __u64 bw_min_bps, bw_max_bps;
+ __u32 weight;
+ bool has_bw_min, has_bw_max, has_weight;
+ int ifindex;
+};
+
+#define SHAPER_ARGS_INIT { .ifindex = -1 }
+
+static int parse_shaper_arg(const char *key, int *argcp, char ***argvp,
+ struct shaper_args *args)
+{
+ int argc = *argcp;
+ char **argv = *argvp;
+
+ if (strcmp(key, "dev") == 0) {
+ NEXT_ARG();
+ args->ifindex = ll_name_to_index(*argv);
+ if (args->ifindex == 0) {
+ fprintf(stderr, "Device \"%s\" not found\n", *argv);
+ return -1;
+ }
+ } else if (strcmp(key, "bw-min") == 0) {
+ NEXT_ARG();
+ if (parse_rate(*argv, &args->bw_min_bps))
+ return -1;
+ args->has_bw_min = true;
+ } else if (strcmp(key, "bw-max") == 0) {
+ NEXT_ARG();
+ if (parse_rate(*argv, &args->bw_max_bps))
+ return -1;
+ args->has_bw_max = true;
+ } else if (strcmp(key, "weight") == 0) {
+ NEXT_ARG();
+ if (get_unsigned(&args->weight, *argv, 10)) {
+ fprintf(stderr, "Invalid weight value\n");
+ return -1;
+ }
+ args->has_weight = true;
+ } else {
+ return 0;
+ }
+
+ *argcp = argc;
+ *argvp = argv;
+ return 1;
+}
+
static void print_netshaper_attrs(struct nlmsghdr *answer)
{
struct rtattr *handle_tb[NET_SHAPER_A_HANDLE_MAX + 1] = {};
@@ -155,41 +203,25 @@ static int do_cmd(int argc, char **argv, int cmd)
GENL_REQUEST(req, 1024, genl_family, 0, NET_SHAPER_FAMILY_VERSION, cmd,
NLM_F_REQUEST | NLM_F_ACK);
- bool has_bw_min = false, has_bw_max = false, has_weight = false;
+ struct shaper_args args = SHAPER_ARGS_INIT;
int handle_scope = NET_SHAPER_SCOPE_UNSPEC;
- __u64 bw_min_bps = 0, bw_max_bps = 0;
- __u32 handle_id = 0, weight = 0;
bool handle_present = false;
bool has_handle_id = false;
struct nlmsghdr *answer;
- int err, ifindex = -1;
+ __u32 handle_id = 0;
+ int err, ret;
while (argc > 0) {
- if (strcmp(*argv, "dev") == 0) {
- NEXT_ARG();
- ifindex = ll_name_to_index(*argv);
- if (ifindex == 0) {
- fprintf(stderr, "Device \"%s\" not found\n", *argv);
- return -1;
- }
- } else if (strcmp(*argv, "bw-min") == 0) {
- NEXT_ARG();
- if (parse_rate(*argv, &bw_min_bps))
- return -1;
- has_bw_min = true;
- } else if (strcmp(*argv, "bw-max") == 0) {
- NEXT_ARG();
- if (parse_rate(*argv, &bw_max_bps))
- return -1;
- has_bw_max = true;
- } else if (strcmp(*argv, "weight") == 0) {
- NEXT_ARG();
- if (get_unsigned(&weight, *argv, 10)) {
- fprintf(stderr, "Invalid weight value\n");
- return -1;
- }
- has_weight = true;
- } else if (strcmp(*argv, "handle") == 0) {
+ ret = parse_shaper_arg(*argv, &argc, &argv, &args);
+ if (ret < 0)
+ return -1;
+ if (ret > 0) {
+ argc--;
+ argv++;
+ continue;
+ }
+
+ if (strcmp(*argv, "handle") == 0) {
handle_present = true;
NEXT_ARG();
@@ -241,16 +273,17 @@ static int do_cmd(int argc, char **argv, int cmd)
argv++;
}
- if (ifindex == -1)
+ if (args.ifindex == -1)
missarg("dev");
if (!handle_present)
missarg("handle");
- if (cmd == NET_SHAPER_CMD_SET && !has_bw_min && !has_bw_max && !has_weight)
+ if (cmd == NET_SHAPER_CMD_SET &&
+ !args.has_bw_min && !args.has_bw_max && !args.has_weight)
missarg("bw-min, bw-max, or weight");
- addattr32(&req.n, sizeof(req), NET_SHAPER_A_IFINDEX, ifindex);
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_IFINDEX, args.ifindex);
struct rtattr *handle = addattr_nest(&req.n, sizeof(req),
NET_SHAPER_A_HANDLE | NLA_F_NESTED);
@@ -260,16 +293,16 @@ static int do_cmd(int argc, char **argv, int cmd)
addattr_nest_end(&req.n, handle);
if (cmd == NET_SHAPER_CMD_SET) {
- if (has_bw_min)
+ if (args.has_bw_min)
addattr64(&req.n, sizeof(req), NET_SHAPER_A_BW_MIN,
- bw_min_bps);
- if (has_bw_max)
+ args.bw_min_bps);
+ if (args.has_bw_max)
addattr64(&req.n, sizeof(req), NET_SHAPER_A_BW_MAX,
- bw_max_bps);
- if (has_weight)
+ args.bw_max_bps);
+ if (args.has_weight)
addattr32(&req.n, sizeof(req), NET_SHAPER_A_WEIGHT,
- weight);
- if (has_bw_min || has_bw_max)
+ args.weight);
+ if (args.has_bw_min || args.has_bw_max)
addattr32(&req.n, sizeof(req), NET_SHAPER_A_METRIC,
NET_SHAPER_METRIC_BPS);
}
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH iproute2-next v2 5/6] netshaper: Add group command for creating scheduling hierarchies
2026-05-09 2:23 [PATCH iproute2-next v2 0/6] netshaper: Extend netshaper support Mohsin Bashir
` (3 preceding siblings ...)
2026-05-09 2:23 ` [PATCH iproute2-next v2 4/6] netshaper: Extract struct shaper_args and parse_shaper_arg() helper Mohsin Bashir
@ 2026-05-09 2:23 ` Mohsin Bashir
2026-05-09 2:23 ` [PATCH iproute2-next v2 6/6] netshaper: Update man page for new parameters and group command Mohsin Bashir
2026-05-09 16:16 ` [PATCH iproute2-next v2 0/6] netshaper: Extend netshaper support Stephen Hemminger
6 siblings, 0 replies; 8+ messages in thread
From: Mohsin Bashir @ 2026-05-09 2:23 UTC (permalink / raw)
To: netdev; +Cc: dsahern, stephen, pabeni, kuba, ernis, mohsin.bashr
From: Mohsin Bashir <hmohsin@meta.com>
Add the group command to create and update scheduling groups via the
NET_SHAPER_CMD_GROUP netlink operation. This enables building shaper
hierarchies by specifying a node handle, parent scope, rate parameters,
and a set of queue leaf shapers to attach.
The group command allows the node handle id to be omitted, letting
the kernel auto-assign one for new nodes. Only queue scope is
accepted for leaf shapers. Group handle and parent scopes are
validated to accept only node or netdev. Parent node scope
requires an explicit id since it must reference an existing node.
Common argument parsing (dev, bw-min, bw-max, weight) reuses the
parse_shaper_arg() helper introduced in the previous patch.
Example usage:
netshaper group dev foo handle scope node parent scope netdev \
bw-max 1gbit leaves scope queue id 0 scope queue id 1
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Mohsin Bashir <hmohsin@meta.com>
---
netshaper/netshaper.c | 220 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 219 insertions(+), 1 deletion(-)
diff --git a/netshaper/netshaper.c b/netshaper/netshaper.c
index 9ccd09f4..5b1726d3 100644
--- a/netshaper/netshaper.c
+++ b/netshaper/netshaper.c
@@ -31,10 +31,15 @@ static void usage(void)
fprintf(stderr,
"Usage: netshaper [ OPTIONS ] { COMMAND | help }\n"
"OPTIONS := { -V[ersion] | -c[olor] | -help }\n"
- "COMMAND := { set | get | delete } dev DEVNAME\n"
+ "COMMAND := { set | get | delete | group } dev DEVNAME\n"
" handle scope HANDLE_SCOPE [id HANDLE_ID]\n"
" [bw-min BW_MIN] [bw-max BW_MAX] [weight WEIGHT]\n"
"\n"
+ "netshaper group dev DEVNAME handle scope SCOPE [ id ID ]\n"
+ " parent scope SCOPE [ id ID ]\n"
+ " [ bw-min BW ] [ bw-max BW ] [ weight WEIGHT ]\n"
+ " leaves { scope SCOPE id ID } [ ... ]\n"
+ "\n"
"Where: DEVNAME := STRING\n"
" HANDLE_SCOPE := { netdev | queue | node }\n"
" HANDLE_ID := UINT (required for queue/node, optional for netdev)\n"
@@ -319,6 +324,217 @@ static int do_cmd(int argc, char **argv, int cmd)
return err;
}
+#define NET_SHAPER_MAX_LEAVES 256
+
+static int do_group(int argc, char **argv)
+{
+ GENL_REQUEST(req, 4096, genl_family, 0, NET_SHAPER_FAMILY_VERSION,
+ NET_SHAPER_CMD_GROUP, NLM_F_REQUEST | NLM_F_ACK);
+
+ struct shaper_args args = SHAPER_ARGS_INIT;
+ bool parsing_leaves = false, has_handle_id = false, has_parent_id = false;
+ int parent_scope = -1, num_leaves = 0;
+ int err, ret, handle_scope = NET_SHAPER_SCOPE_UNSPEC;
+ __u32 handle_id = 0, parent_id = 0;
+ struct nlmsghdr *answer;
+
+ struct {
+ int scope;
+ __u32 id;
+ } leaves[NET_SHAPER_MAX_LEAVES];
+
+ while (argc > 0) {
+ if (parsing_leaves) {
+ if (strcmp(*argv, "scope") == 0) {
+ int lscope;
+
+ NEXT_ARG();
+ lscope = parse_scope(*argv);
+ if (lscope < 0) {
+ fprintf(stderr, "Invalid leaf scope \"%s\"\n",
+ *argv);
+ return -1;
+ }
+ NEXT_ARG();
+ if (strcmp(*argv, "id") != 0) {
+ fprintf(stderr, "Expected \"id\" after leaf scope\n");
+ return -1;
+ }
+
+ NEXT_ARG();
+ if (num_leaves >= ARRAY_SIZE(leaves)) {
+ fprintf(stderr, "Too many leaves\n");
+ return -1;
+ }
+ leaves[num_leaves].scope = lscope;
+ if (get_unsigned(&leaves[num_leaves].id, *argv, 10)) {
+ fprintf(stderr, "Invalid leaf id\n");
+ return -1;
+ }
+ num_leaves++;
+ argc--;
+ argv++;
+ continue;
+ }
+ parsing_leaves = false;
+ }
+
+ ret = parse_shaper_arg(*argv, &argc, &argv, &args);
+ if (ret < 0)
+ return -1;
+ if (ret > 0) {
+ argc--;
+ argv++;
+ continue;
+ }
+
+ if (strcmp(*argv, "handle") == 0) {
+ NEXT_ARG();
+ if (strcmp(*argv, "scope") != 0) {
+ fprintf(stderr, "Expected \"scope\" after \"handle\"\n");
+ return -1;
+ }
+ NEXT_ARG();
+ handle_scope = parse_scope(*argv);
+ if (handle_scope < 0) {
+ fprintf(stderr, "Invalid handle scope \"%s\"\n",
+ *argv);
+ return -1;
+ }
+ if (handle_scope != NET_SHAPER_SCOPE_NODE &&
+ handle_scope != NET_SHAPER_SCOPE_NETDEV) {
+ fprintf(stderr, "Group handle scope must be \"node\" or \"netdev\"\n");
+ return -1;
+ }
+
+ if (argc > 1 && strcmp(argv[1], "id") == 0) {
+ NEXT_ARG();
+ NEXT_ARG();
+ if (get_unsigned(&handle_id, *argv, 10)) {
+ fprintf(stderr, "Invalid handle id\n");
+ return -1;
+ }
+ has_handle_id = true;
+ }
+ } else if (strcmp(*argv, "parent") == 0) {
+ NEXT_ARG();
+ if (strcmp(*argv, "scope") != 0) {
+ fprintf(stderr, "Expected \"scope\" after \"parent\"\n");
+ return -1;
+ }
+ NEXT_ARG();
+ parent_scope = parse_scope(*argv);
+ if (parent_scope < 0) {
+ fprintf(stderr, "Invalid parent scope \"%s\"\n",
+ *argv);
+ return -1;
+ }
+ if (parent_scope != NET_SHAPER_SCOPE_NODE &&
+ parent_scope != NET_SHAPER_SCOPE_NETDEV) {
+ fprintf(stderr, "Parent scope must be \"node\" or \"netdev\"\n");
+ return -1;
+ }
+
+ if (parent_scope == NET_SHAPER_SCOPE_NODE) {
+ NEXT_ARG();
+ if (strcmp(*argv, "id") != 0) {
+ fprintf(stderr, "What is \"%s\"\n", *argv);
+ usage();
+ return -1;
+ }
+ NEXT_ARG();
+ if (get_unsigned(&parent_id, *argv, 10)) {
+ fprintf(stderr, "Invalid parent id\n");
+ return -1;
+ }
+ has_parent_id = true;
+ } else if (argc > 1 && strcmp(argv[1], "id") == 0) {
+ NEXT_ARG();
+ NEXT_ARG();
+ if (get_unsigned(&parent_id, *argv, 10)) {
+ fprintf(stderr, "Invalid parent id\n");
+ return -1;
+ }
+ has_parent_id = true;
+ }
+ } else if (strcmp(*argv, "leaves") == 0) {
+ parsing_leaves = true;
+ argc--;
+ argv++;
+ continue;
+ } else {
+ fprintf(stderr, "What is \"%s\"\n", *argv);
+ usage();
+ return -1;
+ }
+ argc--;
+ argv++;
+ }
+
+ if (args.ifindex == -1)
+ missarg("dev");
+ if (handle_scope == NET_SHAPER_SCOPE_UNSPEC)
+ missarg("handle");
+ if (parent_scope < 0)
+ missarg("parent");
+ if (num_leaves == 0)
+ missarg("leaves");
+
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_IFINDEX, args.ifindex);
+
+ struct rtattr *parent = addattr_nest(&req.n, sizeof(req),
+ NET_SHAPER_A_PARENT | NLA_F_NESTED);
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_HANDLE_SCOPE, parent_scope);
+ if (has_parent_id)
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_HANDLE_ID, parent_id);
+ addattr_nest_end(&req.n, parent);
+
+ struct rtattr *handle = addattr_nest(&req.n, sizeof(req),
+ NET_SHAPER_A_HANDLE | NLA_F_NESTED);
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_HANDLE_SCOPE, handle_scope);
+ if (has_handle_id)
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_HANDLE_ID, handle_id);
+ addattr_nest_end(&req.n, handle);
+
+ if (args.has_bw_min)
+ addattr64(&req.n, sizeof(req), NET_SHAPER_A_BW_MIN,
+ args.bw_min_bps);
+ if (args.has_bw_max)
+ addattr64(&req.n, sizeof(req), NET_SHAPER_A_BW_MAX,
+ args.bw_max_bps);
+ if (args.has_weight)
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_WEIGHT, args.weight);
+
+ if (args.has_bw_min || args.has_bw_max)
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_METRIC,
+ NET_SHAPER_METRIC_BPS);
+
+ for (int i = 0; i < num_leaves; i++) {
+ struct rtattr *leaf, *leaf_handle;
+
+ leaf = addattr_nest(&req.n, sizeof(req),
+ NET_SHAPER_A_LEAVES | NLA_F_NESTED);
+ leaf_handle = addattr_nest(&req.n, sizeof(req),
+ NET_SHAPER_A_HANDLE | NLA_F_NESTED);
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_HANDLE_SCOPE,
+ leaves[i].scope);
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_HANDLE_ID,
+ leaves[i].id);
+ addattr_nest_end(&req.n, leaf_handle);
+ addattr_nest_end(&req.n, leaf);
+ }
+
+ err = rtnl_talk(&gen_rth, &req.n, &answer);
+ if (err < 0) {
+ printf("Kernel command failed: %d\n", err);
+ return err;
+ }
+
+ print_netshaper_attrs(answer);
+ free(answer);
+ return 0;
+}
+
int main(int argc, char **argv)
{
int color = default_color_opt();
@@ -363,6 +579,8 @@ int main(int argc, char **argv)
return do_cmd(argc - 1, argv + 1, NET_SHAPER_CMD_DELETE);
if (strcmp(*argv, "show") == 0)
return do_cmd(argc - 1, argv + 1, NET_SHAPER_CMD_GET);
+ if (strcmp(*argv, "group") == 0)
+ return do_group(argc - 1, argv + 1);
if (strcmp(*argv, "help") == 0) {
usage();
return 0;
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH iproute2-next v2 6/6] netshaper: Update man page for new parameters and group command
2026-05-09 2:23 [PATCH iproute2-next v2 0/6] netshaper: Extend netshaper support Mohsin Bashir
` (4 preceding siblings ...)
2026-05-09 2:23 ` [PATCH iproute2-next v2 5/6] netshaper: Add group command for creating scheduling hierarchies Mohsin Bashir
@ 2026-05-09 2:23 ` Mohsin Bashir
2026-05-09 16:16 ` [PATCH iproute2-next v2 0/6] netshaper: Extend netshaper support Stephen Hemminger
6 siblings, 0 replies; 8+ messages in thread
From: Mohsin Bashir @ 2026-05-09 2:23 UTC (permalink / raw)
To: netdev; +Cc: dsahern, stephen, pabeni, kuba, ernis, mohsin.bashr
From: Mohsin Bashir <hmohsin@meta.com>
Document bw-min, weight, and the group command in the netshaper
man page. Node id is required for set/show/delete but optional
for the group command. Leaves are documented as queue-only.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Mohsin Bashir <hmohsin@meta.com>
---
man/man8/netshaper.8 | 140 ++++++++++++++++++++++++++++++++++++-------
1 file changed, 119 insertions(+), 21 deletions(-)
diff --git a/man/man8/netshaper.8 b/man/man8/netshaper.8
index f5fe36cc..ed4f77fc 100644
--- a/man/man8/netshaper.8
+++ b/man/man8/netshaper.8
@@ -25,8 +25,12 @@ netshaper \- show / manipulate network device hardware shaping configuration
.IR HANDLE_SCOPE
.RI "[ " id
.IR HANDLE_ID " ]"
-.B bw-max
-.IR BW_MAX
+.RI "[ " bw-min
+.IR BW_MIN " ]"
+.RI "[ " bw-max
+.IR BW_MAX " ]"
+.RI "[ " weight
+.IR WEIGHT " ]"
.ti -8
.B "netshaper" " { " show " | " delete " }"
@@ -37,6 +41,27 @@ netshaper \- show / manipulate network device hardware shaping configuration
.RI "[ " id
.IR HANDLE_ID " ]"
+.ti -8
+.B "netshaper group"
+.B dev
+.IR DEV
+.B handle scope
+.IR HANDLE_SCOPE
+.RI "[ " id
+.IR HANDLE_ID " ]"
+.B parent scope
+.IR PARENT_SCOPE
+.RI "[ " id
+.IR PARENT_ID " ]"
+.RI "[ " bw-min
+.IR BW_MIN " ]"
+.RI "[ " bw-max
+.IR BW_MAX " ]"
+.RI "[ " weight
+.IR WEIGHT " ]"
+.B leaves
+.BI "{ scope " SCOPE " id " ID " } [ ... ]"
+
.SH DESCRIPTION
.B netshaper
allows configuration and management of hardware rate limiting (shaping) capabilities
@@ -69,7 +94,11 @@ parameter is required and specifies the queue number.
Shapers representing scheduling groups that can be placed at arbitrary
locations in the scheduling tree. The
.I id
-parameter is required.
+parameter is required for
+.BR set ", " show ", and " delete
+commands. For the
+.B group
+command it is optional; if omitted, the kernel auto-assigns one.
.SH COMMANDS
@@ -77,16 +106,18 @@ parameter is required.
.B netshaper set
- Create or update a shaper configuration
-Creates or updates a shaper with the specified parameters. The
+Creates or updates a shaper with the specified parameters. At least one of
+.BR bw-min ", " bw-max ", or " weight
+must be provided. The
.I id
-parameter is optional for netdev scope but required for all other scopes.
+parameter is required for queue and node scopes and optional for netdev scope.
.SS
.B netshaper show
- Display shaper information
Shows the current configuration of the specified shaper, including bandwidth
-limits and device information.
+limits, weight, and parent information.
.SS
.B netshaper delete
@@ -94,6 +125,15 @@ limits and device information.
Removes the specified shaper configuration from the device.
+.SS
+.B netshaper group
+- Create a scheduling hierarchy
+
+Creates a scheduling group by binding one or more leaf shapers to a parent
+node in a single operation. The command specifies the group node's handle,
+its parent, optional bandwidth and weight parameters, and the set of leaf
+shapers to attach.
+
.SH PARAMETERS
.TP
@@ -115,21 +155,51 @@ or
.TP
.BI id " HANDLE_ID"
-Numeric identifier for the shaper. Optional for
-.B netdev
-scope (defaults to 0), required for
+Numeric identifier for the shaper. Required for
.B queue
and
.B node
-scopes.
+scopes. Optional for
+.B netdev
+scope (defaults to 0). For the
+.B group
+command, node
+.I id
+may be omitted to let the kernel auto-assign one.
.RE
.TP
-.BI bw-max " BW_MAX"
-Maximum bandwidth limit for the shaper. Accepts values with suffixes:
+.BI bw-min " BW_MIN"
+Minimum guaranteed bandwidth for the shaper. Accepts values with suffixes:
.BR kbit ", " mbit ", " gbit
for kilobits, megabits, and gigabits per second respectively.
+.TP
+.BI bw-max " BW_MAX"
+Maximum bandwidth limit for the shaper. Accepts the same suffixes as
+.BR bw-min .
+
+.TP
+.BI weight " WEIGHT"
+Scheduling weight for the shaper, used for weighted fair sharing among
+siblings under the same parent node. Value is an unsigned integer.
+
+.TP
+.B parent
+Defines the parent node for the
+.B group
+command. Uses the same
+.BI scope " / " id
+syntax as
+.BR handle .
+
+.TP
+.B leaves
+Specifies one or more leaf shapers to attach to the group. Each leaf
+is given as
+.BI "scope " SCOPE " id " ID\fR.
+Multiple leaves can be specified in sequence.
+
.SH OPTIONS
.TP
@@ -156,17 +226,27 @@ Display usage information and exit.
.SH EXAMPLES
.TP
-.B Example 1: Create a device-level shaper (ID optional)
+.B Example 1: Create a device-level shaper
.nf
# netshaper set dev foo handle scope netdev bw-max 10gbit
.fi
.RS
-Creates a netdev-scoped shaper with default id 0 and sets the maximum
-bandwidth to 10 gigabits per second.
+Creates a netdev-scoped shaper with maximum bandwidth of 10 Gbit/s.
.RE
.TP
-.B Example 2: Show shaper configuration
+.B Example 2: Set bandwidth and weight on a queue shaper
+.nf
+# netshaper set dev foo handle scope queue id 0 \e
+ bw-min 1gbit bw-max 5gbit weight 10
+.fi
+.RS
+Creates a queue-scoped shaper on queue 0 with minimum/maximum bandwidth
+limits and a scheduling weight.
+.RE
+
+.TP
+.B Example 3: Show shaper configuration
.nf
# netshaper show dev foo handle scope netdev
.fi
@@ -175,9 +255,20 @@ Displays the current shaper configuration for the specified device and handle.
.RE
.TP
-.B Example 3: Delete shaper configuration
+.B Example 4: Create a scheduling hierarchy with group
.nf
-# netshaper delete dev eth0 handle scope netdev
+# netshaper group dev foo handle scope node parent scope netdev \e
+ bw-max 10gbit leaves scope queue id 0 scope queue id 1
+.fi
+.RS
+Creates a node shaper under the netdev parent with a 10 Gbit/s cap,
+grouping queues 0 and 1 as leaves.
+.RE
+
+.TP
+.B Example 5: Delete shaper configuration
+.nf
+# netshaper delete dev foo handle scope netdev
.fi
.RS
Removes the specified shaper configuration.
@@ -197,15 +288,20 @@ and
.B node
scopes, the
.I id
-parameter is required.
+parameter is required. For the
+.B group
+command, node
+.I id
+may be omitted to let the kernel auto-assign one.
.IP \(bu
Bandwidth values support standard suffixes:
.BR kbit " (kilobits per second), "
.BR mbit " (megabits per second), "
.BR gbit " (gigabits per second)."
.IP \(bu
-This command currently supports basic shaper operations. Additional
-functionality will be added as requirements are identified.
+The
+.B group
+command creates a node and attaches leaves in a single atomic operation.
.SH SEE ALSO
.BR ip (8),
@@ -214,3 +310,5 @@ functionality will be added as requirements are identified.
.SH AUTHOR
Erni Sri Satya Vennela <ernis@linux.microsoft.com>
+.br
+Mohsin Bashir <hmohsin@meta.com>
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH iproute2-next v2 0/6] netshaper: Extend netshaper support
2026-05-09 2:23 [PATCH iproute2-next v2 0/6] netshaper: Extend netshaper support Mohsin Bashir
` (5 preceding siblings ...)
2026-05-09 2:23 ` [PATCH iproute2-next v2 6/6] netshaper: Update man page for new parameters and group command Mohsin Bashir
@ 2026-05-09 16:16 ` Stephen Hemminger
6 siblings, 0 replies; 8+ messages in thread
From: Stephen Hemminger @ 2026-05-09 16:16 UTC (permalink / raw)
To: Mohsin Bashir; +Cc: netdev, dsahern, pabeni, kuba, ernis
On Fri, 8 May 2026 19:23:47 -0700
Mohsin Bashir <mohsin.bashr@gmail.com> wrote:
> From: Mohsin Bashir <hmohsin@meta.com>
>
> This series extends the netshaper CLI with missing parameter support
> and adds the group command for building scheduling hierarchies.
>
> The existing netshaper tool only supports setting bw-max on individual
> shapers. This series adds the remaining shaper attributes (bw-min,
> weight) needed for TX scheduling, and introduces the group command
> which ties leaf shapers to a parent node in a single operation.
>
> Changelog:
>
> Patch 1:
> - parse_scope(): use loop over net_shaper_scope_names[]
> instead of hardcoded string comparisons
> - parse_rate(): replace magic number 8 with BITS_PER_BYTE
>
> Patch 2:
> - Add has_bw_min/has_bw_max/has_weight booleans so zero-valued
> attrs are correctly sent
> - Add has_handle_id; only send handle id when explicitly provided
> - Keep node id required in do_cmd(); only netdev scope has
> optional id
> - Only emit rate/weight attrs for set command, not show/delete
> - Validate ll_name_to_index() return, error on unknown device
>
> Patch 3:
> - Use print_rate() for bandwidth display instead of truncating
> to integer Mbps
>
> Patch 4 (new):
> - Extract struct shaper_args and parse_shaper_arg() helper to
> share argument parsing between do_cmd() and do_group()
>
> Patch 5 (was v1 patch 5, v1 patch 4 dropped):
> - Reuse parse_shaper_arg() for common argument parsing
> - Validate group handle and parent scopes are node or netdev
> - Require parent id when parent scope is node
> - Fold optional node handle id into do_group() only
> - Define NET_SHAPER_MAX_LEAVES (256) for leaves array size
>
> Patch 6:
> - Document node id as required for set/show/delete, optional
> only for group
> - Add author to man page
>
> V1: https://lore.kernel.org/netdev/20260501011611.3533573-1-mohsin.bashr@gmail.com/
>
> Mohsin Bashir (6):
> netshaper: Extract parse_scope() and parse_rate() helpers
> netshaper: Add bw-min and weight parameter support
> netshaper: Extend show output with parent, bw-min and weight
> netshaper: Extract struct shaper_args and parse_shaper_arg() helper
> netshaper: Add group command for creating scheduling hierarchies
> netshaper: Update man page for new parameters and group command
>
> include/utils.h | 1 +
> man/man8/netshaper.8 | 140 ++++++++++--
> netshaper/netshaper.c | 485 ++++++++++++++++++++++++++++++++++--------
> 3 files changed, 522 insertions(+), 104 deletions(-)
>
Automated AI review is not setup for iproute2-next so ran it manually.
Subject: Re: [PATCH iproute2-next v2 0/6] netshaper: Extend netshaper support
On Fri, 8 May 2026, Mohsin Bashir wrote:
> This series extends the netshaper CLI with missing parameter support
> and adds the group command for building scheduling hierarchies.
Thanks for the patches. One issue found in patch 5:
On Fri, 8 May 2026 19:23:52 -0700, Mohsin Bashir wrote:
> +static int do_group(int argc, char **argv)
> +{
[...]
> + err = rtnl_talk(&gen_rth, &req.n, &answer);
> + if (err < 0) {
> + printf("Kernel command failed: %d\n", err);
> + return err;
> + }
This error message uses printf() to write to stdout, which will corrupt
JSON output if enabled. Error messages must always go to stderr to preserve
the integrity of JSON output when using the -json flag.
This should be:
fprintf(stderr, "Kernel command failed: %d\n", err);
The rest of the series looks good. All other error messages correctly
use fprintf(stderr, ...), the new parse_scope() helper properly uses
strcmp() instead of matches(), and the print_rate() usage for bandwidth
display is a nice improvement over the previous truncated integer Mbps
display.
With that fix:
Reviewed-by: Claude Assistant <noreply@anthropic.com>
^ permalink raw reply [flat|nested] 8+ messages in thread