From: Mohsin Bashir <mohsin.bashr@gmail.com>
To: netdev@vger.kernel.org
Cc: dsahern@kernel.org, stephen@networkplumber.org,
pabeni@redhat.com, kuba@kernel.org, ernis@linux.microsoft.com,
mohsin.bashr@gmail.com
Subject: [PATCH iproute2-next 5/5] netshaper: Add group command for creating scheduling hierarchies
Date: Thu, 30 Apr 2026 18:16:11 -0700 [thread overview]
Message-ID: <20260501011611.3533573-6-mohsin.bashr@gmail.com> (raw)
In-Reply-To: <20260501011611.3533573-1-mohsin.bashr@gmail.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 leaf shapers (queues or nodes) to attach.
Example usage:
netshaper group dev eth0 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 <mohsin.bashr@gmail.com>
---
netshaper/netshaper.c | 206 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 205 insertions(+), 1 deletion(-)
diff --git a/netshaper/netshaper.c b/netshaper/netshaper.c
index fc68e735..7ec8220c 100644
--- a/netshaper/netshaper.c
+++ b/netshaper/netshaper.c
@@ -31,9 +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-max BW_MAX] [bw-min BW_MIN] [weight WEIGHT]\n"
+ "\n"
+ "netshaper group dev DEVNAME handle scope SCOPE [ id ID ]\n"
+ " parent scope SCOPE [ id ID ]\n"
+ " [ bw-max BW ] [ bw-min 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"
@@ -264,6 +270,202 @@ static int do_cmd(int argc, char **argv, int cmd)
return err;
}
+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);
+
+ bool has_bw_max = false, has_bw_min = false, has_weight = false;
+ bool parsing_leaves = false, has_hid = false, has_pid = false;
+ int parent_scope = -1, ifindex = -1, num_leaves = 0;
+ int err, handle_scope = NET_SHAPER_SCOPE_UNSPEC;
+ __u32 handle_id = 0, parent_id = 0, weight = 0;
+ __u64 bw_max_bps = 0, bw_min_bps = 0;
+ struct nlmsghdr *answer;
+
+ struct {
+ int scope;
+ __u32 id;
+ } leaves[128];
+
+ 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();
+ 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;
+ }
+
+ 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-max") == 0) {
+ NEXT_ARG();
+ if (parse_rate(*argv, &bw_max_bps))
+ return -1;
+ has_bw_max = true;
+ } 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, "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) {
+ 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 (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_hid = 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 (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_pid = 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 (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, 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_pid)
+ 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_hid)
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_HANDLE_ID, handle_id);
+ addattr_nest_end(&req.n, handle);
+
+ if (has_bw_max)
+ addattr64(&req.n, sizeof(req), NET_SHAPER_A_BW_MAX, bw_max_bps);
+ if (has_bw_min)
+ addattr64(&req.n, sizeof(req), NET_SHAPER_A_BW_MIN, bw_min_bps);
+ if (has_weight)
+ addattr32(&req.n, sizeof(req), NET_SHAPER_A_WEIGHT, weight);
+
+ if (has_bw_max || has_bw_min)
+ 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) {
+ fprintf(stderr, "Kernel command failed: %d\n", err);
+ return err;
+ }
+
+ print_netshaper_attrs(answer);
+ return 0;
+}
+
int main(int argc, char **argv)
{
int color = default_color_opt();
@@ -308,6 +510,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.52.0
next prev parent reply other threads:[~2026-05-01 1:16 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-01 1:16 [PATCH iproute2-next 0/5] netshaper: Extend netshaper support Mohsin Bashir
2026-05-01 1:16 ` [PATCH iproute2-next 1/5] netshaper: Extract parse_scope() and parse_rate() helpers Mohsin Bashir
2026-05-01 17:47 ` David Ahern
2026-05-01 1:16 ` [PATCH iproute2-next 2/5] netshaper: Add bw-min and weight parameter support Mohsin Bashir
2026-05-01 17:50 ` David Ahern
2026-05-01 1:16 ` [PATCH iproute2-next 3/5] netshaper: Extend show output with parent, bw-min and weight Mohsin Bashir
2026-05-01 1:16 ` [PATCH iproute2-next 4/5] netshaper: Make handle id optional for node scope Mohsin Bashir
2026-05-01 1:16 ` Mohsin Bashir [this message]
2026-05-01 15:17 ` [PATCH iproute2-next 0/5] netshaper: Extend netshaper support Stephen Hemminger
2026-05-01 21:13 ` Mohsin Bashir
2026-05-01 17:52 ` David Ahern
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260501011611.3533573-6-mohsin.bashr@gmail.com \
--to=mohsin.bashr@gmail.com \
--cc=dsahern@kernel.org \
--cc=ernis@linux.microsoft.com \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stephen@networkplumber.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.