Merge branch 'net-sched-tc_dump_qdisc-optimizations'

Eric Dumazet says:

====================
net/sched: tc_dump_qdisc() optimizations

Before converting tc_dump_qdisc() to RCU, we make the following changes:

- Use for_each_netdev_dump() instead of for_each_netdev()

- Only dump qdiscs of a single device at user space request.
====================

Link: https://patch.msgid.link/20260430023628.3216283-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-04-30 20:54:58 -07:00
commit edf4bee421

View File

@ -976,7 +976,7 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
out_nlmsg_trim:
nla_put_failure:
nlmsg_trim(skb, b);
return -1;
return -EMSGSIZE;
}
static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible)
@ -1836,11 +1836,13 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
if (q_idx < s_q_idx) {
q_idx++;
} else {
if (!tc_qdisc_dump_ignore(q, dump_invisible) &&
tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWQDISC, NULL) <= 0)
goto done;
if (!tc_qdisc_dump_ignore(q, dump_invisible))
ret = tc_fill_qdisc(skb, q, q->parent,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWQDISC, NULL);
if (ret < 0)
goto out;
q_idx++;
}
@ -1858,79 +1860,81 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
q_idx++;
continue;
}
if (!tc_qdisc_dump_ignore(q, dump_invisible) &&
tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWQDISC, NULL) <= 0)
goto done;
if (!tc_qdisc_dump_ignore(q, dump_invisible))
ret = tc_fill_qdisc(skb, q, q->parent,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWQDISC, NULL);
if (ret < 0)
goto out;
q_idx++;
}
out:
*q_idx_p = q_idx;
return ret;
done:
ret = -1;
goto 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;
const struct tcmsg *tcm;
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,
rtm_tca_policy, cb->extack);
if (err < 0)
return err;
tcm = nlmsg_data(nlh);
if (tcm->tcm_ifindex && !ctx->ifindex)
ctx->ifindex = tcm->tcm_ifindex;
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 (tcm->tcm_ifindex && ctx->ifindex != tcm->tcm_ifindex)
break;
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;
}
done:
cb->args[0] = idx;
cb->args[1] = q_idx;
return skb->len;
error_unlock:
netdev_unlock_ops(dev);
ctx->q_idx = q_idx;
return err;
}
@ -1987,15 +1991,16 @@ static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,
out_nlmsg_trim:
nla_put_failure:
nlmsg_trim(skb, b);
return -1;
return -EMSGSIZE;
}
static int tclass_notify(struct net *net, struct sk_buff *oskb,
struct nlmsghdr *n, struct Qdisc *q,
unsigned long cl, int event, struct netlink_ext_ack *extack)
{
struct sk_buff *skb;
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
struct sk_buff *skb;
int ret;
if (!rtnl_notify_needed(net, n->nlmsg_flags, RTNLGRP_TC))
return 0;
@ -2004,9 +2009,10 @@ static int tclass_notify(struct net *net, struct sk_buff *oskb,
if (!skb)
return -ENOBUFS;
if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event, extack) < 0) {
ret = tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event, extack);
if (ret < 0) {
kfree_skb(skb);
return -EINVAL;
return ret;
}
return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
@ -2017,17 +2023,19 @@ static int tclass_get_notify(struct net *net, struct sk_buff *oskb,
struct nlmsghdr *n, struct Qdisc *q,
unsigned long cl, struct netlink_ext_ack *extack)
{
struct sk_buff *skb;
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
struct sk_buff *skb;
int ret;
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb)
return -ENOBUFS;
if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, RTM_NEWTCLASS,
extack) < 0) {
ret = tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0,
RTM_NEWTCLASS, extack);
if (ret < 0) {
kfree_skb(skb);
return -EINVAL;
return ret;
}
return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
@ -2041,7 +2049,7 @@ static int tclass_del_notify(struct net *net,
struct netlink_ext_ack *extack)
{
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
struct sk_buff *skb;
struct sk_buff *skb = NULL;
int err = 0;
if (!cops->delete)
@ -2052,13 +2060,12 @@ static int tclass_del_notify(struct net *net,
if (!skb)
return -ENOBUFS;
if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0,
RTM_DELTCLASS, extack) < 0) {
err = tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0,
RTM_DELTCLASS, extack);
if (err < 0) {
kfree_skb(skb);
return -EINVAL;
return err;
}
} else {
skb = NULL;
}
err = cops->delete(q, cl, extack);