From 677d9158fc0aa16f875198d83c7bd8f87238aed5 Mon Sep 17 00:00:00 2001 From: Jonathan Vestin Date: Wed, 18 Mar 2015 17:13:01 +0100 Subject: [PATCH] netdev-linux: Support for SFQ, FQ_CoDel and CoDel qdiscs. This patch adds support for SFQ, CoDel and FQ_CoDel classless qdiscs to Open vSwitch. It also removes the requirement for a QoS to have at least one Queue (as this makes no sense when using classless qdiscs). I have also not implemented class_{get,set,delete,get_stats,dump_stats} because they are meant for qdiscs with classes. Signed-off-by: Jonathan Vestin [blp@nicira.com mostly applied stylistic changes] Signed-off-by: Ben Pfaff --- AUTHORS | 2 +- NEWS | 1 + lib/netdev-linux.c | 643 +++++++++++++++++++++++++++++++++++++++++++ vswitchd/bridge.c | 2 +- vswitchd/vswitch.xml | 27 ++ 5 files changed, 673 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index db4520f37..8fba915f5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -85,6 +85,7 @@ Jesse Gross jesse@nicira.com Jing Ai jinga@google.com Joe Perches joe@perches.com Joe Stringer joestringer@nicira.com +Jonathan Vestin jonavest@kau.se Jun Nakajima jun.nakajima@intel.com Justin Pettit jpettit@nicira.com Keith Amidon keith@nicira.com @@ -268,7 +269,6 @@ Joan Cirer joan@ev0.net John Darrington john@darrington.wattle.id.au John Galgay john@galgay.net John Hurley john.hurley@netronome.com -Jonathan Vestin jonavest@kau.se K 華 k940545@hotmail.com Kevin Mancuso kevin.mancuso@rackspace.com Kiran Shanbhog kiran@vmware.com diff --git a/NEWS b/NEWS index 9461f536d..79b3703d6 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,6 @@ Post-v2.3.0 --------------------- + - Added support for SFQ, FQ_CoDel and CoDel qdiscs. - Add bash command-line completion support for ovs-vsctl Please check utilities/ovs-command-compgen.INSTALL.md for how to use. - The MAC learning feature now includes per-port fairness to mitigate diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 9b2e74f7e..8253dfb4b 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -377,12 +377,18 @@ tc_destroy(struct tc *tc) static const struct tc_ops tc_ops_htb; static const struct tc_ops tc_ops_hfsc; +static const struct tc_ops tc_ops_codel; +static const struct tc_ops tc_ops_fqcodel; +static const struct tc_ops tc_ops_sfq; static const struct tc_ops tc_ops_default; static const struct tc_ops tc_ops_other; static const struct tc_ops *const tcs[] = { &tc_ops_htb, /* Hierarchy token bucket (see tc-htb(8)). */ &tc_ops_hfsc, /* Hierarchical fair service curve. */ + &tc_ops_codel, /* Controlled delay */ + &tc_ops_fqcodel, /* Fair queue controlled delay */ + &tc_ops_sfq, /* Stochastic fair queueing */ &tc_ops_default, /* Default qdisc (see tc-pfifo_fast(8)). */ &tc_ops_other, /* Some other qdisc. */ NULL @@ -2832,6 +2838,643 @@ const struct netdev_class netdev_internal_class = NULL, /* get_features */ netdev_internal_get_status); + +#define CODEL_N_QUEUES 0x0000 + +struct codel { + struct tc tc; + uint32_t target; + uint32_t limit; + uint32_t interval; +}; + +static struct codel * +codel_get__(const struct netdev *netdev_) +{ + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + return CONTAINER_OF(netdev->tc, struct codel, tc); +} + +static void +codel_install__(struct netdev *netdev_, uint32_t target, uint32_t limit, + uint32_t interval) +{ + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct codel *codel; + + codel = xmalloc(sizeof *codel); + tc_init(&codel->tc, &tc_ops_codel); + codel->target = target; + codel->limit = limit; + codel->interval = interval; + + netdev->tc = &codel->tc; +} + +static int +codel_setup_qdisc__(struct netdev *netdev, uint32_t target, uint32_t limit, + uint32_t interval) +{ + size_t opt_offset; + struct ofpbuf request; + struct tcmsg *tcmsg; + uint32_t otarget, olimit, ointerval; + int error; + + tc_del_qdisc(netdev); + + tcmsg = tc_make_request(netdev, RTM_NEWQDISC, + NLM_F_EXCL | NLM_F_CREATE, &request); + if (!tcmsg) { + return ENODEV; + } + tcmsg->tcm_handle = tc_make_handle(1, 0); + tcmsg->tcm_parent = TC_H_ROOT; + + otarget = target ? target : 5000; + olimit = limit ? limit : 10240; + ointerval = interval ? interval : 100000; + + nl_msg_put_string(&request, TCA_KIND, "codel"); + opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS); + nl_msg_put_u32(&request, TCA_CODEL_TARGET, otarget); + nl_msg_put_u32(&request, TCA_CODEL_LIMIT, olimit); + nl_msg_put_u32(&request, TCA_CODEL_INTERVAL, ointerval); + nl_msg_end_nested(&request, opt_offset); + + error = tc_transact(&request, NULL); + if (error) { + VLOG_WARN_RL(&rl, "failed to replace %s qdisc, " + "target %u, limit %u, interval %u error %d(%s)", + netdev_get_name(netdev), + otarget, olimit, ointerval, + error, ovs_strerror(error)); + } + return error; +} + +static void +codel_parse_qdisc_details__(struct netdev *netdev OVS_UNUSED, + const struct smap *details, struct codel *codel) +{ + const char *target_s; + const char *limit_s; + const char *interval_s; + + target_s = smap_get(details, "target"); + limit_s = smap_get(details, "limit"); + interval_s = smap_get(details, "interval"); + + codel->target = target_s ? strtoull(target_s, NULL, 10) : 0; + codel->limit = limit_s ? strtoull(limit_s, NULL, 10) : 0; + codel->interval = interval_s ? strtoull(interval_s, NULL, 10) : 0; + + if (!codel->target) { + codel->target = 5000; + } + if (!codel->limit) { + codel->limit = 10240; + } + if (!codel->interval) { + codel->interval = 100000; + } +} + +static int +codel_tc_install(struct netdev *netdev, const struct smap *details) +{ + int error; + struct codel codel; + + codel_parse_qdisc_details__(netdev, details, &codel); + error = codel_setup_qdisc__(netdev, codel.target, codel.limit, + codel.interval); + if (!error) { + codel_install__(netdev, codel.target, codel.limit, codel.interval); + } + return error; +} + +static int +codel_parse_tca_options__(struct nlattr *nl_options, struct codel *codel) +{ + static const struct nl_policy tca_codel_policy[] = { + [TCA_CODEL_TARGET] = { .type = NL_A_U32 }, + [TCA_CODEL_LIMIT] = { .type = NL_A_U32 }, + [TCA_CODEL_INTERVAL] = { .type = NL_A_U32 } + }; + + struct nlattr *attrs[ARRAY_SIZE(tca_codel_policy)]; + + if (!nl_parse_nested(nl_options, tca_codel_policy, + attrs, ARRAY_SIZE(tca_codel_policy))) { + VLOG_WARN_RL(&rl, "failed to parse CoDel class options"); + return EPROTO; + } + + codel->target = nl_attr_get_u32(attrs[TCA_CODEL_TARGET]); + codel->limit = nl_attr_get_u32(attrs[TCA_CODEL_LIMIT]); + codel->interval = nl_attr_get_u32(attrs[TCA_CODEL_INTERVAL]); + return 0; +} + +static int +codel_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg) +{ + struct nlattr *nlattr; + const char * kind; + int error; + struct codel codel; + + error = tc_parse_qdisc(nlmsg, &kind, &nlattr); + if (error != 0) { + return error; + } + + error = codel_parse_tca_options__(nlattr, &codel); + if (error != 0) { + return error; + } + + codel_install__(netdev, codel.target, codel.limit, codel.interval); + return 0; +} + + +static void +codel_tc_destroy(struct tc *tc) +{ + struct codel *codel = CONTAINER_OF(tc, struct codel, tc); + tc_destroy(tc); + free(codel); +} + +static int +codel_qdisc_get(const struct netdev *netdev, struct smap *details) +{ + const struct codel *codel = codel_get__(netdev); + smap_add_format(details, "target", "%u", codel->target); + smap_add_format(details, "limit", "%u", codel->limit); + smap_add_format(details, "interval", "%u", codel->interval); + return 0; +} + +static int +codel_qdisc_set(struct netdev *netdev, const struct smap *details) +{ + struct codel codel; + + codel_parse_qdisc_details__(netdev, details, &codel); + codel_install__(netdev, codel.target, codel.limit, codel.interval); + codel_get__(netdev)->target = codel.target; + codel_get__(netdev)->limit = codel.limit; + codel_get__(netdev)->interval = codel.interval; + return 0; +} + +static const struct tc_ops tc_ops_codel = { + "codel", /* linux_name */ + "linux-codel", /* ovs_name */ + CODEL_N_QUEUES, /* n_queues */ + codel_tc_install, + codel_tc_load, + codel_tc_destroy, + codel_qdisc_get, + codel_qdisc_set, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +/* FQ-CoDel traffic control class. */ + +#define FQCODEL_N_QUEUES 0x0000 + +struct fqcodel { + struct tc tc; + uint32_t target; + uint32_t limit; + uint32_t interval; + uint32_t flows; + uint32_t quantum; +}; + +static struct fqcodel * +fqcodel_get__(const struct netdev *netdev_) +{ + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + return CONTAINER_OF(netdev->tc, struct fqcodel, tc); +} + +static void +fqcodel_install__(struct netdev *netdev_, uint32_t target, uint32_t limit, + uint32_t interval, uint32_t flows, uint32_t quantum) +{ + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct fqcodel *fqcodel; + + fqcodel = xmalloc(sizeof *fqcodel); + tc_init(&fqcodel->tc, &tc_ops_fqcodel); + fqcodel->target = target; + fqcodel->limit = limit; + fqcodel->interval = interval; + fqcodel->flows = flows; + fqcodel->quantum = quantum; + + netdev->tc = &fqcodel->tc; +} + +static int +fqcodel_setup_qdisc__(struct netdev *netdev, uint32_t target, uint32_t limit, + uint32_t interval, uint32_t flows, uint32_t quantum) +{ + size_t opt_offset; + struct ofpbuf request; + struct tcmsg *tcmsg; + uint32_t otarget, olimit, ointerval, oflows, oquantum; + int error; + + tc_del_qdisc(netdev); + + tcmsg = tc_make_request(netdev, RTM_NEWQDISC, + NLM_F_EXCL | NLM_F_CREATE, &request); + if (!tcmsg) { + return ENODEV; + } + tcmsg->tcm_handle = tc_make_handle(1, 0); + tcmsg->tcm_parent = TC_H_ROOT; + + otarget = target ? target : 5000; + olimit = limit ? limit : 10240; + ointerval = interval ? interval : 100000; + oflows = flows ? flows : 1024; + oquantum = quantum ? quantum : 1514; /* fq_codel default quantum is 1514 + not mtu */ + + nl_msg_put_string(&request, TCA_KIND, "fq_codel"); + opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS); + nl_msg_put_u32(&request, TCA_FQ_CODEL_TARGET, otarget); + nl_msg_put_u32(&request, TCA_FQ_CODEL_LIMIT, olimit); + nl_msg_put_u32(&request, TCA_FQ_CODEL_INTERVAL, ointerval); + nl_msg_put_u32(&request, TCA_FQ_CODEL_FLOWS, oflows); + nl_msg_put_u32(&request, TCA_FQ_CODEL_QUANTUM, oquantum); + nl_msg_end_nested(&request, opt_offset); + + error = tc_transact(&request, NULL); + if (error) { + VLOG_WARN_RL(&rl, "failed to replace %s qdisc, " + "target %u, limit %u, interval %u, flows %u, quantum %u error %d(%s)", + netdev_get_name(netdev), + otarget, olimit, ointerval, oflows, oquantum, + error, ovs_strerror(error)); + } + return error; +} + +static void +fqcodel_parse_qdisc_details__(struct netdev *netdev OVS_UNUSED, + const struct smap *details, struct fqcodel *fqcodel) +{ + const char *target_s; + const char *limit_s; + const char *interval_s; + const char *flows_s; + const char *quantum_s; + + target_s = smap_get(details, "target"); + limit_s = smap_get(details, "limit"); + interval_s = smap_get(details, "interval"); + flows_s = smap_get(details, "flows"); + quantum_s = smap_get(details, "quantum"); + fqcodel->target = target_s ? strtoull(target_s, NULL, 10) : 0; + fqcodel->limit = limit_s ? strtoull(limit_s, NULL, 10) : 0; + fqcodel->interval = interval_s ? strtoull(interval_s, NULL, 10) : 0; + fqcodel->flows = flows_s ? strtoull(flows_s, NULL, 10) : 0; + fqcodel->quantum = quantum_s ? strtoull(quantum_s, NULL, 10) : 0; + if (!fqcodel->target) { + fqcodel->target = 5000; + } + if (!fqcodel->limit) { + fqcodel->limit = 10240; + } + if (!fqcodel->interval) { + fqcodel->interval = 1000000; + } + if (!fqcodel->flows) { + fqcodel->flows = 1024; + } + if (!fqcodel->quantum) { + fqcodel->quantum = 1514; + } +} + +static int +fqcodel_tc_install(struct netdev *netdev, const struct smap *details) +{ + int error; + struct fqcodel fqcodel; + + fqcodel_parse_qdisc_details__(netdev, details, &fqcodel); + error = fqcodel_setup_qdisc__(netdev, fqcodel.target, fqcodel.limit, + fqcodel.interval, fqcodel.flows, + fqcodel.quantum); + if (!error) { + fqcodel_install__(netdev, fqcodel.target, fqcodel.limit, + fqcodel.interval, fqcodel.flows, fqcodel.quantum); + } + return error; +} + +static int +fqcodel_parse_tca_options__(struct nlattr *nl_options, struct fqcodel *fqcodel) +{ + static const struct nl_policy tca_fqcodel_policy[] = { + [TCA_FQ_CODEL_TARGET] = { .type = NL_A_U32 }, + [TCA_FQ_CODEL_LIMIT] = { .type = NL_A_U32 }, + [TCA_FQ_CODEL_INTERVAL] = { .type = NL_A_U32 }, + [TCA_FQ_CODEL_FLOWS] = { .type = NL_A_U32 }, + [TCA_FQ_CODEL_QUANTUM] = { .type = NL_A_U32 } + }; + + struct nlattr *attrs[ARRAY_SIZE(tca_fqcodel_policy)]; + + if (!nl_parse_nested(nl_options, tca_fqcodel_policy, + attrs, ARRAY_SIZE(tca_fqcodel_policy))) { + VLOG_WARN_RL(&rl, "failed to parse FQ_CoDel class options"); + return EPROTO; + } + + fqcodel->target = nl_attr_get_u32(attrs[TCA_FQ_CODEL_TARGET]); + fqcodel->limit = nl_attr_get_u32(attrs[TCA_FQ_CODEL_LIMIT]); + fqcodel->interval =nl_attr_get_u32(attrs[TCA_FQ_CODEL_INTERVAL]); + fqcodel->flows = nl_attr_get_u32(attrs[TCA_FQ_CODEL_FLOWS]); + fqcodel->quantum = nl_attr_get_u32(attrs[TCA_FQ_CODEL_QUANTUM]); + return 0; +} + +static int +fqcodel_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg) +{ + struct nlattr *nlattr; + const char * kind; + int error; + struct fqcodel fqcodel; + + error = tc_parse_qdisc(nlmsg, &kind, &nlattr); + if (error != 0) { + return error; + } + + error = fqcodel_parse_tca_options__(nlattr, &fqcodel); + if (error != 0) { + return error; + } + + fqcodel_install__(netdev, fqcodel.target, fqcodel.limit, fqcodel.interval, + fqcodel.flows, fqcodel.quantum); + return 0; +} + +static void +fqcodel_tc_destroy(struct tc *tc) +{ + struct fqcodel *fqcodel = CONTAINER_OF(tc, struct fqcodel, tc); + tc_destroy(tc); + free(fqcodel); +} + +static int +fqcodel_qdisc_get(const struct netdev *netdev, struct smap *details) +{ + const struct fqcodel *fqcodel = fqcodel_get__(netdev); + smap_add_format(details, "target", "%u", fqcodel->target); + smap_add_format(details, "limit", "%u", fqcodel->limit); + smap_add_format(details, "interval", "%u", fqcodel->interval); + smap_add_format(details, "flows", "%u", fqcodel->flows); + smap_add_format(details, "quantum", "%u", fqcodel->quantum); + return 0; +} + +static int +fqcodel_qdisc_set(struct netdev *netdev, const struct smap *details) +{ + struct fqcodel fqcodel; + + fqcodel_parse_qdisc_details__(netdev, details, &fqcodel); + fqcodel_install__(netdev, fqcodel.target, fqcodel.limit, fqcodel.interval, + fqcodel.flows, fqcodel.quantum); + fqcodel_get__(netdev)->target = fqcodel.target; + fqcodel_get__(netdev)->limit = fqcodel.limit; + fqcodel_get__(netdev)->interval = fqcodel.interval; + fqcodel_get__(netdev)->flows = fqcodel.flows; + fqcodel_get__(netdev)->quantum = fqcodel.quantum; + return 0; +} + +static const struct tc_ops tc_ops_fqcodel = { + "fq_codel", /* linux_name */ + "linux-fq_codel", /* ovs_name */ + FQCODEL_N_QUEUES, /* n_queues */ + fqcodel_tc_install, + fqcodel_tc_load, + fqcodel_tc_destroy, + fqcodel_qdisc_get, + fqcodel_qdisc_set, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +/* SFQ traffic control class. */ + +#define SFQ_N_QUEUES 0x0000 + +struct sfq { + struct tc tc; + uint32_t quantum; + uint32_t perturb; +}; + +static struct sfq * +sfq_get__(const struct netdev *netdev_) +{ + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + return CONTAINER_OF(netdev->tc, struct sfq, tc); +} + +static void +sfq_install__(struct netdev *netdev_, uint32_t quantum, uint32_t perturb) +{ + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct sfq *sfq; + + sfq = xmalloc(sizeof *sfq); + tc_init(&sfq->tc, &tc_ops_sfq); + sfq->perturb = perturb; + sfq->quantum = quantum; + + netdev->tc = &sfq->tc; +} + +static int +sfq_setup_qdisc__(struct netdev *netdev, uint32_t quantum, uint32_t perturb) +{ + struct tc_sfq_qopt opt; + struct ofpbuf request; + struct tcmsg *tcmsg; + int mtu; + int mtu_error, error; + mtu_error = netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu); + + tc_del_qdisc(netdev); + + tcmsg = tc_make_request(netdev, RTM_NEWQDISC, + NLM_F_EXCL | NLM_F_CREATE, &request); + if (!tcmsg) { + return ENODEV; + } + tcmsg->tcm_handle = tc_make_handle(1, 0); + tcmsg->tcm_parent = TC_H_ROOT; + + memset(&opt, 0, sizeof opt); + if (!quantum) { + if (!mtu_error) { + opt.quantum = mtu; /* if we cannot find mtu, use default */ + } + } else { + opt.quantum = quantum; + } + + if (!perturb) { + opt.perturb_period = 10; + } else { + opt.perturb_period = perturb; + } + + nl_msg_put_string(&request, TCA_KIND, "sfq"); + nl_msg_put_unspec(&request, TCA_OPTIONS, &opt, sizeof opt); + + error = tc_transact(&request, NULL); + if (error) { + VLOG_WARN_RL(&rl, "failed to replace %s qdisc, " + "quantum %u, perturb %u error %d(%s)", + netdev_get_name(netdev), + opt.quantum, opt.perturb_period, + error, ovs_strerror(error)); + } + return error; +} + +static void +sfq_parse_qdisc_details__(struct netdev *netdev, + const struct smap *details, struct sfq *sfq) +{ + const char *perturb_s; + const char *quantum_s; + int mtu; + int mtu_error; + + perturb_s = smap_get(details, "perturb"); + quantum_s = smap_get(details, "quantum"); + sfq->perturb = perturb_s ? strtoull(perturb_s, NULL, 10) : 0; + sfq->quantum = quantum_s ? strtoull(quantum_s, NULL, 10) : 0; + if (!sfq->perturb) { + sfq->perturb = 10; + } + + if (!sfq->quantum) { + mtu_error = netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu); + if (!mtu_error) { + sfq->quantum = mtu; + } else { + VLOG_WARN_RL(&rl, "when using SFQ, you must specify quantum on a " + "device without mtu"); + return; + } + } +} + +static int +sfq_tc_install(struct netdev *netdev, const struct smap *details) +{ + int error; + struct sfq sfq; + + sfq_parse_qdisc_details__(netdev, details, &sfq); + error = sfq_setup_qdisc__(netdev, sfq.quantum, sfq.perturb); + if (!error) { + sfq_install__(netdev, sfq.quantum, sfq.perturb); + } + return error; +} + +static int +sfq_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg) +{ + const struct tc_sfq_qopt *sfq; + struct nlattr *nlattr; + const char * kind; + int error; + + error = tc_parse_qdisc(nlmsg, &kind, &nlattr); + if (error == 0) { + sfq = nl_attr_get(nlattr); + sfq_install__(netdev, sfq->perturb_period, sfq->quantum); + return 0; + } + + return error; +} + +static void +sfq_tc_destroy(struct tc *tc) +{ + struct sfq *sfq = CONTAINER_OF(tc, struct sfq, tc); + tc_destroy(tc); + free(sfq); +} + +static int +sfq_qdisc_get(const struct netdev *netdev, struct smap *details) +{ + const struct sfq *sfq = sfq_get__(netdev); + smap_add_format(details, "quantum", "%u", sfq->quantum); + smap_add_format(details, "perturb", "%u", sfq->perturb); + return 0; +} + +static int +sfq_qdisc_set(struct netdev *netdev, const struct smap *details) +{ + struct sfq sfq; + + sfq_parse_qdisc_details__(netdev, details, &sfq); + sfq_install__(netdev, sfq.quantum, sfq.perturb); + sfq_get__(netdev)->quantum = sfq.quantum; + sfq_get__(netdev)->perturb = sfq.perturb; + return 0; +} + +static const struct tc_ops tc_ops_sfq = { + "sfq", /* linux_name */ + "linux-sfq", /* ovs_name */ + SFQ_N_QUEUES, /* n_queues */ + sfq_tc_install, + sfq_tc_load, + sfq_tc_destroy, + sfq_qdisc_get, + sfq_qdisc_set, + NULL, + NULL, + NULL, + NULL, + NULL +}; + /* HTB traffic control class. */ #define HTB_N_QUEUES 0xf000 diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 68648b9bc..4213a7902 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -4382,7 +4382,7 @@ iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos) ofpbuf_init(&queues_buf, 0); - if (!qos || qos->type[0] == '\0' || qos->n_queues < 1) { + if (!qos || qos->type[0] == '\0') { netdev_set_qos(iface->netdev, NULL, NULL); } else { const struct ovsdb_datum *queues; diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 1bd652217..07f3bea4a 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -3149,6 +3149,33 @@ information on how this classifier works. +
+
linux-sfq
+
+ Linux ``Stochastic Fairness Queueing'' classifier. See + tc-sfq(8) (also at + http://linux.die.net/man/8/tc-sfq) for information on + how this classifier works. +
+
+
+
linux-codel
+
+ Linux ``Controlled Delay'' classifier. See tc-codel(8) + (also at + http://man7.org/linux/man-pages/man8/tc-codel.8.html) + for information on how this classifier works. +
+
+
+
linux-fq_codel
+
+ Linux ``Fair Queuing with Controlled Delay'' classifier. See + tc-fq_codel(8) (also at + http://man7.org/linux/man-pages/man8/tc-fq_codel.8.html) + for information on how this classifier works. +
+
-- 2.20.1