From 1efe982e39be7e8b7852a19957c7b49cab46e67c Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 9 Apr 2019 13:42:52 +0200 Subject: [PATCH] tc/qdisc: add support for fq_codel attributes --- libnm-core/nm-utils.c | 13 ++++++ src/devices/nm-device.c | 23 ++++++++++ src/platform/nm-linux-platform.c | 72 ++++++++++++++++++++++++++++++++ src/platform/nm-platform.c | 55 ++++++++++++++++++++---- src/platform/nm-platform.h | 14 +++++++ 5 files changed, 170 insertions(+), 7 deletions(-) diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 9a1430f6c7..009e802859 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -2308,12 +2308,25 @@ static const NMVariantAttributeSpec * const tc_object_attribute_spec[] = { NULL, }; +static const NMVariantAttributeSpec * const tc_qdisc_fq_codel_spec[] = { + TC_ATTR_SPEC_PTR ("limit", G_VARIANT_TYPE_UINT32, FALSE, FALSE, 0 ), + TC_ATTR_SPEC_PTR ("flows", G_VARIANT_TYPE_UINT32, FALSE, FALSE, 0 ), + TC_ATTR_SPEC_PTR ("target", G_VARIANT_TYPE_UINT32, FALSE, FALSE, 0 ), + TC_ATTR_SPEC_PTR ("interval", G_VARIANT_TYPE_UINT32, FALSE, FALSE, 0 ), + TC_ATTR_SPEC_PTR ("quantum", G_VARIANT_TYPE_UINT32, FALSE, FALSE, 0 ), + TC_ATTR_SPEC_PTR ("ce_threshold", G_VARIANT_TYPE_UINT32, FALSE, FALSE, 0 ), + TC_ATTR_SPEC_PTR ("memory", G_VARIANT_TYPE_UINT32, FALSE, FALSE, 0 ), + TC_ATTR_SPEC_PTR ("ecn", G_VARIANT_TYPE_BOOLEAN, TRUE, FALSE, 0 ), + NULL, +}; + typedef struct { const char *kind; const NMVariantAttributeSpec * const *attrs; } NMQdiscAttributeSpec; static const NMQdiscAttributeSpec *const tc_qdisc_attribute_spec[] = { + &(const NMQdiscAttributeSpec) { "fq_codel", tc_qdisc_fq_codel_spec }, NULL, }; diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index bd4fbcc37f..44eb0f5321 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -6526,6 +6526,29 @@ tc_commit (NMDevice *self) qdisc->parent = nm_tc_qdisc_get_parent (s_qdisc); qdisc->info = 0; +#define GET_ATTR(name, dst, variant_type, type, dflt) G_STMT_START { \ + GVariant *_variant = nm_tc_qdisc_get_attribute (s_qdisc, ""name""); \ + \ + if ( _variant \ + && g_variant_is_of_type (_variant, G_VARIANT_TYPE_ ## variant_type)) \ + (dst) = g_variant_get_ ## type (_variant); \ + else \ + (dst) = (dflt); \ +} G_STMT_END + + if (strcmp (qdisc->kind, "fq_codel") == 0) { + GET_ATTR("limit", qdisc->fq_codel.limit, UINT32, uint32, 0); + GET_ATTR("flows", qdisc->fq_codel.flows, UINT32, uint32, 0); + GET_ATTR("target", qdisc->fq_codel.target, UINT32, uint32, 0); + GET_ATTR("interval", qdisc->fq_codel.interval, UINT32, uint32, 0); + GET_ATTR("quantum", qdisc->fq_codel.quantum, UINT32, uint32, 0); + GET_ATTR("ce_threshold", qdisc->fq_codel.ce_threshold, UINT32, uint32, -1); + GET_ATTR("memory", qdisc->fq_codel.memory, UINT32, uint32, -1); + GET_ATTR("ecn", qdisc->fq_codel.ecn, BOOLEAN, boolean, FALSE); + } + +#undef GET_ADDR + g_ptr_array_add (qdiscs, q); } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index d4b0115252..a346d6618c 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -84,6 +84,13 @@ enum { /*****************************************************************************/ +/* Compat with older kernels. */ + +#define TCA_FQ_CODEL_CE_THRESHOLD 7 +#define TCA_FQ_CODEL_MEMORY_LIMIT 9 + +/*****************************************************************************/ + #define VLAN_FLAG_MVRP 0x8 /*****************************************************************************/ @@ -3481,6 +3488,7 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) { static const struct nla_policy policy[] = { [TCA_KIND] = { .type = NLA_STRING }, + [TCA_OPTIONS] = { .type = NLA_NESTED }, }; struct nlattr *tb[G_N_ELEMENTS (policy)]; const struct tcmsg *tcm; @@ -3506,6 +3514,45 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) obj->qdisc.parent = tcm->tcm_parent; obj->qdisc.info = tcm->tcm_info; + if (tb[TCA_OPTIONS]) { + 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, "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 = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_ECN: + obj->qdisc.fq_codel.ecn = nla_get_u32 (options_attr); + break; + } + } + } + } + return obj; } @@ -4161,6 +4208,7 @@ _nl_msg_new_qdisc (int nlmsg_type, const NMPlatformQdisc *qdisc) { nm_auto_nlmsg struct nl_msg *msg = NULL; + struct nlattr *tc_options; const struct tcmsg tcm = { .tcm_family = qdisc->addr_family, .tcm_ifindex = qdisc->ifindex, @@ -4176,6 +4224,30 @@ _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 (strcmp (qdisc->kind, "fq_codel") == 0) { + 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 != -1) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_CE_THRESHOLD, qdisc->fq_codel.ce_threshold); + if (qdisc->fq_codel.memory != -1) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_MEMORY_LIMIT, qdisc->fq_codel.memory); + if (qdisc->fq_codel.ecn) + NLA_PUT_S32 (msg, TCA_FQ_CODEL_ECN, qdisc->fq_codel.ecn); + } + + nla_nest_end (msg, tc_options); + return g_steal_pointer (&msg); nla_put_failure: diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 1fc0ccb750..3d78902860 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -6430,13 +6430,32 @@ nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len) if (!nm_utils_to_string_buffer_init_null (qdisc, &buf, &len)) return buf; - g_snprintf (buf, len, "%s%s family %d handle %x parent %x info %x", - qdisc->kind, - _to_string_dev (NULL, qdisc->ifindex, str_dev, sizeof (str_dev)), - qdisc->addr_family, - qdisc->handle, - qdisc->parent, - qdisc->info); + nm_utils_strbuf_append (&buf, &len, "%s%s family %u handle %x parent %x info %x", + qdisc->kind, + _to_string_dev (NULL, qdisc->ifindex, str_dev, sizeof (str_dev)), + qdisc->addr_family, + qdisc->handle, + qdisc->parent, + qdisc->info); + + if (nm_streq0 (qdisc->kind, "fq_codel")) { + if (qdisc->fq_codel.limit) + nm_utils_strbuf_append (&buf, &len, " limit %u", qdisc->fq_codel.limit); + if (qdisc->fq_codel.flows) + nm_utils_strbuf_append (&buf, &len, " flows %u", qdisc->fq_codel.flows); + if (qdisc->fq_codel.target) + nm_utils_strbuf_append (&buf, &len, " target %u", qdisc->fq_codel.target); + if (qdisc->fq_codel.interval) + nm_utils_strbuf_append (&buf, &len, " interval %u", qdisc->fq_codel.interval); + if (qdisc->fq_codel.quantum) + nm_utils_strbuf_append (&buf, &len, " quantum %u", qdisc->fq_codel.quantum); + if (qdisc->fq_codel.ce_threshold != -1) + nm_utils_strbuf_append (&buf, &len, " ce_threshold %u", qdisc->fq_codel.ce_threshold); + if (qdisc->fq_codel.memory != -1) + nm_utils_strbuf_append (&buf, &len, " memory %u", qdisc->fq_codel.memory); + if (qdisc->fq_codel.ecn) + nm_utils_strbuf_append (&buf, &len, " ecn"); + } return buf; } @@ -6451,6 +6470,17 @@ nm_platform_qdisc_hash_update (const NMPlatformQdisc *obj, NMHashState *h) obj->handle, obj->parent, obj->info); + if (nm_streq0 (obj->kind, "fq_codel")) { + nm_hash_update_vals (h, + obj->fq_codel.limit, + obj->fq_codel.flows, + obj->fq_codel.target, + obj->fq_codel.interval, + obj->fq_codel.quantum, + obj->fq_codel.ce_threshold, + obj->fq_codel.memory, + obj->fq_codel.ecn == TRUE); + } } int @@ -6464,6 +6494,17 @@ nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b) NM_CMP_FIELD (a, b, handle); NM_CMP_FIELD (a, b, info); + if (nm_streq0 (a->kind, "fq_codel")) { + NM_CMP_FIELD (a, b, fq_codel.limit); + NM_CMP_FIELD (a, b, fq_codel.flows); + NM_CMP_FIELD (a, b, fq_codel.target); + NM_CMP_FIELD (a, b, fq_codel.interval); + NM_CMP_FIELD (a, b, fq_codel.quantum); + NM_CMP_FIELD (a, b, fq_codel.ce_threshold); + NM_CMP_FIELD (a, b, fq_codel.memory); + NM_CMP_FIELD (a, b, fq_codel.ecn == TRUE); + } + return 0; } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 97be88324e..8742b90555 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -596,6 +596,17 @@ typedef struct { bool uid_range_has:1; /* has(FRA_UID_RANGE) */ } NMPlatformRoutingRule; +typedef struct { + guint32 limit; + guint32 flows; + guint32 target; + guint32 interval; + guint32 quantum; + guint32 ce_threshold; + guint32 memory; + bool ecn:1; +} NMPlatformQdiscFqCodel; + typedef struct { __NMPlatformObjWithIfindex_COMMON; const char *kind; @@ -603,6 +614,9 @@ typedef struct { guint32 handle; guint32 parent; guint32 info; + union { + NMPlatformQdiscFqCodel fq_codel; + }; } NMPlatformQdisc; typedef struct {