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 f1b76b8..bec46f6 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 aeec408..65d5026 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 6546917..75fcf5e 100644 (file)
@@ -545,6 +545,7 @@ enum {
        TCA_STATS2,
        TCA_STAB,
        TCA_PAD,
+       TCA_DUMP_INVISIBLE,
        __TCA_MAX
 };
 
index bcf49cd..62567bf 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 d6ca18d..cf93e5f 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 bb4cbdf..9fe67e2 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 802ac7c..1b98cb2 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 b052b27..3e64d23 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 3ffaa6f..0198c6c 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 4cd5fb1..9586703 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 20b7f16..cadfdd4 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 9226834..b851e20 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 e7839a0..43a3a10 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 d4d7db2..92c2e6d 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 f9e712c..6c85f3e 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 249b2a1..799ea6d 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 fe6963d..ae862f1 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 303355c..40c29a8 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])