qdisc: allow setting default queuing discipline
authorstephen hemminger <stephen@networkplumber.org>
Tue, 27 Aug 2013 23:19:08 +0000 (16:19 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 31 Aug 2013 04:32:32 +0000 (00:32 -0400)
By default, the pfifo_fast queue discipline has been used by default
for all devices. But we have better choices now.

This patch allow setting the default queueing discipline with sysctl.
This allows easy use of better queueing disciplines on all devices
without having to use tc qdisc scripts. It is intended to allow
an easy path for distributions to make fq_codel or sfq the default
qdisc.

This patch also makes pfifo_fast more of a first class qdisc, since
it is now possible to manually override the default and explicitly
use pfifo_fast. The behavior for systems who do not use the sysctl
is unchanged, they still get pfifo_fast

Also removes leftover random # in sysctl net core.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/sysctl/net.txt
include/net/pkt_sched.h
include/net/sch_generic.h
net/core/sysctl_net_core.c
net/sched/sch_api.c
net/sched/sch_generic.c
net/sched/sch_mq.c
net/sched/sch_mqprio.c

index d569f2a424d511218f679258bbb246cae2438b25..9a0319a8247032c0fb6ae1a34129eacbba40c5eb 100644 (file)
@@ -50,6 +50,19 @@ The maximum number of packets that kernel can handle on a NAPI interrupt,
 it's a Per-CPU variable.
 Default: 64
 
+default_qdisc
+--------------
+
+The default queuing discipline to use for network devices. This allows
+overriding the default queue discipline of pfifo_fast with an
+alternative. Since the default queuing discipline is created with the
+no additional parameters so is best suited to queuing disciplines that
+work well without configuration like stochastic fair queue (sfq),
+CoDel (codel) or fair queue CoDel (fq_codel). Don't use queuing disciplines
+like Hierarchical Token Bucket or Deficit Round Robin which require setting
+up classes and bandwidths.
+Default: pfifo_fast
+
 busy_read
 ----------------
 Low latency busy poll timeout for socket reads. (needs CONFIG_NET_RX_BUSY_POLL)
index f7c24f8fbdc5c61208a26a11123f33d4d6d385df..59ec3cd15d681c04230b3cccd386c045140c0645 100644 (file)
@@ -85,6 +85,9 @@ struct Qdisc *fifo_create_dflt(struct Qdisc *sch, struct Qdisc_ops *ops,
 
 int register_qdisc(struct Qdisc_ops *qops);
 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_list_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 76368c9d4503c124df44a5ed56fc942782209db8..2e31bf14df0080891af98044dad71e84ff432aeb 100644 (file)
@@ -316,6 +316,7 @@ extern struct Qdisc noop_qdisc;
 extern struct Qdisc_ops noop_qdisc_ops;
 extern struct Qdisc_ops pfifo_fast_ops;
 extern struct Qdisc_ops mq_qdisc_ops;
+extern const struct Qdisc_ops *default_qdisc_ops;
 
 struct Qdisc_class_common {
        u32                     classid;
index 31107abd278391dfe6752a46fea03ec22978b604..cca444190907c9da8ea1ac86f998d76c0574ace9 100644 (file)
@@ -20,6 +20,7 @@
 #include <net/sock.h>
 #include <net/net_ratelimit.h>
 #include <net/busy_poll.h>
+#include <net/pkt_sched.h>
 
 static int zero = 0;
 static int one = 1;
@@ -193,6 +194,26 @@ static int flow_limit_table_len_sysctl(struct ctl_table *table, int write,
 }
 #endif /* CONFIG_NET_FLOW_LIMIT */
 
+#ifdef CONFIG_NET_SCHED
+static int set_default_qdisc(struct ctl_table *table, int write,
+                            void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       char id[IFNAMSIZ];
+       struct ctl_table tbl = {
+               .data = id,
+               .maxlen = IFNAMSIZ,
+       };
+       int ret;
+
+       qdisc_get_default(id, IFNAMSIZ);
+
+       ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
+       if (write && ret == 0)
+               ret = qdisc_set_default(id);
+       return ret;
+}
+#endif
+
 static struct ctl_table net_core_table[] = {
 #ifdef CONFIG_NET
        {
@@ -315,7 +336,14 @@ static struct ctl_table net_core_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
-#
+#endif
+#ifdef CONFIG_NET_SCHED
+       {
+               .procname       = "default_qdisc",
+               .mode           = 0644,
+               .maxlen         = IFNAMSIZ,
+               .proc_handler   = set_default_qdisc
+       },
 #endif
 #endif /* CONFIG_NET */
        {
index 51b968d3febb477be1e4183c8a94258c97fdcd18..812e57900591e40e37a615d2d8e9012d21ae370a 100644 (file)
@@ -131,6 +131,11 @@ static DEFINE_RWLOCK(qdisc_mod_lock);
  ************************************************/
 
 
+/* Qdisc to use by default */
+
+const struct Qdisc_ops *default_qdisc_ops = &pfifo_fast_ops;
+EXPORT_SYMBOL(default_qdisc_ops);
+
 /* The list of all installed queueing disciplines. */
 
 static struct Qdisc_ops *qdisc_base;
@@ -200,6 +205,58 @@ int unregister_qdisc(struct Qdisc_ops *qops)
 }
 EXPORT_SYMBOL(unregister_qdisc);
 
+/* Get default qdisc if not otherwise specified */
+void qdisc_get_default(char *name, size_t len)
+{
+       read_lock(&qdisc_mod_lock);
+       strlcpy(name, default_qdisc_ops->id, len);
+       read_unlock(&qdisc_mod_lock);
+}
+
+static struct Qdisc_ops *qdisc_lookup_default(const char *name)
+{
+       struct Qdisc_ops *q = NULL;
+
+       for (q = qdisc_base; q; q = q->next) {
+               if (!strcmp(name, q->id)) {
+                       if (!try_module_get(q->owner))
+                               q = NULL;
+                       break;
+               }
+       }
+
+       return q;
+}
+
+/* Set new default qdisc to use */
+int qdisc_set_default(const char *name)
+{
+       const struct Qdisc_ops *ops;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       write_lock(&qdisc_mod_lock);
+       ops = qdisc_lookup_default(name);
+       if (!ops) {
+               /* Not found, drop lock and try to load module */
+               write_unlock(&qdisc_mod_lock);
+               request_module("sch_%s", name);
+               write_lock(&qdisc_mod_lock);
+
+               ops = qdisc_lookup_default(name);
+       }
+
+       if (ops) {
+               /* Set new default */
+               module_put(default_qdisc_ops->owner);
+               default_qdisc_ops = ops;
+       }
+       write_unlock(&qdisc_mod_lock);
+
+       return ops ? 0 : -ENOENT;
+}
+
 /* We know handle. Find qdisc among all qdisc's attached to device
    (root qdisc, all its children, children of children etc.)
  */
@@ -1854,6 +1911,7 @@ static int __init pktsched_init(void)
                return err;
        }
 
+       register_qdisc(&pfifo_fast_ops);
        register_qdisc(&pfifo_qdisc_ops);
        register_qdisc(&bfifo_qdisc_ops);
        register_qdisc(&pfifo_head_drop_qdisc_ops);
index 48be3d5c0d9246117c03b0174da16a637129640c..5078e0c5db8ddb5c6186e91cedcbc4ae9c05d35b 100644 (file)
@@ -530,7 +530,6 @@ struct Qdisc_ops pfifo_fast_ops __read_mostly = {
        .dump           =       pfifo_fast_dump,
        .owner          =       THIS_MODULE,
 };
-EXPORT_SYMBOL(pfifo_fast_ops);
 
 static struct lock_class_key qdisc_tx_busylock;
 
@@ -583,6 +582,9 @@ struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue,
 {
        struct Qdisc *sch;
 
+       if (!try_module_get(ops->owner))
+               goto errout;
+
        sch = qdisc_alloc(dev_queue, ops);
        if (IS_ERR(sch))
                goto errout;
@@ -686,7 +688,7 @@ static void attach_one_default_qdisc(struct net_device *dev,
 
        if (dev->tx_queue_len) {
                qdisc = qdisc_create_dflt(dev_queue,
-                                         &pfifo_fast_ops, TC_H_ROOT);
+                                         default_qdisc_ops, TC_H_ROOT);
                if (!qdisc) {
                        netdev_info(dev, "activation failed\n");
                        return;
@@ -739,9 +741,8 @@ void dev_activate(struct net_device *dev)
        int need_watchdog;
 
        /* No queueing discipline is attached to device;
-          create default one i.e. pfifo_fast for devices,
-          which need queueing and noqueue_qdisc for
-          virtual interfaces
+        * create default one for devices, which need queueing
+        * and noqueue_qdisc for virtual interfaces
         */
 
        if (dev->qdisc == &noop_qdisc)
index 5da78a19ac9ac5af653f8f07ba6e2c8deb634683..2e56185736d67d589b78918ff59fd7641eb2377a 100644 (file)
@@ -57,7 +57,7 @@ static int mq_init(struct Qdisc *sch, struct nlattr *opt)
 
        for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
                dev_queue = netdev_get_tx_queue(dev, ntx);
-               qdisc = qdisc_create_dflt(dev_queue, &pfifo_fast_ops,
+               qdisc = qdisc_create_dflt(dev_queue, default_qdisc_ops,
                                          TC_H_MAKE(TC_H_MAJ(sch->handle),
                                                    TC_H_MIN(ntx + 1)));
                if (qdisc == NULL)
index accec33c454c720eb864bf21a7afc33de5100658..d44c868cb537d0e1da33e9d6806c00966e7286fc 100644 (file)
@@ -124,7 +124,7 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt)
 
        for (i = 0; i < dev->num_tx_queues; i++) {
                dev_queue = netdev_get_tx_queue(dev, i);
-               qdisc = qdisc_create_dflt(dev_queue, &pfifo_fast_ops,
+               qdisc = qdisc_create_dflt(dev_queue, default_qdisc_ops,
                                          TC_H_MAKE(TC_H_MAJ(sch->handle),
                                                    TC_H_MIN(i + 1)));
                if (qdisc == NULL) {