From: Eric Dumazet Date: Thu, 30 Apr 2026 02:36:27 +0000 (+0000) Subject: net/sched: switch tc_dump_qdisc() to for_each_netdev_dump() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=748bbef5fc6a6f5931103b68b67d345c67d0850b;p=thirdparty%2Flinux.git net/sched: switch tc_dump_qdisc() to for_each_netdev_dump() Use for_each_netdev_dump() instead of for_each_netdev(). This is more scalable, and will ease RCU conversion. This also offer better behavior when other threads are adding or deleting netevices concurrently. This enables dumping qdiscs for a single device at user space request in the following patch. Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260430023628.3216283-4-edumazet@google.com Signed-off-by: Jakub Kicinski --- diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 029e0f87ea9c..cf3d01dd6b26 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1877,18 +1877,17 @@ out: static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) { - struct net *net = sock_net(skb->sk); - int idx, q_idx; - int s_idx, s_q_idx; - struct net_device *dev; const struct nlmsghdr *nlh = cb->nlh; + struct net *net = sock_net(skb->sk); struct nlattr *tca[TCA_MAX + 1]; + struct { + unsigned long ifindex; + int q_idx; + } *ctx = (void *)cb->ctx; + struct net_device *dev; + int s_q_idx, q_idx; int err; - s_idx = cb->args[0]; - s_q_idx = q_idx = cb->args[1]; - - idx = 0; ASSERT_RTNL(); err = nlmsg_parse_deprecated(nlh, sizeof(struct tcmsg), tca, TCA_MAX, @@ -1896,42 +1895,39 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) if (err < 0) return err; - for_each_netdev(net, dev) { + s_q_idx = ctx->q_idx; + + for_each_netdev_dump(net, dev, ctx->ifindex) { struct netdev_queue *dev_queue; + struct Qdisc *q; - if (idx < s_idx) - goto cont; - if (idx > s_idx) - s_q_idx = 0; q_idx = 0; netdev_lock_ops(dev); - if (tc_dump_qdisc_root(rtnl_dereference(dev->qdisc), - skb, cb, &q_idx, s_q_idx, - true, tca[TCA_DUMP_INVISIBLE]) < 0) { - netdev_unlock_ops(dev); - goto done; - } + q = rtnl_dereference(dev->qdisc); + err = tc_dump_qdisc_root(q, skb, cb, &q_idx, s_q_idx, + true, tca[TCA_DUMP_INVISIBLE]); + if (err < 0) + goto error_unlock; dev_queue = dev_ingress_queue(dev); - if (dev_queue && - tc_dump_qdisc_root(rtnl_dereference(dev_queue->qdisc_sleeping), - skb, cb, &q_idx, s_q_idx, false, - tca[TCA_DUMP_INVISIBLE]) < 0) { - netdev_unlock_ops(dev); - goto done; + if (dev_queue) { + q = rtnl_dereference(dev_queue->qdisc_sleeping); + err = tc_dump_qdisc_root(q, skb, cb, &q_idx, s_q_idx, + false, tca[TCA_DUMP_INVISIBLE]); + if (err < 0) + goto error_unlock; } netdev_unlock_ops(dev); - -cont: - idx++; + s_q_idx = 0; } + return skb->len; -done: - cb->args[0] = idx; - cb->args[1] = q_idx; +error_unlock: + netdev_unlock_ops(dev); + ctx->q_idx = q_idx; - return skb->len; + return err; }