net: sched: make default fifo qdiscs appear in the dump
authorJiri Kosina <jkosina@suse.cz>
Wed, 8 Mar 2017 15:03:32 +0000 (16:03 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 13 Mar 2017 05:53:02 +0000 (22:53 -0700)
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>
18 files changed:
include/net/pkt_sched.h
include/net/sch_generic.h
include/uapi/linux/rtnetlink.h
net/sched/sch_api.c
net/sched/sch_cbq.c
net/sched/sch_drr.c
net/sched/sch_dsmark.c
net/sched/sch_generic.c
net/sched/sch_hfsc.c
net/sched/sch_htb.c
net/sched/sch_mq.c
net/sched/sch_mqprio.c
net/sched/sch_multiq.c
net/sched/sch_prio.c
net/sched/sch_qfq.c
net/sched/sch_red.c
net/sched/sch_sfb.c
net/sched/sch_tbf.c

index f1b76b8e6d2d296177116d0ef0f254d175551cbe..bec46f63f10ced844f8aec2b19bebf8b3dc01167 100644 (file)
@@ -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);
index aeec4086afb2446dadb1fb8c54ad54a909634380..65d50261031473d33c27f6bce1020048a697481d 100644 (file)
@@ -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;
index 6546917d605a916bfd5a905e30eb05d68fd6ad6b..75fcf5eff093a7164f5b4071d425b9a9ec322cc7 100644 (file)
@@ -545,6 +545,7 @@ enum {
        TCA_STATS2,
        TCA_STAB,
        TCA_PAD,
+       TCA_DUMP_INVISIBLE,
        __TCA_MAX
 };
 
index bcf49cd2278670197f2a7e9d4e9a62ae8d117468..62567bfe52c723262a291360cecd572fefced164 100644 (file)
@@ -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)) {
index d6ca18dc04c3e9e72efedd44088e95118a06b711..cf93e5ff3d630e50442d65b5440883fb8467e6a0 100644 (file)
@@ -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;
index bb4cbdf7500482b170eef6e7923cf2f2259e52b5..9fe67e257dfa8a52b38142a9269fe363616e1187 100644 (file)
@@ -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,
index 802ac7c2e5e87eed1341ba4c09d3e5d70bc75876..1b98cb2160ff1f424f7162e2d27df6490dfab25b 100644 (file)
@@ -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);
 
index b052b27a984e39c244c94132f1162a7033e5cc63..3e64d23e098cff218eea7ea0371302a535e6935c 100644 (file)
@@ -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
 }
 
index 3ffaa6fb0990f0aa31487a2f1829b1f1accf8b21..0198c6cdda4973a0e4d9ac96e1c10c242d0954e9 100644 (file)
@@ -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;
index 4cd5fb134bc9e2dbcdd61b51fb951f94301ed54c..95867033542ec4c889e3c1e7ebd266700aafbef7 100644 (file)
@@ -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,
index 20b7f1646f69270e08d8b7588759a0146f262e89..cadfdd4f1e521b3d68b8fa62d5797f3ff604651d 100644 (file)
@@ -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
 
        }
index 922683418e53853cb71747d8d30ab0e4a989254b..b851e209da4d37f723fedee77a4680572de39d3f 100644 (file)
@@ -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;
index e7839a0d0eaa52572f675fdb1dfc590c2a70ac76..43a3a10b3c8118fc2e0deff98be2635d2ad81330 100644 (file)
@@ -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,
index d4d7db267b6edfa56582ca4a588590e0ded9fe66..92c2e6d448d7984af35d6beb2cb3aea717b76511 100644 (file)
@@ -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;
index f9e712ce2d15ce9280c31d2f75d62b84034ae51d..6c85f3e9239bbc2b127ca7b7e61826de3b57873c 100644 (file)
@@ -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);
index 249b2a18acbd99288eb0a2579a0f29c2ab0b3ded..799ea6dd69b266ccb25d52abab68116e3508b3cb 100644 (file)
@@ -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;
index fe6963d2151956c508b510edec680b89201173ce..ae862f172c944283be1cbb56f971cf821cd12bf8 100644 (file)
@@ -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,
index 303355c449ab336227d9b115496e0882f2f2a079..40c29a801391c33b4c1633befe9aa9a9a506b2f2 100644 (file)
@@ -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])