summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-05-01 06:54:58 +0300
committerJakub Kicinski <kuba@kernel.org>2026-05-01 06:54:59 +0300
commitedf4bee4215a173c0534d1851d7523d827149f9e (patch)
tree6f7deb5ae2ac1f8ce18b479ecd712dfa48edcafe
parentbc6a9b667f9ff66789c6c66b3bfe0ce5df972b67 (diff)
parent6f63b829632cbb4e7c4b6c55c79f5bb6652c06a8 (diff)
downloadlinux-edf4bee4215a173c0534d1851d7523d827149f9e.tar.xz
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>
-rw-r--r--net/sched/sch_api.c127
1 files changed, 67 insertions, 60 deletions
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index ed869a5ffc73..dd0edc9bd461 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -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;
}
+ 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;
}
@@ -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);