Commit 49b49971 authored by Jiri Kosina's avatar Jiri Kosina Committed by David S. Miller

net: sched: make default fifo qdiscs appear in the dump

The original reason [1] for having hidden qdiscs (potential scalability
issues in qdisc_match_from_root() with single linked list in case of large
amount of qdiscs) has been invalidated by 59cc1f61 ("net: sched: convert
qdisc linked list to hashtable").

This allows us for bringing more clarity and determinism into the dump by
making default pfifo qdiscs visible.

We're not turning this on by default though, at it was deemed [2] too
intrusive / unnecessary change of default behavior towards userspace.
Instead, TCA_DUMP_INVISIBLE netlink attribute is introduced, which allows
applications to request complete qdisc hierarchy dump, including the
ones that have always been implicit/invisible.

Singleton noop_qdisc stays invisible, as teaching the whole infrastructure
about singletons would require quite some surgery with very little gain
(seeing no qdisc or seeing noop qdisc in the dump is probably setting
the same user expectation).

[1] http://lkml.kernel.org/r/1460732328.10638.74.camel@edumazet-glaptop3.roam.corp.google.com
[2] http://lkml.kernel.org/r/20161021.105935.1907696543877061916.davem@davemloft.netSigned-off-by: default avatarJiri Kosina <jkosina@suse.cz>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5e8456fd
...@@ -92,7 +92,7 @@ int unregister_qdisc(struct Qdisc_ops *qops); ...@@ -92,7 +92,7 @@ int unregister_qdisc(struct Qdisc_ops *qops);
void qdisc_get_default(char *id, size_t len); void qdisc_get_default(char *id, size_t len);
int qdisc_set_default(const char *id); int qdisc_set_default(const char *id);
void qdisc_hash_add(struct Qdisc *q); void qdisc_hash_add(struct Qdisc *q, bool invisible);
void qdisc_hash_del(struct Qdisc *q); void qdisc_hash_del(struct Qdisc *q);
struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle); struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle);
struct Qdisc *qdisc_lookup_class(struct net_device *dev, u32 handle); struct Qdisc *qdisc_lookup_class(struct net_device *dev, u32 handle);
......
...@@ -66,6 +66,7 @@ struct Qdisc { ...@@ -66,6 +66,7 @@ struct Qdisc {
#define TCQ_F_NOPARENT 0x40 /* root of its hierarchy : #define TCQ_F_NOPARENT 0x40 /* root of its hierarchy :
* qdisc_tree_decrease_qlen() should stop. * qdisc_tree_decrease_qlen() should stop.
*/ */
#define TCQ_F_INVISIBLE 0x80 /* invisible by default in dump */
u32 limit; u32 limit;
const struct Qdisc_ops *ops; const struct Qdisc_ops *ops;
struct qdisc_size_table __rcu *stab; struct qdisc_size_table __rcu *stab;
......
...@@ -545,6 +545,7 @@ enum { ...@@ -545,6 +545,7 @@ enum {
TCA_STATS2, TCA_STATS2,
TCA_STAB, TCA_STAB,
TCA_PAD, TCA_PAD,
TCA_DUMP_INVISIBLE,
__TCA_MAX __TCA_MAX
}; };
......
...@@ -274,7 +274,7 @@ static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle) ...@@ -274,7 +274,7 @@ static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
return NULL; return NULL;
} }
void qdisc_hash_add(struct Qdisc *q) void qdisc_hash_add(struct Qdisc *q, bool invisible)
{ {
if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) { if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) {
struct Qdisc *root = qdisc_dev(q)->qdisc; struct Qdisc *root = qdisc_dev(q)->qdisc;
...@@ -282,6 +282,8 @@ void qdisc_hash_add(struct Qdisc *q) ...@@ -282,6 +282,8 @@ void qdisc_hash_add(struct Qdisc *q)
WARN_ON_ONCE(root == &noop_qdisc); WARN_ON_ONCE(root == &noop_qdisc);
ASSERT_RTNL(); ASSERT_RTNL();
hash_add_rcu(qdisc_dev(q)->qdisc_hash, &q->hash, q->handle); hash_add_rcu(qdisc_dev(q)->qdisc_hash, &q->hash, q->handle);
if (invisible)
q->flags |= TCQ_F_INVISIBLE;
} }
} }
EXPORT_SYMBOL(qdisc_hash_add); EXPORT_SYMBOL(qdisc_hash_add);
...@@ -1003,7 +1005,7 @@ static struct Qdisc *qdisc_create(struct net_device *dev, ...@@ -1003,7 +1005,7 @@ static struct Qdisc *qdisc_create(struct net_device *dev,
goto err_out4; goto err_out4;
} }
qdisc_hash_add(sch); qdisc_hash_add(sch, false);
return sch; return sch;
} }
...@@ -1401,9 +1403,14 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, ...@@ -1401,9 +1403,14 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
return -1; return -1;
} }
static bool tc_qdisc_dump_ignore(struct Qdisc *q) static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible)
{ {
return (q->flags & TCQ_F_BUILTIN) ? true : false; if (q->flags & TCQ_F_BUILTIN)
return true;
if ((q->flags & TCQ_F_INVISIBLE) && !dump_invisible)
return true;
return false;
} }
static int qdisc_notify(struct net *net, struct sk_buff *oskb, static int qdisc_notify(struct net *net, struct sk_buff *oskb,
...@@ -1417,12 +1424,12 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb, ...@@ -1417,12 +1424,12 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb,
if (!skb) if (!skb)
return -ENOBUFS; return -ENOBUFS;
if (old && !tc_qdisc_dump_ignore(old)) { if (old && !tc_qdisc_dump_ignore(old, false)) {
if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq, if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq,
0, RTM_DELQDISC) < 0) 0, RTM_DELQDISC) < 0)
goto err_out; goto err_out;
} }
if (new && !tc_qdisc_dump_ignore(new)) { if (new && !tc_qdisc_dump_ignore(new, false)) {
if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq, if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq,
old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0) old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0)
goto err_out; goto err_out;
...@@ -1439,7 +1446,8 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb, ...@@ -1439,7 +1446,8 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb,
static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
struct netlink_callback *cb, struct netlink_callback *cb,
int *q_idx_p, int s_q_idx, bool recur) int *q_idx_p, int s_q_idx, bool recur,
bool dump_invisible)
{ {
int ret = 0, q_idx = *q_idx_p; int ret = 0, q_idx = *q_idx_p;
struct Qdisc *q; struct Qdisc *q;
...@@ -1452,7 +1460,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, ...@@ -1452,7 +1460,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
if (q_idx < s_q_idx) { if (q_idx < s_q_idx) {
q_idx++; q_idx++;
} else { } else {
if (!tc_qdisc_dump_ignore(q) && if (!tc_qdisc_dump_ignore(q, dump_invisible) &&
tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWQDISC) <= 0) RTM_NEWQDISC) <= 0)
...@@ -1474,7 +1482,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, ...@@ -1474,7 +1482,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
q_idx++; q_idx++;
continue; continue;
} }
if (!tc_qdisc_dump_ignore(q) && if (!tc_qdisc_dump_ignore(q, dump_invisible) &&
tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWQDISC) <= 0) RTM_NEWQDISC) <= 0)
...@@ -1496,12 +1504,21 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1496,12 +1504,21 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
int idx, q_idx; int idx, q_idx;
int s_idx, s_q_idx; int s_idx, s_q_idx;
struct net_device *dev; struct net_device *dev;
const struct nlmsghdr *nlh = cb->nlh;
struct tcmsg *tcm = nlmsg_data(nlh);
struct nlattr *tca[TCA_MAX + 1];
int err;
s_idx = cb->args[0]; s_idx = cb->args[0];
s_q_idx = q_idx = cb->args[1]; s_q_idx = q_idx = cb->args[1];
idx = 0; idx = 0;
ASSERT_RTNL(); ASSERT_RTNL();
err = nlmsg_parse(nlh, sizeof(*tcm), tca, TCA_MAX, NULL);
if (err < 0)
return err;
for_each_netdev(net, dev) { for_each_netdev(net, dev) {
struct netdev_queue *dev_queue; struct netdev_queue *dev_queue;
...@@ -1512,13 +1529,14 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1512,13 +1529,14 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
q_idx = 0; q_idx = 0;
if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx, if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx,
true) < 0) true, tca[TCA_DUMP_INVISIBLE]) < 0)
goto done; goto done;
dev_queue = dev_ingress_queue(dev); dev_queue = dev_ingress_queue(dev);
if (dev_queue && if (dev_queue &&
tc_dump_qdisc_root(dev_queue->qdisc_sleeping, skb, cb, tc_dump_qdisc_root(dev_queue->qdisc_sleeping, skb, cb,
&q_idx, s_q_idx, false) < 0) &q_idx, s_q_idx, false,
tca[TCA_DUMP_INVISIBLE]) < 0)
goto done; goto done;
cont: cont:
...@@ -1762,7 +1780,7 @@ static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb, ...@@ -1762,7 +1780,7 @@ static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb,
{ {
struct qdisc_dump_args arg; struct qdisc_dump_args arg;
if (tc_qdisc_dump_ignore(q) || if (tc_qdisc_dump_ignore(q, false) ||
*t_p < s_t || !q->ops->cl_ops || *t_p < s_t || !q->ops->cl_ops ||
(tcm->tcm_parent && (tcm->tcm_parent &&
TC_H_MAJ(tcm->tcm_parent) != q->handle)) { TC_H_MAJ(tcm->tcm_parent) != q->handle)) {
......
...@@ -1161,6 +1161,8 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt) ...@@ -1161,6 +1161,8 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt)
sch->handle); sch->handle);
if (!q->link.q) if (!q->link.q)
q->link.q = &noop_qdisc; q->link.q = &noop_qdisc;
else
qdisc_hash_add(q->link.q, true);
q->link.priority = TC_CBQ_MAXPRIO - 1; q->link.priority = TC_CBQ_MAXPRIO - 1;
q->link.priority2 = TC_CBQ_MAXPRIO - 1; q->link.priority2 = TC_CBQ_MAXPRIO - 1;
...@@ -1600,6 +1602,9 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t ...@@ -1600,6 +1602,9 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
cl->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid); cl->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid);
if (!cl->q) if (!cl->q)
cl->q = &noop_qdisc; cl->q = &noop_qdisc;
else
qdisc_hash_add(cl->q, true);
cl->common.classid = classid; cl->common.classid = classid;
cl->tparent = parent; cl->tparent = parent;
cl->qdisc = sch; cl->qdisc = sch;
......
...@@ -117,6 +117,8 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid, ...@@ -117,6 +117,8 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
&pfifo_qdisc_ops, classid); &pfifo_qdisc_ops, classid);
if (cl->qdisc == NULL) if (cl->qdisc == NULL)
cl->qdisc = &noop_qdisc; cl->qdisc = &noop_qdisc;
else
qdisc_hash_add(cl->qdisc, true);
if (tca[TCA_RATE]) { if (tca[TCA_RATE]) {
err = gen_replace_estimator(&cl->bstats, NULL, &cl->rate_est, err = gen_replace_estimator(&cl->bstats, NULL, &cl->rate_est,
......
...@@ -368,6 +368,8 @@ static int dsmark_init(struct Qdisc *sch, struct nlattr *opt) ...@@ -368,6 +368,8 @@ static int dsmark_init(struct Qdisc *sch, struct nlattr *opt)
p->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, sch->handle); p->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, sch->handle);
if (p->q == NULL) if (p->q == NULL)
p->q = &noop_qdisc; p->q = &noop_qdisc;
else
qdisc_hash_add(p->q, true);
pr_debug("%s: qdisc %p\n", __func__, p->q); pr_debug("%s: qdisc %p\n", __func__, p->q);
......
...@@ -795,7 +795,7 @@ static void attach_default_qdiscs(struct net_device *dev) ...@@ -795,7 +795,7 @@ static void attach_default_qdiscs(struct net_device *dev)
} }
#ifdef CONFIG_NET_SCHED #ifdef CONFIG_NET_SCHED
if (dev->qdisc) if (dev->qdisc)
qdisc_hash_add(dev->qdisc); qdisc_hash_add(dev->qdisc, false);
#endif #endif
} }
......
...@@ -1066,6 +1066,8 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid, ...@@ -1066,6 +1066,8 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
&pfifo_qdisc_ops, classid); &pfifo_qdisc_ops, classid);
if (cl->qdisc == NULL) if (cl->qdisc == NULL)
cl->qdisc = &noop_qdisc; cl->qdisc = &noop_qdisc;
else
qdisc_hash_add(cl->qdisc, true);
INIT_LIST_HEAD(&cl->children); INIT_LIST_HEAD(&cl->children);
cl->vt_tree = RB_ROOT; cl->vt_tree = RB_ROOT;
cl->cf_tree = RB_ROOT; cl->cf_tree = RB_ROOT;
...@@ -1425,6 +1427,8 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt) ...@@ -1425,6 +1427,8 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
sch->handle); sch->handle);
if (q->root.qdisc == NULL) if (q->root.qdisc == NULL)
q->root.qdisc = &noop_qdisc; q->root.qdisc = &noop_qdisc;
else
qdisc_hash_add(q->root.qdisc, true);
INIT_LIST_HEAD(&q->root.children); INIT_LIST_HEAD(&q->root.children);
q->root.vt_tree = RB_ROOT; q->root.vt_tree = RB_ROOT;
q->root.cf_tree = RB_ROOT; q->root.cf_tree = RB_ROOT;
......
...@@ -1460,6 +1460,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, ...@@ -1460,6 +1460,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
qdisc_class_hash_insert(&q->clhash, &cl->common); qdisc_class_hash_insert(&q->clhash, &cl->common);
if (parent) if (parent)
parent->children++; parent->children++;
if (cl->un.leaf.q != &noop_qdisc)
qdisc_hash_add(cl->un.leaf.q, true);
} else { } else {
if (tca[TCA_RATE]) { if (tca[TCA_RATE]) {
err = gen_replace_estimator(&cl->bstats, NULL, err = gen_replace_estimator(&cl->bstats, NULL,
......
...@@ -84,7 +84,7 @@ static void mq_attach(struct Qdisc *sch) ...@@ -84,7 +84,7 @@ static void mq_attach(struct Qdisc *sch)
qdisc_destroy(old); qdisc_destroy(old);
#ifdef CONFIG_NET_SCHED #ifdef CONFIG_NET_SCHED
if (ntx < dev->real_num_tx_queues) if (ntx < dev->real_num_tx_queues)
qdisc_hash_add(qdisc); qdisc_hash_add(qdisc, false);
#endif #endif
} }
......
...@@ -175,7 +175,7 @@ static void mqprio_attach(struct Qdisc *sch) ...@@ -175,7 +175,7 @@ static void mqprio_attach(struct Qdisc *sch)
if (old) if (old)
qdisc_destroy(old); qdisc_destroy(old);
if (ntx < dev->real_num_tx_queues) if (ntx < dev->real_num_tx_queues)
qdisc_hash_add(qdisc); qdisc_hash_add(qdisc, false);
} }
kfree(priv->qdiscs); kfree(priv->qdiscs);
priv->qdiscs = NULL; priv->qdiscs = NULL;
......
...@@ -217,6 +217,8 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt) ...@@ -217,6 +217,8 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt)
sch_tree_lock(sch); sch_tree_lock(sch);
old = q->queues[i]; old = q->queues[i];
q->queues[i] = child; q->queues[i] = child;
if (child != &noop_qdisc)
qdisc_hash_add(child, true);
if (old != &noop_qdisc) { if (old != &noop_qdisc) {
qdisc_tree_reduce_backlog(old, qdisc_tree_reduce_backlog(old,
......
...@@ -192,8 +192,11 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt) ...@@ -192,8 +192,11 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt)
qdisc_destroy(child); qdisc_destroy(child);
} }
for (i = oldbands; i < q->bands; i++) for (i = oldbands; i < q->bands; i++) {
q->queues[i] = queues[i]; q->queues[i] = queues[i];
if (q->queues[i] != &noop_qdisc)
qdisc_hash_add(q->queues[i], true);
}
sch_tree_unlock(sch); sch_tree_unlock(sch);
return 0; return 0;
......
...@@ -494,6 +494,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, ...@@ -494,6 +494,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
goto destroy_class; goto destroy_class;
} }
if (cl->qdisc != &noop_qdisc)
qdisc_hash_add(cl->qdisc, true);
sch_tree_lock(sch); sch_tree_lock(sch);
qdisc_class_hash_insert(&q->clhash, &cl->common); qdisc_class_hash_insert(&q->clhash, &cl->common);
sch_tree_unlock(sch); sch_tree_unlock(sch);
......
...@@ -191,6 +191,8 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt) ...@@ -191,6 +191,8 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt)
return PTR_ERR(child); return PTR_ERR(child);
} }
if (child != &noop_qdisc)
qdisc_hash_add(child, true);
sch_tree_lock(sch); sch_tree_lock(sch);
q->flags = ctl->flags; q->flags = ctl->flags;
q->limit = ctl->limit; q->limit = ctl->limit;
......
...@@ -513,6 +513,8 @@ static int sfb_change(struct Qdisc *sch, struct nlattr *opt) ...@@ -513,6 +513,8 @@ static int sfb_change(struct Qdisc *sch, struct nlattr *opt)
if (IS_ERR(child)) if (IS_ERR(child))
return PTR_ERR(child); return PTR_ERR(child);
if (child != &noop_qdisc)
qdisc_hash_add(child, true);
sch_tree_lock(sch); sch_tree_lock(sch);
qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen, qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen,
......
...@@ -396,6 +396,8 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt) ...@@ -396,6 +396,8 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt)
q->qdisc->qstats.backlog); q->qdisc->qstats.backlog);
qdisc_destroy(q->qdisc); qdisc_destroy(q->qdisc);
q->qdisc = child; q->qdisc = child;
if (child != &noop_qdisc);
qdisc_hash_add(child, true);
} }
q->limit = qopt->limit; q->limit = qopt->limit;
if (tb[TCA_TBF_PBURST]) if (tb[TCA_TBF_PBURST])
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment