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; } - -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);