mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-05 12:40:16 +01:00
tc: add support for sfq qdisc
Add support for Stochastic Fairness Queueing queueing discipline.
This commit is contained in:
parent
f695dd8de3
commit
b22b4f9101
7 changed files with 200 additions and 52 deletions
|
|
@ -2308,6 +2308,16 @@ static const NMVariantAttributeSpec *const tc_object_attribute_spec[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static const NMVariantAttributeSpec *const tc_qdisc_sfq_spec[] = {
|
||||
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("quantum", G_VARIANT_TYPE_UINT32, ),
|
||||
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("perturb", G_VARIANT_TYPE_INT32, ),
|
||||
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("limit", G_VARIANT_TYPE_UINT32, ),
|
||||
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("divisor", G_VARIANT_TYPE_UINT32, ),
|
||||
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("flows", G_VARIANT_TYPE_UINT32, ),
|
||||
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("depth", G_VARIANT_TYPE_UINT32, ),
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NMVariantAttributeSpec *const tc_qdisc_fq_codel_spec[] = {
|
||||
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("limit", G_VARIANT_TYPE_UINT32, ),
|
||||
NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("flows", G_VARIANT_TYPE_UINT32, ),
|
||||
|
|
@ -2335,6 +2345,7 @@ typedef struct {
|
|||
|
||||
static const NMQdiscAttributeSpec *const tc_qdisc_attribute_spec[] = {
|
||||
&(const NMQdiscAttributeSpec) { "fq_codel", tc_qdisc_fq_codel_spec },
|
||||
&(const NMQdiscAttributeSpec) { "sfq", tc_qdisc_sfq_spec },
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2232,6 +2232,7 @@ test_tc_config_qdisc (void)
|
|||
NMTCQdisc *qdisc1, *qdisc2;
|
||||
char *str;
|
||||
GError *error = NULL;
|
||||
GVariant *variant;
|
||||
|
||||
qdisc1 = nm_tc_qdisc_new ("fq_codel", TC_H_ROOT, &error);
|
||||
nmtst_assert_success (qdisc1, error);
|
||||
|
|
@ -2294,6 +2295,30 @@ test_tc_config_qdisc (void)
|
|||
|
||||
nm_tc_qdisc_unref (qdisc1);
|
||||
nm_tc_qdisc_unref (qdisc2);
|
||||
|
||||
#define CHECK_ATTRIBUTE(qdisc, name, vtype, type, value) \
|
||||
variant = nm_tc_qdisc_get_attribute (qdisc, name); \
|
||||
g_assert (variant); \
|
||||
g_assert (g_variant_is_of_type (variant, vtype)); \
|
||||
g_assert_cmpint (g_variant_get_ ## type(variant), ==, value);
|
||||
|
||||
qdisc1 = nm_utils_tc_qdisc_from_str ("handle 1235 root sfq perturb 10 quantum 1480 "
|
||||
"limit 9000 flows 1024 divisor 500 depth 12",
|
||||
&error);
|
||||
nmtst_assert_success (qdisc1, error);
|
||||
|
||||
g_assert_cmpstr (nm_tc_qdisc_get_kind (qdisc1), ==, "sfq");
|
||||
g_assert (nm_tc_qdisc_get_handle (qdisc1) == TC_H_MAKE (0x1235u << 16, 0x0000u));
|
||||
g_assert (nm_tc_qdisc_get_parent (qdisc1) == TC_H_ROOT);
|
||||
CHECK_ATTRIBUTE (qdisc1, "perturb", G_VARIANT_TYPE_INT32, int32, 10);
|
||||
CHECK_ATTRIBUTE (qdisc1, "quantum", G_VARIANT_TYPE_UINT32, uint32, 1480);
|
||||
CHECK_ATTRIBUTE (qdisc1, "limit", G_VARIANT_TYPE_UINT32, uint32, 9000);
|
||||
CHECK_ATTRIBUTE (qdisc1, "flows", G_VARIANT_TYPE_UINT32, uint32, 1024);
|
||||
CHECK_ATTRIBUTE (qdisc1, "divisor", G_VARIANT_TYPE_UINT32, uint32, 500);
|
||||
CHECK_ATTRIBUTE (qdisc1, "depth", G_VARIANT_TYPE_UINT32, uint32, 12);
|
||||
nm_tc_qdisc_unref (qdisc1);
|
||||
|
||||
#undef CHECK_ATTRIBUTE
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -1173,6 +1173,13 @@ nm_utils_qdiscs_from_tc_setting (NMPlatform *platform,
|
|||
GET_ATTR ("ce_threshold", qdisc->fq_codel.ce_threshold, UINT32, uint32, NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED);
|
||||
GET_ATTR ("memory_limit", qdisc->fq_codel.memory_limit, UINT32, uint32, NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET);
|
||||
GET_ATTR ("ecn", qdisc->fq_codel.ecn, BOOLEAN, boolean, FALSE);
|
||||
} else if (nm_streq (qdisc->kind, "sfq")) {
|
||||
GET_ATTR ("limit", qdisc->sfq.limit, UINT32, uint32, 0);
|
||||
GET_ATTR ("flows", qdisc->sfq.flows, UINT32, uint32, 0);
|
||||
GET_ATTR ("divisor", qdisc->sfq.divisor, UINT32, uint32, 0);
|
||||
GET_ATTR ("perturb", qdisc->sfq.perturb_period, INT32, int32, 0);
|
||||
GET_ATTR ("quantum", qdisc->sfq.quantum, UINT32, uint32, 0);
|
||||
GET_ATTR ("depth", qdisc->sfq.depth, UINT32, uint32, 0);
|
||||
}
|
||||
|
||||
#undef GET_ADDR
|
||||
|
|
|
|||
|
|
@ -3700,36 +3700,50 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only)
|
|||
struct nlattr *options_attr;
|
||||
int remaining;
|
||||
|
||||
nla_for_each_nested (options_attr, tb[TCA_OPTIONS], remaining) {
|
||||
if (nla_len (options_attr) < sizeof (uint32_t))
|
||||
continue;
|
||||
if (nm_streq0 (obj->qdisc.kind, "sfq")) {
|
||||
struct tc_sfq_qopt_v1 opt = {};
|
||||
|
||||
if (nm_streq0 (obj->qdisc.kind, "fq_codel")) {
|
||||
switch (nla_type (options_attr)) {
|
||||
case TCA_FQ_CODEL_LIMIT:
|
||||
obj->qdisc.fq_codel.limit = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_FLOWS:
|
||||
obj->qdisc.fq_codel.flows = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_TARGET:
|
||||
obj->qdisc.fq_codel.target = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_INTERVAL:
|
||||
obj->qdisc.fq_codel.interval = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_QUANTUM:
|
||||
obj->qdisc.fq_codel.quantum = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_CE_THRESHOLD:
|
||||
obj->qdisc.fq_codel.ce_threshold = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_MEMORY_LIMIT:
|
||||
obj->qdisc.fq_codel.memory_limit = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_ECN:
|
||||
obj->qdisc.fq_codel.ecn = !!nla_get_u32 (options_attr);
|
||||
break;
|
||||
if (tb[TCA_OPTIONS]->nla_len >= nla_attr_size (sizeof (opt))) {
|
||||
memcpy (&opt, nla_data (tb[TCA_OPTIONS]), sizeof (opt));
|
||||
obj->qdisc.sfq.quantum = opt.v0.quantum;
|
||||
obj->qdisc.sfq.perturb_period = opt.v0.perturb_period;
|
||||
obj->qdisc.sfq.limit = opt.v0.limit;
|
||||
obj->qdisc.sfq.divisor = opt.v0.divisor;
|
||||
obj->qdisc.sfq.flows = opt.v0.flows;
|
||||
obj->qdisc.sfq.depth = opt.depth;
|
||||
}
|
||||
} else {
|
||||
nla_for_each_nested (options_attr, tb[TCA_OPTIONS], remaining) {
|
||||
if (nla_len (options_attr) < sizeof (uint32_t))
|
||||
continue;
|
||||
|
||||
if (nm_streq0 (obj->qdisc.kind, "fq_codel")) {
|
||||
switch (nla_type (options_attr)) {
|
||||
case TCA_FQ_CODEL_LIMIT:
|
||||
obj->qdisc.fq_codel.limit = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_FLOWS:
|
||||
obj->qdisc.fq_codel.flows = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_TARGET:
|
||||
obj->qdisc.fq_codel.target = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_INTERVAL:
|
||||
obj->qdisc.fq_codel.interval = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_QUANTUM:
|
||||
obj->qdisc.fq_codel.quantum = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_CE_THRESHOLD:
|
||||
obj->qdisc.fq_codel.ce_threshold = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_MEMORY_LIMIT:
|
||||
obj->qdisc.fq_codel.memory_limit = nla_get_u32 (options_attr);
|
||||
break;
|
||||
case TCA_FQ_CODEL_ECN:
|
||||
obj->qdisc.fq_codel.ecn = !!nla_get_u32 (options_attr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4651,30 +4665,43 @@ _nl_msg_new_qdisc (int nlmsg_type,
|
|||
|
||||
NLA_PUT_STRING (msg, TCA_KIND, qdisc->kind);
|
||||
|
||||
if (!(tc_options = nla_nest_start (msg, TCA_OPTIONS)))
|
||||
goto nla_put_failure;
|
||||
if (nm_streq (qdisc->kind, "sfq")) {
|
||||
struct tc_sfq_qopt_v1 opt = { };
|
||||
|
||||
if (nm_streq (qdisc->kind, "fq_codel")) {
|
||||
if (qdisc->fq_codel.limit)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_LIMIT, qdisc->fq_codel.limit);
|
||||
if (qdisc->fq_codel.flows)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_FLOWS, qdisc->fq_codel.flows);
|
||||
if (qdisc->fq_codel.target)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_TARGET, qdisc->fq_codel.target);
|
||||
if (qdisc->fq_codel.interval)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_INTERVAL, qdisc->fq_codel.interval);
|
||||
if (qdisc->fq_codel.quantum)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_QUANTUM, qdisc->fq_codel.quantum);
|
||||
if (qdisc->fq_codel.ce_threshold != NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_CE_THRESHOLD, qdisc->fq_codel.ce_threshold);
|
||||
if (qdisc->fq_codel.memory_limit != NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_MEMORY_LIMIT, qdisc->fq_codel.memory_limit);
|
||||
if (qdisc->fq_codel.ecn)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_ECN, qdisc->fq_codel.ecn);
|
||||
opt.v0.quantum = qdisc->sfq.quantum;
|
||||
opt.v0.limit = qdisc->sfq.limit;
|
||||
opt.v0.perturb_period = qdisc->sfq.perturb_period;
|
||||
opt.v0.flows = qdisc->sfq.flows;
|
||||
opt.v0.divisor = qdisc->sfq.divisor;
|
||||
opt.depth = qdisc->sfq.depth;
|
||||
|
||||
NLA_PUT (msg, TCA_OPTIONS, sizeof (opt), &opt);
|
||||
} else {
|
||||
if (!(tc_options = nla_nest_start (msg, TCA_OPTIONS)))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nm_streq (qdisc->kind, "fq_codel")) {
|
||||
if (qdisc->fq_codel.limit)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_LIMIT, qdisc->fq_codel.limit);
|
||||
if (qdisc->fq_codel.flows)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_FLOWS, qdisc->fq_codel.flows);
|
||||
if (qdisc->fq_codel.target)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_TARGET, qdisc->fq_codel.target);
|
||||
if (qdisc->fq_codel.interval)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_INTERVAL, qdisc->fq_codel.interval);
|
||||
if (qdisc->fq_codel.quantum)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_QUANTUM, qdisc->fq_codel.quantum);
|
||||
if (qdisc->fq_codel.ce_threshold != NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_CE_THRESHOLD, qdisc->fq_codel.ce_threshold);
|
||||
if (qdisc->fq_codel.memory_limit != NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_MEMORY_LIMIT, qdisc->fq_codel.memory_limit);
|
||||
if (qdisc->fq_codel.ecn)
|
||||
NLA_PUT_U32 (msg, TCA_FQ_CODEL_ECN, qdisc->fq_codel.ecn);
|
||||
}
|
||||
|
||||
nla_nest_end (msg, tc_options);
|
||||
}
|
||||
|
||||
nla_nest_end (msg, tc_options);
|
||||
|
||||
return g_steal_pointer (&msg);
|
||||
|
||||
nla_put_failure:
|
||||
|
|
|
|||
|
|
@ -6486,6 +6486,19 @@ nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len)
|
|||
nm_utils_strbuf_append (&buf, &len, " memory_limit %u", qdisc->fq_codel.memory_limit);
|
||||
if (qdisc->fq_codel.ecn)
|
||||
nm_utils_strbuf_append (&buf, &len, " ecn");
|
||||
} else if (nm_streq0 (qdisc->kind, "sfq")) {
|
||||
if (qdisc->sfq.quantum)
|
||||
nm_utils_strbuf_append (&buf, &len, " quantum %u", qdisc->sfq.quantum);
|
||||
if (qdisc->sfq.perturb_period)
|
||||
nm_utils_strbuf_append (&buf, &len, " perturb %d", qdisc->sfq.perturb_period);
|
||||
if (qdisc->sfq.limit)
|
||||
nm_utils_strbuf_append (&buf, &len, " limit %u", (guint) qdisc->sfq.limit);
|
||||
if (qdisc->sfq.divisor)
|
||||
nm_utils_strbuf_append (&buf, &len, " divisor %u", qdisc->sfq.divisor);
|
||||
if (qdisc->sfq.flows)
|
||||
nm_utils_strbuf_append (&buf, &len, " flows %u", qdisc->sfq.flows);
|
||||
if (qdisc->sfq.depth)
|
||||
nm_utils_strbuf_append (&buf, &len, " depth %u", qdisc->sfq.depth);
|
||||
}
|
||||
|
||||
return buf0;
|
||||
|
|
@ -6512,6 +6525,14 @@ nm_platform_qdisc_hash_update (const NMPlatformQdisc *obj, NMHashState *h)
|
|||
obj->fq_codel.memory_limit,
|
||||
NM_HASH_COMBINE_BOOLS (guint8,
|
||||
obj->fq_codel.ecn));
|
||||
} else if (nm_streq0 (obj->kind, "sfq")) {
|
||||
nm_hash_update_vals (h,
|
||||
obj->sfq.quantum,
|
||||
obj->sfq.perturb_period,
|
||||
obj->sfq.limit,
|
||||
obj->sfq.divisor,
|
||||
obj->sfq.flows,
|
||||
obj->sfq.depth);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6538,6 +6559,13 @@ nm_platform_qdisc_cmp_full (const NMPlatformQdisc *a,
|
|||
NM_CMP_FIELD (a, b, fq_codel.ce_threshold);
|
||||
NM_CMP_FIELD (a, b, fq_codel.memory_limit);
|
||||
NM_CMP_FIELD_UNSAFE (a, b, fq_codel.ecn);
|
||||
} else if (nm_streq0 (a->kind, "sfq")) {
|
||||
NM_CMP_FIELD (a, b, sfq.quantum);
|
||||
NM_CMP_FIELD (a, b, sfq.perturb_period);
|
||||
NM_CMP_FIELD (a, b, sfq.limit);
|
||||
NM_CMP_FIELD (a, b, sfq.flows);
|
||||
NM_CMP_FIELD (a, b, sfq.divisor);
|
||||
NM_CMP_FIELD (a, b, sfq.depth);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -629,6 +629,15 @@ typedef struct {
|
|||
bool ecn:1;
|
||||
} NMPlatformQdiscFqCodel;
|
||||
|
||||
typedef struct {
|
||||
unsigned quantum;
|
||||
int perturb_period;
|
||||
guint32 limit;
|
||||
unsigned divisor;
|
||||
unsigned flows;
|
||||
unsigned depth;
|
||||
} NMPlatformQdiscSfq;
|
||||
|
||||
typedef struct {
|
||||
__NMPlatformObjWithIfindex_COMMON;
|
||||
|
||||
|
|
@ -642,6 +651,7 @@ typedef struct {
|
|||
guint32 info;
|
||||
union {
|
||||
NMPlatformQdiscFqCodel fq_codel;
|
||||
NMPlatformQdiscSfq sfq;
|
||||
};
|
||||
} NMPlatformQdisc;
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ test_qdisc1 (void)
|
|||
}
|
||||
|
||||
static void
|
||||
test_qdisc2 (void)
|
||||
test_qdisc_fq_codel (void)
|
||||
{
|
||||
int ifindex;
|
||||
gs_unref_ptrarray GPtrArray *known = NULL;
|
||||
|
|
@ -113,6 +113,45 @@ test_qdisc2 (void)
|
|||
g_assert_cmpint (qdisc->fq_codel.quantum, ==, 1000);
|
||||
}
|
||||
|
||||
static void
|
||||
test_qdisc_sfq (void)
|
||||
{
|
||||
int ifindex;
|
||||
gs_unref_ptrarray GPtrArray *known = NULL;
|
||||
gs_unref_ptrarray GPtrArray *plat = NULL;
|
||||
NMPObject *obj;
|
||||
NMPlatformQdisc *qdisc;
|
||||
|
||||
ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME);
|
||||
g_assert_cmpint (ifindex, >, 0);
|
||||
|
||||
nmtstp_run_command ("tc qdisc del dev %s root", DEVICE_NAME);
|
||||
|
||||
nmtstp_wait_for_signal (NM_PLATFORM_GET, 0);
|
||||
|
||||
known = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref);
|
||||
obj = qdisc_new (ifindex, "sfq", TC_H_ROOT);
|
||||
obj->qdisc.handle = TC_H_MAKE (0x8143 << 16, 0);
|
||||
obj->qdisc.sfq.perturb_period = 10;
|
||||
obj->qdisc.sfq.quantum = 1540;
|
||||
obj->qdisc.sfq.flows = 256;
|
||||
g_ptr_array_add (known, obj);
|
||||
|
||||
g_assert (nm_platform_qdisc_sync (NM_PLATFORM_GET, ifindex, known));
|
||||
plat = qdiscs_lookup (ifindex);
|
||||
g_assert (plat);
|
||||
g_assert_cmpint (plat->len, ==, 1);
|
||||
|
||||
obj = plat->pdata[0];
|
||||
qdisc = NMP_OBJECT_CAST_QDISC (obj);
|
||||
g_assert_cmpstr (qdisc->kind, ==, "sfq");
|
||||
g_assert_cmpint (qdisc->handle, ==, TC_H_MAKE (0x8143 << 16, 0));
|
||||
g_assert_cmpint (qdisc->parent, ==, TC_H_ROOT);
|
||||
g_assert_cmpint (qdisc->sfq.perturb_period, ==, 10);
|
||||
g_assert_cmpint (qdisc->sfq.quantum, ==, 1540);
|
||||
g_assert_cmpint (qdisc->sfq.flows, ==, 256);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP;
|
||||
|
|
@ -128,6 +167,7 @@ _nmtstp_setup_tests (void)
|
|||
{
|
||||
if (nmtstp_is_root_test ()) {
|
||||
nmtstp_env1_add_test_func ("/link/qdisc/1", test_qdisc1, TRUE);
|
||||
nmtstp_env1_add_test_func ("/link/qdisc/2", test_qdisc2, TRUE);
|
||||
nmtstp_env1_add_test_func ("/link/qdisc/fq_codel", test_qdisc_fq_codel, TRUE);
|
||||
nmtstp_env1_add_test_func ("/link/qdisc/sfq", test_qdisc_sfq, TRUE);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue