mirror of
https://github.com/openvswitch/ovs
synced 2025-08-30 22:05:19 +00:00
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 <jonavest@kau.se> [blp@nicira.com mostly applied stylistic changes] Signed-off-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
committed by
Ben Pfaff
parent
14691214d1
commit
677d9158fc
2
AUTHORS
2
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
|
||||
|
1
NEWS
1
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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -3149,6 +3149,33 @@
|
||||
information on how this classifier works.
|
||||
</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><code>linux-sfq</code></dt>
|
||||
<dd>
|
||||
Linux ``Stochastic Fairness Queueing'' classifier. See
|
||||
<code>tc-sfq</code>(8) (also at
|
||||
<code>http://linux.die.net/man/8/tc-sfq</code>) for information on
|
||||
how this classifier works.
|
||||
</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><code>linux-codel</code></dt>
|
||||
<dd>
|
||||
Linux ``Controlled Delay'' classifier. See <code>tc-codel</code>(8)
|
||||
(also at
|
||||
<code>http://man7.org/linux/man-pages/man8/tc-codel.8.html</code>)
|
||||
for information on how this classifier works.
|
||||
</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><code>linux-fq_codel</code></dt>
|
||||
<dd>
|
||||
Linux ``Fair Queuing with Controlled Delay'' classifier. See
|
||||
<code>tc-fq_codel</code>(8) (also at
|
||||
<code>http://man7.org/linux/man-pages/man8/tc-fq_codel.8.html</code>)
|
||||
for information on how this classifier works.
|
||||
</dd>
|
||||
</dl>
|
||||
</column>
|
||||
|
||||
<column name="queues">
|
||||
|
Reference in New Issue
Block a user