forked from Minki/linux
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 59cc1f61f0
("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.net
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
5e8456fdd2
commit
49b499718f
@ -92,7 +92,7 @@ int unregister_qdisc(struct Qdisc_ops *qops);
|
||||
void qdisc_get_default(char *id, size_t len);
|
||||
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);
|
||||
struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle);
|
||||
struct Qdisc *qdisc_lookup_class(struct net_device *dev, u32 handle);
|
||||
|
@ -66,6 +66,7 @@ struct Qdisc {
|
||||
#define TCQ_F_NOPARENT 0x40 /* root of its hierarchy :
|
||||
* qdisc_tree_decrease_qlen() should stop.
|
||||
*/
|
||||
#define TCQ_F_INVISIBLE 0x80 /* invisible by default in dump */
|
||||
u32 limit;
|
||||
const struct Qdisc_ops *ops;
|
||||
struct qdisc_size_table __rcu *stab;
|
||||
|
@ -545,6 +545,7 @@ enum {
|
||||
TCA_STATS2,
|
||||
TCA_STAB,
|
||||
TCA_PAD,
|
||||
TCA_DUMP_INVISIBLE,
|
||||
__TCA_MAX
|
||||
};
|
||||
|
||||
|
@ -274,7 +274,7 @@ static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
|
||||
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)) {
|
||||
struct Qdisc *root = qdisc_dev(q)->qdisc;
|
||||
@ -282,6 +282,8 @@ void qdisc_hash_add(struct Qdisc *q)
|
||||
WARN_ON_ONCE(root == &noop_qdisc);
|
||||
ASSERT_RTNL();
|
||||
hash_add_rcu(qdisc_dev(q)->qdisc_hash, &q->hash, q->handle);
|
||||
if (invisible)
|
||||
q->flags |= TCQ_F_INVISIBLE;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(qdisc_hash_add);
|
||||
@ -1003,7 +1005,7 @@ static struct Qdisc *qdisc_create(struct net_device *dev,
|
||||
goto err_out4;
|
||||
}
|
||||
|
||||
qdisc_hash_add(sch);
|
||||
qdisc_hash_add(sch, false);
|
||||
|
||||
return sch;
|
||||
}
|
||||
@ -1401,9 +1403,14 @@ nla_put_failure:
|
||||
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,
|
||||
@ -1417,12 +1424,12 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb,
|
||||
if (!skb)
|
||||
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,
|
||||
0, RTM_DELQDISC) < 0)
|
||||
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,
|
||||
old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0)
|
||||
goto err_out;
|
||||
@ -1439,7 +1446,8 @@ err_out:
|
||||
|
||||
static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
|
||||
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;
|
||||
struct Qdisc *q;
|
||||
@ -1452,7 +1460,7 @@ 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) &&
|
||||
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) <= 0)
|
||||
@ -1474,7 +1482,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
|
||||
q_idx++;
|
||||
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,
|
||||
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||
RTM_NEWQDISC) <= 0)
|
||||
@ -1496,12 +1504,21 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
int idx, q_idx;
|
||||
int s_idx, s_q_idx;
|
||||
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_q_idx = q_idx = cb->args[1];
|
||||
|
||||
idx = 0;
|
||||
ASSERT_RTNL();
|
||||
|
||||
err = nlmsg_parse(nlh, sizeof(*tcm), tca, TCA_MAX, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for_each_netdev(net, dev) {
|
||||
struct netdev_queue *dev_queue;
|
||||
|
||||
@ -1512,13 +1529,14 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
q_idx = 0;
|
||||
|
||||
if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx,
|
||||
true) < 0)
|
||||
true, tca[TCA_DUMP_INVISIBLE]) < 0)
|
||||
goto done;
|
||||
|
||||
dev_queue = dev_ingress_queue(dev);
|
||||
if (dev_queue &&
|
||||
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;
|
||||
|
||||
cont:
|
||||
@ -1762,7 +1780,7 @@ static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb,
|
||||
{
|
||||
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 ||
|
||||
(tcm->tcm_parent &&
|
||||
TC_H_MAJ(tcm->tcm_parent) != q->handle)) {
|
||||
|
@ -1161,6 +1161,8 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt)
|
||||
sch->handle);
|
||||
if (!q->link.q)
|
||||
q->link.q = &noop_qdisc;
|
||||
else
|
||||
qdisc_hash_add(q->link.q, true);
|
||||
|
||||
q->link.priority = 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
|
||||
cl->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid);
|
||||
if (!cl->q)
|
||||
cl->q = &noop_qdisc;
|
||||
else
|
||||
qdisc_hash_add(cl->q, true);
|
||||
|
||||
cl->common.classid = classid;
|
||||
cl->tparent = parent;
|
||||
cl->qdisc = sch;
|
||||
|
@ -117,6 +117,8 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
|
||||
&pfifo_qdisc_ops, classid);
|
||||
if (cl->qdisc == NULL)
|
||||
cl->qdisc = &noop_qdisc;
|
||||
else
|
||||
qdisc_hash_add(cl->qdisc, true);
|
||||
|
||||
if (tca[TCA_RATE]) {
|
||||
err = gen_replace_estimator(&cl->bstats, NULL, &cl->rate_est,
|
||||
|
@ -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);
|
||||
if (p->q == NULL)
|
||||
p->q = &noop_qdisc;
|
||||
else
|
||||
qdisc_hash_add(p->q, true);
|
||||
|
||||
pr_debug("%s: qdisc %p\n", __func__, p->q);
|
||||
|
||||
|
@ -795,7 +795,7 @@ static void attach_default_qdiscs(struct net_device *dev)
|
||||
}
|
||||
#ifdef CONFIG_NET_SCHED
|
||||
if (dev->qdisc)
|
||||
qdisc_hash_add(dev->qdisc);
|
||||
qdisc_hash_add(dev->qdisc, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1066,6 +1066,8 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
|
||||
&pfifo_qdisc_ops, classid);
|
||||
if (cl->qdisc == NULL)
|
||||
cl->qdisc = &noop_qdisc;
|
||||
else
|
||||
qdisc_hash_add(cl->qdisc, true);
|
||||
INIT_LIST_HEAD(&cl->children);
|
||||
cl->vt_tree = RB_ROOT;
|
||||
cl->cf_tree = RB_ROOT;
|
||||
@ -1425,6 +1427,8 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
|
||||
sch->handle);
|
||||
if (q->root.qdisc == NULL)
|
||||
q->root.qdisc = &noop_qdisc;
|
||||
else
|
||||
qdisc_hash_add(q->root.qdisc, true);
|
||||
INIT_LIST_HEAD(&q->root.children);
|
||||
q->root.vt_tree = RB_ROOT;
|
||||
q->root.cf_tree = RB_ROOT;
|
||||
|
@ -1460,6 +1460,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
|
||||
qdisc_class_hash_insert(&q->clhash, &cl->common);
|
||||
if (parent)
|
||||
parent->children++;
|
||||
if (cl->un.leaf.q != &noop_qdisc)
|
||||
qdisc_hash_add(cl->un.leaf.q, true);
|
||||
} else {
|
||||
if (tca[TCA_RATE]) {
|
||||
err = gen_replace_estimator(&cl->bstats, NULL,
|
||||
|
@ -84,7 +84,7 @@ static void mq_attach(struct Qdisc *sch)
|
||||
qdisc_destroy(old);
|
||||
#ifdef CONFIG_NET_SCHED
|
||||
if (ntx < dev->real_num_tx_queues)
|
||||
qdisc_hash_add(qdisc);
|
||||
qdisc_hash_add(qdisc, false);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ static void mqprio_attach(struct Qdisc *sch)
|
||||
if (old)
|
||||
qdisc_destroy(old);
|
||||
if (ntx < dev->real_num_tx_queues)
|
||||
qdisc_hash_add(qdisc);
|
||||
qdisc_hash_add(qdisc, false);
|
||||
}
|
||||
kfree(priv->qdiscs);
|
||||
priv->qdiscs = NULL;
|
||||
|
@ -217,6 +217,8 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt)
|
||||
sch_tree_lock(sch);
|
||||
old = q->queues[i];
|
||||
q->queues[i] = child;
|
||||
if (child != &noop_qdisc)
|
||||
qdisc_hash_add(child, true);
|
||||
|
||||
if (old != &noop_qdisc) {
|
||||
qdisc_tree_reduce_backlog(old,
|
||||
|
@ -192,8 +192,11 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt)
|
||||
qdisc_destroy(child);
|
||||
}
|
||||
|
||||
for (i = oldbands; i < q->bands; i++)
|
||||
for (i = oldbands; i < q->bands; i++) {
|
||||
q->queues[i] = queues[i];
|
||||
if (q->queues[i] != &noop_qdisc)
|
||||
qdisc_hash_add(q->queues[i], true);
|
||||
}
|
||||
|
||||
sch_tree_unlock(sch);
|
||||
return 0;
|
||||
|
@ -494,6 +494,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
|
||||
goto destroy_class;
|
||||
}
|
||||
|
||||
if (cl->qdisc != &noop_qdisc)
|
||||
qdisc_hash_add(cl->qdisc, true);
|
||||
sch_tree_lock(sch);
|
||||
qdisc_class_hash_insert(&q->clhash, &cl->common);
|
||||
sch_tree_unlock(sch);
|
||||
|
@ -191,6 +191,8 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt)
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
if (child != &noop_qdisc)
|
||||
qdisc_hash_add(child, true);
|
||||
sch_tree_lock(sch);
|
||||
q->flags = ctl->flags;
|
||||
q->limit = ctl->limit;
|
||||
|
@ -513,6 +513,8 @@ static int sfb_change(struct Qdisc *sch, struct nlattr *opt)
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
if (child != &noop_qdisc)
|
||||
qdisc_hash_add(child, true);
|
||||
sch_tree_lock(sch);
|
||||
|
||||
qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen,
|
||||
|
@ -396,6 +396,8 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt)
|
||||
q->qdisc->qstats.backlog);
|
||||
qdisc_destroy(q->qdisc);
|
||||
q->qdisc = child;
|
||||
if (child != &noop_qdisc);
|
||||
qdisc_hash_add(child, true);
|
||||
}
|
||||
q->limit = qopt->limit;
|
||||
if (tb[TCA_TBF_PBURST])
|
||||
|
Loading…
Reference in New Issue
Block a user