Merge pull request #15171 from ssahani/tc-qfq

network: TC- Introduce Quick Fair Queueing (QFQ)
This commit is contained in:
Yu Watanabe 2020-06-04 11:17:42 +09:00 committed by GitHub
commit bb9a9e33c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 305 additions and 5 deletions

View File

@ -3252,6 +3252,45 @@
</variablelist>
</refsect1>
<refsect1>
<title>[QuickFairQueueing] Section Options</title>
<para>The <literal>[QuickFairQueueing]</literal> section manages the queueing discipline
(qdisc) of Quick Fair Queueing (QFQ).</para>
<variablelist class='network-directives'>
<xi:include href="tc.xml" xpointer="qdisc-parent" />
<xi:include href="tc.xml" xpointer="qdisc-handle" />
</variablelist>
</refsect1>
<refsect1>
<title>[QuickFairQueueingClass] Section Options</title>
<para>The <literal>[QuickFairQueueingClass]</literal> section manages the traffic control class of
Quick Fair Queueing (qfq).</para>
<variablelist class='network-directives'>
<xi:include href="tc.xml" xpointer="tclass-parent" />
<xi:include href="tc.xml" xpointer="tclass-classid" />
<varlistentry>
<term><varname>Weight=</varname></term>
<listitem>
<para>Specifies the weight of the class. Takse an integer in the range 1..1023. Defaults to
unset in which case the kernel default is used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>MaxPacketSize=</varname></term>
<listitem>
<para>Specifies the maximum packet size in bytes for the class. When suffixed with K, M, or G, the specified
size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000. When unset,
the kernel default is used.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[BridgeVLAN] Section Options</title>
<para>The <literal>[BridgeVLAN]</literal> section manages the VLAN ID configuration of a bridge port and accepts

View File

@ -810,6 +810,11 @@ static const NLType rtnl_tca_option_data_pie_types[] = {
[TCA_PIE_LIMIT] = { .type = NETLINK_TYPE_U32 },
};
static const NLType rtnl_tca_option_data_qfq_types[] = {
[TCA_QFQ_WEIGHT] = { .type = NETLINK_TYPE_U32 },
[TCA_QFQ_LMAX] = { .type = NETLINK_TYPE_U32 },
};
static const NLType rtnl_tca_option_data_sfb_types[] = {
[TCA_SFB_PARMS] = { .size = sizeof(struct tc_sfb_qopt) },
};
@ -834,6 +839,7 @@ static const char* const nl_union_tca_option_data_table[] = {
[NL_UNION_TCA_OPTION_DATA_HHF] = "hhf",
[NL_UNION_TCA_OPTION_DATA_HTB] = "htb",
[NL_UNION_TCA_OPTION_DATA_PIE] = "pie",
[NL_UNION_TCA_OPTION_DATA_QFQ] = "qfq",
[NL_UNION_TCA_OPTION_DATA_SFB] = "sfb",
[NL_UNION_TCA_OPTION_DATA_TBF] = "tbf",
};
@ -859,6 +865,8 @@ static const NLTypeSystem rtnl_tca_option_data_type_systems[] = {
.types = rtnl_tca_option_data_htb_types },
[NL_UNION_TCA_OPTION_DATA_PIE] = { .count = ELEMENTSOF(rtnl_tca_option_data_pie_types),
.types = rtnl_tca_option_data_pie_types },
[NL_UNION_TCA_OPTION_DATA_QFQ] = { .count = ELEMENTSOF(rtnl_tca_option_data_qfq_types),
.types = rtnl_tca_option_data_qfq_types },
[NL_UNION_TCA_OPTION_DATA_SFB] = { .count = ELEMENTSOF(rtnl_tca_option_data_sfb_types),
.types = rtnl_tca_option_data_sfb_types },
[NL_UNION_TCA_OPTION_DATA_TBF] = { .count = ELEMENTSOF(rtnl_tca_option_data_tbf_types),

View File

@ -105,6 +105,7 @@ typedef enum NLUnionTCAOptionData {
NL_UNION_TCA_OPTION_DATA_HHF,
NL_UNION_TCA_OPTION_DATA_HTB,
NL_UNION_TCA_OPTION_DATA_PIE,
NL_UNION_TCA_OPTION_DATA_QFQ,
NL_UNION_TCA_OPTION_DATA_SFB,
NL_UNION_TCA_OPTION_DATA_TBF,
_NL_UNION_TCA_OPTION_DATA_MAX,

View File

@ -131,6 +131,8 @@ sources = files('''
tc/pie.h
tc/qdisc.c
tc/qdisc.h
tc/qfq.c
tc/qfq.h
tc/sfb.c
tc/sfb.h
tc/sfq.c

View File

@ -312,6 +312,12 @@ PFIFOFast.Handle, config_parse_qdisc_handle,
PFIFOHeadDrop.Parent, config_parse_qdisc_parent, QDISC_KIND_PFIFO_HEAD_DROP, 0
PFIFOHeadDrop.Handle, config_parse_qdisc_handle, QDISC_KIND_PFIFO_HEAD_DROP, 0
PFIFOHeadDrop.PacketLimit, config_parse_pfifo_size, QDISC_KIND_PFIFO_HEAD_DROP, 0
QuickFairQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_QFQ, 0
QuickFairQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_QFQ, 0
QuickFairQueueingClass.Parent, config_parse_tclass_parent, TCLASS_KIND_QFQ, 0
QuickFairQueueingClass.ClassId, config_parse_tclass_classid, TCLASS_KIND_QFQ, 0
QuickFairQueueingClass.Weight, config_parse_quick_fair_queueing_weight, TCLASS_KIND_QFQ, 0
QuickFairQueueingClass.MaxPacketSize, config_parse_quick_fair_queueing_max_packet, TCLASS_KIND_QFQ, 0
FairQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_FQ, 0
FairQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_FQ, 0
FairQueueing.PacketLimit, config_parse_fair_queueing_u32, QDISC_KIND_FQ, 0

View File

@ -508,9 +508,6 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
"ControlledDelay\0"
"DeficitRoundRobinScheduler\0"
"DeficitRoundRobinSchedulerClass\0"
"PFIFO\0"
"PFIFOFast\0"
"PFIFOHeadDrop\0"
"FairQueueing\0"
"FairQueueingControlledDelay\0"
"GenericRandomEarlyDetection\0"
@ -518,7 +515,12 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
"HierarchyTokenBucket\0"
"HierarchyTokenBucketClass\0"
"NetworkEmulator\0"
"PFIFO\0"
"PFIFOFast\0"
"PFIFOHeadDrop\0"
"PIE\0"
"QuickFairQueueing\0"
"QuickFairQueueingClass\0"
"StochasticFairBlue\0"
"StochasticFairnessQueueing\0"
"TokenBucketFilter\0"

View File

@ -27,6 +27,7 @@ const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = {
[QDISC_KIND_HTB] = &htb_vtable,
[QDISC_KIND_NETEM] = &netem_vtable,
[QDISC_KIND_PIE] = &pie_vtable,
[QDISC_KIND_QFQ] = &qfq_vtable,
[QDISC_KIND_PFIFO] = &pfifo_vtable,
[QDISC_KIND_PFIFO_FAST] = &pfifo_fast_vtable,
[QDISC_KIND_PFIFO_HEAD_DROP] = &pfifo_head_drop_vtable,

View File

@ -23,6 +23,7 @@ typedef enum QDiscKind {
QDISC_KIND_PFIFO_FAST,
QDISC_KIND_PFIFO_HEAD_DROP,
QDISC_KIND_PIE,
QDISC_KIND_QFQ,
QDISC_KIND_SFB,
QDISC_KIND_SFQ,
QDISC_KIND_TBF,
@ -93,6 +94,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_handle);
#include "hhf.h"
#include "htb.h"
#include "pie.h"
#include "qfq.h"
#include "netem.h"
#include "drr.h"
#include "sfb.h"

170
src/network/tc/qfq.c Normal file
View File

@ -0,0 +1,170 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2020 VMware, Inc. */
#include <linux/pkt_sched.h>
#include "parse-util.h"
#include "qdisc.h"
#include "qfq.h"
#include "string-util.h"
#define QFQ_MAX_WEIGHT (1 << 10)
#define QFQ_MIN_MAX_PACKET 512
#define QFQ_MAX_MAX_PACKET (1 << 16)
const QDiscVTable qfq_vtable = {
.object_size = sizeof(QuickFairQueueing),
.tca_kind = "qfq",
};
static int quick_fair_queueing_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) {
QuickFairQueueingClass *qfq;
int r;
assert(link);
assert(tclass);
assert(req);
qfq = TCLASS_TO_QFQ(tclass);
r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "qfq");
if (r < 0)
return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
if (qfq->weight > 0) {
r = sd_netlink_message_append_u32(req, TCA_QFQ_WEIGHT, qfq->weight);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_QFQ_WEIGHT attribute: %m");
}
if (qfq->max_packet > 0) {
r = sd_netlink_message_append_u32(req, TCA_QFQ_LMAX, qfq->max_packet);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_QFQ_LMAX attribute: %m");
}
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
return 0;
}
int config_parse_quick_fair_queueing_weight(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
QuickFairQueueingClass *qfq;
Network *network = data;
uint32_t v;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = tclass_new_static(TCLASS_KIND_QFQ, network, filename, section_line, &tclass);
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to create traffic control class, ignoring assignment: %m");
qfq = TCLASS_TO_QFQ(tclass);
if (isempty(rvalue)) {
qfq->weight = 0;
tclass = NULL;
return 0;
}
r = safe_atou32(rvalue, &v);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
if (v == 0 || v > QFQ_MAX_WEIGHT) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Invalid '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
qfq->weight = v;
tclass = NULL;
return 0;
}
int config_parse_quick_fair_queueing_max_packet(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
QuickFairQueueingClass *qfq;
Network *network = data;
uint64_t v;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = tclass_new_static(TCLASS_KIND_QFQ, network, filename, section_line, &tclass);
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to create traffic control class, ignoring assignment: %m");
qfq = TCLASS_TO_QFQ(tclass);
if (isempty(rvalue)) {
qfq->max_packet = 0;
tclass = NULL;
return 0;
}
r = parse_size(rvalue, 1000, &v);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
if (v < QFQ_MIN_MAX_PACKET || v > QFQ_MAX_MAX_PACKET) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Invalid '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
qfq->max_packet = (uint32_t) v;
tclass = NULL;
return 0;
}
const TClassVTable qfq_tclass_vtable = {
.object_size = sizeof(QuickFairQueueingClass),
.tca_kind = "qfq",
.fill_message = quick_fair_queueing_class_fill_message,
};

26
src/network/tc/qfq.h Normal file
View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2020 VMware, Inc. */
#pragma once
#include "conf-parser.h"
#include "qdisc.h"
typedef struct QuickFairQueueing {
QDisc meta;
} QuickFairQueueing;
DEFINE_QDISC_CAST(QFQ, QuickFairQueueing);
extern const QDiscVTable qfq_vtable;
typedef struct QuickFairQueueingClass {
TClass meta;
uint32_t weight;
uint32_t max_packet;
} QuickFairQueueingClass;
DEFINE_TCLASS_CAST(QFQ, QuickFairQueueingClass);
extern const TClassVTable qfq_tclass_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_quick_fair_queueing_weight);
CONFIG_PARSER_PROTOTYPE(config_parse_quick_fair_queueing_max_packet);

View File

@ -18,6 +18,7 @@
const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = {
[TCLASS_KIND_DRR] = &drr_tclass_vtable,
[TCLASS_KIND_HTB] = &htb_tclass_vtable,
[TCLASS_KIND_QFQ] = &qfq_tclass_vtable,
};
static int tclass_new(TClassKind kind, TClass **ret) {

View File

@ -11,6 +11,7 @@
typedef enum TClassKind {
TCLASS_KIND_DRR,
TCLASS_KIND_HTB,
TCLASS_KIND_QFQ,
_TCLASS_KIND_MAX,
_TCLASS_KIND_INVALID = -1,
} TClassKind;
@ -67,3 +68,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_tclass_classid);
#include "drr.h"
#include "htb.h"
#include "qfq.h"

View File

@ -411,6 +411,14 @@ PacketLimit=
Parent=
Handle=
PacketLimit=
[QuickFairQueueing]
Parent=
Handle=
[QuickFairQueueingClass]
Parent=
ClassId=
Weight=
MaxPacketSize=
[DeficitRoundRobinScheduler]
Parent=
Handle=

View File

@ -0,0 +1,22 @@
[Match]
Name=test1
[Network]
IPv6AcceptRA=no
Address=10.1.2.4/16
[QuickFairQueueing]
Parent=root
Handle=0002
[QuickFairQueueingClass]
Parent=root
ClassId=0002:0030
Weight=2
MaxPacketSize=16000
[QuickFairQueueingClass]
Parent=root
ClassId=0002:0031
Weight=10
MaxPacketSize=8000

View File

@ -1676,6 +1676,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-qdisc-hhf.network',
'25-qdisc-ingress-netem-compat.network',
'25-qdisc-pie.network',
'25-qdisc-qfq.network',
'25-route-ipv6-src.network',
'25-route-static.network',
'25-route-vrf.network',
@ -2423,10 +2424,11 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'prio 1 rate 1Mbit ceil 500Kbit')
def test_qdisc2(self):
copy_unit_to_networkd_unit_path('25-qdisc-drr.network', '12-dummy.netdev')
copy_unit_to_networkd_unit_path('25-qdisc-drr.network', '12-dummy.netdev',
'25-qdisc-qfq.network', '11-dummy.netdev')
start_networkd()
self.wait_online(['dummy98:routable'])
self.wait_online(['dummy98:routable', 'test1:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
@ -2435,6 +2437,14 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
print(output)
self.assertRegex(output, 'class drr 2:30 root quantum 2000b')
output = check_output('tc qdisc show dev test1')
print(output)
self.assertRegex(output, 'qdisc qfq 2: root')
output = check_output('tc class show dev test1')
print(output)
self.assertRegex(output, 'class qfq 2:30 root weight 2 maxpkt 16000')
self.assertRegex(output, 'class qfq 2:31 root weight 10 maxpkt 8000')
@expectedFailureIfCAKEIsNotAvailable()
def test_qdisc_cake(self):
copy_unit_to_networkd_unit_path('25-qdisc-cake.network', '12-dummy.netdev')