tools: ynl: add a sample for TC
authorJakub Kicinski <kuba@kernel.org>
Tue, 20 May 2025 16:19:16 +0000 (09:19 -0700)
committerJakub Kicinski <kuba@kernel.org>
Wed, 21 May 2025 19:38:23 +0000 (12:38 -0700)
Add a very simple TC dump sample with decoding of fq_codel attrs:

  # ./tools/net/ynl/samples/tc
        dummy0: fq_codel  limit: 10240p target: 5ms new_flow_cnt: 0

proving that selector passing (for stats) works.

Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
Link: https://patch.msgid.link/20250520161916.413298-13-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
tools/net/ynl/samples/.gitignore
tools/net/ynl/samples/tc.c [new file with mode: 0644]

index b3ec3fb0929f7f53f42def64d3fd612e47adaed0..7f5fca7682d74a32c027460596ded08cc8be44a9 100644 (file)
@@ -6,3 +6,4 @@ page-pool
 rt-addr
 rt-link
 rt-route
+tc
diff --git a/tools/net/ynl/samples/tc.c b/tools/net/ynl/samples/tc.c
new file mode 100644 (file)
index 0000000..0bfff0f
--- /dev/null
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stdio.h>
+#include <string.h>
+
+#include <ynl.h>
+
+#include <net/if.h>
+
+#include "tc-user.h"
+
+static void tc_qdisc_print(struct tc_getqdisc_rsp *q)
+{
+       char ifname[IF_NAMESIZE];
+       const char *name;
+
+       name = if_indextoname(q->_hdr.tcm_ifindex, ifname);
+       if (name)
+               printf("%16s: ", name);
+
+       if (q->_len.kind) {
+               printf("%s  ", q->kind);
+
+               if (q->options._present.fq_codel) {
+                       struct tc_fq_codel_attrs *fq_codel;
+                       struct tc_fq_codel_xstats *stats;
+
+                       fq_codel = &q->options.fq_codel;
+                       stats = q->stats2.app.fq_codel;
+
+                       if (fq_codel->_present.limit)
+                               printf("limit: %dp ", fq_codel->limit);
+                       if (fq_codel->_present.target)
+                               printf("target: %dms ",
+                                      (fq_codel->target + 500) / 1000);
+                       if (q->stats2.app._len.fq_codel)
+                               printf("new_flow_cnt: %d ",
+                                      stats->qdisc_stats.new_flow_count);
+               }
+       }
+
+       printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+       struct tc_getqdisc_req_dump *req;
+       struct tc_getqdisc_list *rsp;
+       struct ynl_error yerr;
+       struct ynl_sock *ys;
+
+       ys = ynl_sock_create(&ynl_tc_family, &yerr);
+       if (!ys) {
+               fprintf(stderr, "YNL: %s\n", yerr.msg);
+               return 1;
+       }
+
+       req = tc_getqdisc_req_dump_alloc();
+       if (!req)
+               goto err_destroy;
+
+       rsp = tc_getqdisc_dump(ys, req);
+       tc_getqdisc_req_dump_free(req);
+       if (!rsp)
+               goto err_close;
+
+       if (ynl_dump_empty(rsp))
+               fprintf(stderr, "Error: no addresses reported\n");
+       ynl_dump_foreach(rsp, qdisc)
+               tc_qdisc_print(qdisc);
+       tc_getqdisc_list_free(rsp);
+
+       ynl_sock_destroy(ys);
+       return 0;
+
+err_close:
+       fprintf(stderr, "YNL: %s\n", ys->err.msg);
+err_destroy:
+       ynl_sock_destroy(ys);
+       return 2;
+}