diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2020-06-08 15:32:10 +0200 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2020-06-08 15:32:10 +0200 |
commit | d1e8eb791c21eb0f0a690adbc626e10a7291a918 (patch) | |
tree | c0ac91db88ba9f6c0a5f46d787b699d9b5c50e29 | |
parent | d507563a80d3e171017700f7b268940b63f5708d (diff) | |
parent | 934777120b90dbf77f0c6e7b9f25451cc21fbc08 (diff) | |
download | NetworkManager-d1e8eb791c21eb0f0a690adbc626e10a7291a918.tar.gz |
tc: merge branch 'bg/tc'
Support more qdiscs.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/525
-rw-r--r-- | libnm-core/nm-utils.c | 66 | ||||
-rw-r--r-- | libnm-core/tests/test-setting.c | 38 | ||||
-rw-r--r-- | shared/nm-glib-aux/nm-shared-utils.c | 10 | ||||
-rw-r--r-- | src/NetworkManagerUtils.c | 138 | ||||
-rw-r--r-- | src/NetworkManagerUtils.h | 7 | ||||
-rw-r--r-- | src/devices/nm-device.c | 121 | ||||
-rw-r--r-- | src/platform/nm-linux-platform.c | 217 | ||||
-rw-r--r-- | src/platform/nm-platform.c | 46 | ||||
-rw-r--r-- | src/platform/nm-platform.h | 18 | ||||
-rw-r--r-- | src/platform/tests/test-tc.c | 94 |
10 files changed, 567 insertions, 188 deletions
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index bc00516821..05846c50aa 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -2300,11 +2300,29 @@ fail: } static const NMVariantAttributeSpec *const tc_object_attribute_spec[] = { - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("root", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("parent", G_VARIANT_TYPE_STRING, .str_type = 'a', ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("handle", G_VARIANT_TYPE_STRING, .str_type = 'a', ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("kind", G_VARIANT_TYPE_STRING, .no_value = TRUE, .str_type = 'a', ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("", G_VARIANT_TYPE_STRING, .no_value = TRUE, .consumes_rest = TRUE, .str_type = 'a', ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("root", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("parent", G_VARIANT_TYPE_STRING, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("handle", G_VARIANT_TYPE_STRING, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("kind", G_VARIANT_TYPE_STRING, .no_value = TRUE, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("", G_VARIANT_TYPE_STRING, .no_value = TRUE, .consumes_rest = TRUE, ), + 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_tbf_spec[] = { + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("rate", G_VARIANT_TYPE_UINT64, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("burst", G_VARIANT_TYPE_UINT32, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("limit", G_VARIANT_TYPE_UINT32, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("latency", G_VARIANT_TYPE_UINT32, ), NULL, }; @@ -2335,6 +2353,8 @@ 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 }, + &(const NMQdiscAttributeSpec) { "tbf", tc_qdisc_tbf_spec }, NULL, }; @@ -2550,17 +2570,17 @@ static const NMVariantAttributeSpec *const tc_action_simple_attribute_spec[] = { }; static const NMVariantAttributeSpec *const tc_action_mirred_attribute_spec[] = { - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("egress", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("ingress", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("mirror", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("redirect", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("dev", G_VARIANT_TYPE_STRING, .no_value = TRUE, .str_type = 'a', ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("egress", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("ingress", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("mirror", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("redirect", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("dev", G_VARIANT_TYPE_STRING, ), NULL, }; static const NMVariantAttributeSpec *const tc_action_attribute_spec[] = { - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("kind", G_VARIANT_TYPE_STRING, .no_value = TRUE, .str_type = 'a', ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("", G_VARIANT_TYPE_STRING, .no_value = TRUE, .consumes_rest = TRUE, .str_type = 'a', ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("kind", G_VARIANT_TYPE_STRING, .no_value = TRUE, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("", G_VARIANT_TYPE_STRING, .no_value = TRUE, .consumes_rest = TRUE, ), NULL, }; @@ -2758,8 +2778,8 @@ nm_utils_tc_tfilter_to_str (NMTCTfilter *tfilter, GError **error) } static const NMVariantAttributeSpec *const tc_tfilter_attribute_spec[] = { - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("action", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("", G_VARIANT_TYPE_STRING, .no_value = TRUE, .consumes_rest = TRUE, .str_type = 'a', ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("action", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE ("", G_VARIANT_TYPE_STRING, .no_value = TRUE, .consumes_rest = TRUE, ), NULL, }; @@ -5654,6 +5674,24 @@ nm_utils_parse_variant_attributes (const char *string, return NULL; } variant = g_variant_new_uint32 (num); + } else if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_INT32)) { + gint64 num = _nm_utils_ascii_str_to_int64 (value, 10, G_MININT32, G_MAXINT32, G_MAXINT64); + + if (num == G_MAXINT64) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("invalid int32 value '%s' for attribute '%s'"), value, (*s)->name); + return NULL; + } + variant = g_variant_new_int32 (num); + } else if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_UINT64)) { + guint64 num = _nm_utils_ascii_str_to_uint64 (value, 10, 0, G_MAXUINT64, G_MAXUINT64); + + if (num == G_MAXUINT64 && errno != 0) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("invalid uint64 value '%s' for attribute '%s'"), value, (*s)->name); + return NULL; + } + variant = g_variant_new_uint64 (num); } else if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_BYTE)) { gint64 num = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT8, -1); diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index ae37ec7bbd..3bae728d5e 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -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,43 @@ 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); + + qdisc1 = nm_utils_tc_qdisc_from_str ("handle 1235 root tbf rate 1000000 burst 5000 limit 10000", + &error); + nmtst_assert_success (qdisc1, error); + + g_assert_cmpstr (nm_tc_qdisc_get_kind (qdisc1), ==, "tbf"); + 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, "rate", G_VARIANT_TYPE_UINT64, uint64, 1000000); + CHECK_ATTRIBUTE (qdisc1, "burst", G_VARIANT_TYPE_UINT32, uint32, 5000); + CHECK_ATTRIBUTE (qdisc1, "limit", G_VARIANT_TYPE_UINT32, uint32, 10000); + nm_tc_qdisc_unref (qdisc1); + + +#undef CHECK_ATTRIBUTE } static void diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c index 5b8f96b151..7f7636c7b3 100644 --- a/shared/nm-glib-aux/nm-shared-utils.c +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -4913,15 +4913,21 @@ _nm_utils_format_variant_attributes_full (GString *str, if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)) value = nm_sprintf_buf (buf, "%u", g_variant_get_uint32 (variant)); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT32)) + value = nm_sprintf_buf (buf, "%d", (int) g_variant_get_int32 (variant)); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT64)) + value = nm_sprintf_buf (buf, "%"G_GUINT64_FORMAT, g_variant_get_uint64 (variant)); else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTE)) value = nm_sprintf_buf (buf, "%hhu", g_variant_get_byte (variant)); else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)) value = g_variant_get_boolean (variant) ? "true" : "false"; else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) value = g_variant_get_string (variant, NULL); - else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTESTRING)) + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTESTRING)) { + /* FIXME: there is no guarantee that the byte array + * is valid UTF-8.*/ value = g_variant_get_bytestring (variant); - else + } else continue; if (sep) diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 3f2f44d3cd..bcb12a564d 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -9,6 +9,7 @@ #include "NetworkManagerUtils.h" #include <linux/fib_rules.h> +#include <linux/pkt_sched.h> #include "nm-glib-aux/nm-c-list.h" @@ -18,6 +19,7 @@ #include "nm-setting-ip4-config.h" #include "nm-setting-ip6-config.h" #include "nm-core-internal.h" +#include "platform/nmp-object.h" #include "platform/nm-platform.h" #include "nm-auth-utils.h" @@ -1125,3 +1127,139 @@ nm_utils_file_is_in_path (const char *abs_filename, ? path : NULL; } + +/* The returned qdisc array is valid as long as s_tc is not modified */ +GPtrArray * +nm_utils_qdiscs_from_tc_setting (NMPlatform *platform, + NMSettingTCConfig *s_tc, + int ip_ifindex) +{ + GPtrArray *qdiscs; + guint nqdiscs; + guint i; + + nqdiscs = nm_setting_tc_config_get_num_qdiscs (s_tc); + qdiscs = g_ptr_array_new_full (nqdiscs, (GDestroyNotify) nmp_object_unref); + + for (i = 0; i < nqdiscs; i++) { + NMTCQdisc *s_qdisc = nm_setting_tc_config_get_qdisc (s_tc, i); + NMPObject *q = nmp_object_new (NMP_OBJECT_TYPE_QDISC, NULL); + NMPlatformQdisc *qdisc = NMP_OBJECT_CAST_QDISC (q); + + qdisc->ifindex = ip_ifindex; + qdisc->kind = nm_tc_qdisc_get_kind (s_qdisc); + + qdisc->addr_family = AF_UNSPEC; + qdisc->handle = nm_tc_qdisc_get_handle (s_qdisc); + 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, 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); + } else if (nm_streq (qdisc->kind, "tbf")) { + GET_ATTR ("rate", qdisc->tbf.rate, UINT64, uint64, 0); + GET_ATTR ("burst", qdisc->tbf.burst, UINT32, uint32, 0); + GET_ATTR ("limit", qdisc->tbf.limit, UINT32, uint32, 0); + GET_ATTR ("latency", qdisc->tbf.latency, UINT32, uint32, 0); + } +#undef GET_ADDR + + g_ptr_array_add (qdiscs, q); + } + + return qdiscs; +} + +/* The returned tfilter array is valid as long as s_tc is not modified */ +GPtrArray * +nm_utils_tfilters_from_tc_setting (NMPlatform *platform, + NMSettingTCConfig *s_tc, + int ip_ifindex) +{ + GPtrArray *tfilters; + guint ntfilters; + guint i; + + ntfilters = nm_setting_tc_config_get_num_tfilters (s_tc); + tfilters = g_ptr_array_new_full (ntfilters, (GDestroyNotify) nmp_object_unref); + + for (i = 0; i < ntfilters; i++) { + NMTCTfilter *s_tfilter = nm_setting_tc_config_get_tfilter (s_tc, i); + NMTCAction *action; + NMPObject *t = nmp_object_new (NMP_OBJECT_TYPE_TFILTER, NULL); + NMPlatformTfilter *tfilter = NMP_OBJECT_CAST_TFILTER (t); + + tfilter->ifindex = ip_ifindex; + tfilter->kind = nm_tc_tfilter_get_kind (s_tfilter); + tfilter->addr_family = AF_UNSPEC; + tfilter->handle = nm_tc_tfilter_get_handle (s_tfilter); + tfilter->parent = nm_tc_tfilter_get_parent (s_tfilter); + tfilter->info = TC_H_MAKE (0, htons (ETH_P_ALL)); + + action = nm_tc_tfilter_get_action (s_tfilter); + if (action) { + GVariant *var; + + tfilter->action.kind = nm_tc_action_get_kind (action); + + if (strcmp (tfilter->action.kind, "simple") == 0) { + var = nm_tc_action_get_attribute (action, "sdata"); + if (var && g_variant_is_of_type (var, G_VARIANT_TYPE_BYTESTRING)) { + g_strlcpy (tfilter->action.simple.sdata, + g_variant_get_bytestring (var), + sizeof (tfilter->action.simple.sdata)); + } + } else if (strcmp (tfilter->action.kind, "mirred") == 0) { + if (nm_tc_action_get_attribute (action, "egress")) + tfilter->action.mirred.egress = TRUE; + + if (nm_tc_action_get_attribute (action, "ingress")) + tfilter->action.mirred.ingress = TRUE; + + if (nm_tc_action_get_attribute (action, "mirror")) + tfilter->action.mirred.mirror = TRUE; + + if (nm_tc_action_get_attribute (action, "redirect")) + tfilter->action.mirred.redirect = TRUE; + + var = nm_tc_action_get_attribute (action, "dev"); + if (var && g_variant_is_of_type (var, G_VARIANT_TYPE_STRING)) { + int ifindex; + + ifindex = nm_platform_link_get_ifindex (platform, + g_variant_get_string (var, NULL)); + if (ifindex > 0) + tfilter->action.mirred.ifindex = ifindex; + } + } + } + + g_ptr_array_add (tfilters, t); + } + + return tfilters; +} diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index 558a262b98..38386c3dab 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -134,4 +134,11 @@ nm_utils_file_is_in_path (const char *abs_filename, /*****************************************************************************/ +GPtrArray *nm_utils_qdiscs_from_tc_setting (NMPlatform *platform, + NMSettingTCConfig *s_tc, + int ip_ifindex); +GPtrArray *nm_utils_tfilters_from_tc_setting (NMPlatform *platform, + NMSettingTCConfig *s_tc, + int ip_ifindex); + #endif /* __NETWORKMANAGER_UTILS_H__ */ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 327df52c6c..30b16f449b 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -19,7 +19,6 @@ #include <linux/if_addr.h> #include <linux/if_arp.h> #include <linux/rtnetlink.h> -#include <linux/pkt_sched.h> #include "nm-std-aux/unaligned.h" #include "nm-glib-aux/nm-dedup-multi.h" @@ -7295,10 +7294,10 @@ tc_commit (NMDevice *self) gs_unref_ptrarray GPtrArray *qdiscs = NULL; gs_unref_ptrarray GPtrArray *tfilters = NULL; NMSettingTCConfig *s_tc = NULL; + NMPlatform *platform; int ip_ifindex; - guint nqdiscs, ntfilters; - guint i; + platform = nm_device_get_platform (self); connection = nm_device_get_applied_connection (self); if (connection) s_tc = nm_connection_get_setting_tc_config (connection); @@ -7308,122 +7307,14 @@ tc_commit (NMDevice *self) return s_tc == NULL; if (s_tc) { - nqdiscs = nm_setting_tc_config_get_num_qdiscs (s_tc); - qdiscs = g_ptr_array_new_full (nqdiscs, (GDestroyNotify) nmp_object_unref); - - for (i = 0; i < nqdiscs; i++) { - NMTCQdisc *s_qdisc = nm_setting_tc_config_get_qdisc (s_tc, i); - NMPObject *q = nmp_object_new (NMP_OBJECT_TYPE_QDISC, NULL); - NMPlatformQdisc *qdisc = NMP_OBJECT_CAST_QDISC (q); - - qdisc->ifindex = ip_ifindex; - - /* Note: kind string is still owned by NMTCTfilter. - * This qdisc instance must not be kept alive beyond this function. - * nm_platform_qdisc_sync() promises to do that. */ - qdisc->kind = nm_tc_qdisc_get_kind (s_qdisc); - - qdisc->addr_family = AF_UNSPEC; - qdisc->handle = nm_tc_qdisc_get_handle (s_qdisc); - 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, 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); - } - -#undef GET_ADDR - - g_ptr_array_add (qdiscs, q); - } - - ntfilters = nm_setting_tc_config_get_num_tfilters (s_tc); - tfilters = g_ptr_array_new_full (ntfilters, (GDestroyNotify) nmp_object_unref); - - for (i = 0; i < ntfilters; i++) { - NMTCTfilter *s_tfilter = nm_setting_tc_config_get_tfilter (s_tc, i); - NMTCAction *action; - NMPObject *q = nmp_object_new (NMP_OBJECT_TYPE_TFILTER, NULL); - NMPlatformTfilter *tfilter = NMP_OBJECT_CAST_TFILTER (q); - - tfilter->ifindex = ip_ifindex; - - /* Note: kind string is still owned by NMTCTfilter. - * This tfilter instance must not be kept alive beyond this function. - * nm_platform_tfilter_sync() promises to do that. */ - tfilter->kind = nm_tc_tfilter_get_kind (s_tfilter); - - tfilter->addr_family = AF_UNSPEC; - tfilter->handle = nm_tc_tfilter_get_handle (s_tfilter); - tfilter->parent = nm_tc_tfilter_get_parent (s_tfilter); - tfilter->info = TC_H_MAKE (0, htons (ETH_P_ALL)); - - action = nm_tc_tfilter_get_action (s_tfilter); - if (action) { - GVariant *var; - - /* Note: kind string is still owned by NMTCAction. - * This tfilter instance must not be kept alive beyond this function. - * nm_platform_tfilter_sync() promises to do that. */ - tfilter->action.kind = nm_tc_action_get_kind (action); - - if (strcmp (tfilter->action.kind, "simple") == 0) { - var = nm_tc_action_get_attribute (action, "sdata"); - if (var && g_variant_is_of_type (var, G_VARIANT_TYPE_BYTESTRING)) { - g_strlcpy (tfilter->action.simple.sdata, - g_variant_get_bytestring (var), - sizeof (tfilter->action.simple.sdata)); - } - } else if (strcmp (tfilter->action.kind, "mirred") == 0) { - if (nm_tc_action_get_attribute (action, "egress")) - tfilter->action.mirred.egress = TRUE; - - if (nm_tc_action_get_attribute (action, "ingress")) - tfilter->action.mirred.ingress = TRUE; - - if (nm_tc_action_get_attribute (action, "mirror")) - tfilter->action.mirred.mirror = TRUE; - - if (nm_tc_action_get_attribute (action, "redirect")) - tfilter->action.mirred.redirect = TRUE; - - var = nm_tc_action_get_attribute (action, "dev"); - if (var && g_variant_is_of_type (var, G_VARIANT_TYPE_STRING)) { - int ifindex; - - ifindex = nm_platform_link_get_ifindex (nm_device_get_platform (self), - g_variant_get_string (var, NULL)); - if (ifindex > 0) - tfilter->action.mirred.ifindex = ifindex; - } - } - } - - g_ptr_array_add (tfilters, q); - } + qdiscs = nm_utils_qdiscs_from_tc_setting (platform, s_tc, ip_ifindex); + tfilters = nm_utils_tfilters_from_tc_setting (platform, s_tc, ip_ifindex); } - if (!nm_platform_qdisc_sync (nm_device_get_platform (self), ip_ifindex, qdiscs)) + if (!nm_platform_qdisc_sync (platform, ip_ifindex, qdiscs)) return FALSE; - if (!nm_platform_tfilter_sync (nm_device_get_platform (self), ip_ifindex, tfilters)) + if (!nm_platform_tfilter_sync (platform, ip_ifindex, tfilters)) return FALSE; return TRUE; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 78e33748c1..c6f8294a7d 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -279,6 +279,10 @@ struct _ifla_vf_vlan_info { /*****************************************************************************/ +#define PSCHED_TIME_UNITS_PER_SEC 1000000 + +/*****************************************************************************/ + typedef enum { INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD, @@ -3660,8 +3664,39 @@ _new_from_nl_routing_rule (struct nlmsghdr *nlh, gboolean id_only) return g_steal_pointer (&obj); } +static guint32 +psched_tick_to_time (NMPlatform *platform, guint32 tick) +{ + static gboolean initialized; + static double tick_in_usec = 1; + + if (!initialized) { + gs_free char *params = NULL; + double clock_factor = 1; + guint32 clock_res; + guint32 t2us; + guint32 us2t; + + initialized = TRUE; + params = nm_platform_sysctl_get (platform, NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/net/psched")); + if ( !params + || sscanf (params, "%08x%08x%08x", &t2us, &us2t, &clock_res) != 3) { + _LOGW ("packet scheduler parameters not available"); + } else { + /* See tc_core_init() in iproute2 */ + if (clock_res == 1000000000) + t2us = us2t; + + clock_factor = (double) clock_res / PSCHED_TIME_UNITS_PER_SEC; + tick_in_usec = (double) t2us / us2t * clock_factor; + } + } + + return tick / tick_in_usec; +} + static NMPObject * -_new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) +_new_from_nl_qdisc (NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_only) { static const struct nla_policy policy[] = { [TCA_KIND] = { .type = NLA_STRING }, @@ -3669,7 +3704,7 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) }; struct nlattr *tb[G_N_ELEMENTS (policy)]; const struct tcmsg *tcm; - NMPObject *obj; + nm_auto_nmpobj NMPObject *obj = NULL; if (nlmsg_parse_arr (nlh, sizeof (*tcm), @@ -3700,42 +3735,77 @@ _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 (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 if (nm_streq0 (obj->qdisc.kind, "tbf")) { + static const struct nla_policy tbf_policy[] = { + [TCA_TBF_PARMS] = { .minlen = sizeof (struct tc_tbf_qopt) }, + [TCA_TBF_RATE64] = { .type = NLA_U64 }, + }; + struct nlattr *tbf_tb[G_N_ELEMENTS (tbf_policy)]; + struct tc_tbf_qopt 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 (nla_parse_nested_arr (tbf_tb, tb[TCA_OPTIONS], tbf_policy) < 0) + return NULL; + if (!tbf_tb[TCA_TBF_PARMS]) + return NULL; + + nla_memcpy_checked_size (&opt, tbf_tb[TCA_TBF_PARMS], sizeof (opt)); + obj->qdisc.tbf.rate = opt.rate.rate; + if (tbf_tb[TCA_TBF_RATE64]) + obj->qdisc.tbf.rate = nla_get_u64 (tbf_tb[TCA_TBF_RATE64]); + obj->qdisc.tbf.burst = ((double) obj->qdisc.tbf.rate * + psched_tick_to_time (platform, opt.buffer)) / + PSCHED_TIME_UNITS_PER_SEC; + obj->qdisc.tbf.limit = opt.limit; + } 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; + } } } } } - return obj; + return g_steal_pointer (&obj); } static NMPObject * @@ -3811,7 +3881,7 @@ nmp_object_new_from_nl (NMPlatform *platform, const NMPCache *cache, struct nl_m case RTM_NEWQDISC: case RTM_DELQDISC: case RTM_GETQDISC: - return _new_from_nl_qdisc (msghdr, id_only); + return _new_from_nl_qdisc (platform, msghdr, id_only); case RTM_NEWTFILTER: case RTM_DELTFILTER: case RTM_GETTFILTER: @@ -4651,29 +4721,65 @@ _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_nest_end (msg, tc_options); + NLA_PUT (msg, TCA_OPTIONS, sizeof (opt), &opt); + } else if (nm_streq (qdisc->kind, "tbf")) { + struct tc_tbf_qopt opt = { }; + + if (!(tc_options = nla_nest_start (msg, TCA_OPTIONS))) + goto nla_put_failure; + + opt.rate.rate = (qdisc->tbf.rate >= (1ULL << 32)) + ? ~0U + : (guint32) qdisc->tbf.rate; + if (qdisc->tbf.limit) + opt.limit = qdisc->tbf.limit; + else if (qdisc->tbf.latency) { + opt.limit = qdisc->tbf.rate * (double) qdisc->tbf.latency + / PSCHED_TIME_UNITS_PER_SEC + + qdisc->tbf.burst; + } + + NLA_PUT (msg, TCA_TBF_PARMS, sizeof (opt), &opt); + if (qdisc->tbf.rate >= (1ULL << 32)) + NLA_PUT_U64 (msg, TCA_TBF_RATE64, qdisc->tbf.rate); + NLA_PUT_U32 (msg, TCA_TBF_BURST, qdisc->tbf.burst); + + nla_nest_end (msg, tc_options); + } 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); + } return g_steal_pointer (&msg); @@ -4789,7 +4895,8 @@ _genl_sock (NMLinuxPlatform *platform) nm_assert (!_pathid); \ nm_assert (_path[0] == '/'); \ nm_assert ( g_str_has_prefix (_path, "/proc/sys/") \ - || g_str_has_prefix (_path, "/sys/")); \ + || g_str_has_prefix (_path, "/sys/") \ + || g_str_has_prefix (_path, "/proc/net")); \ } else { \ nm_assert (_pathid && _pathid[0] && _pathid[0] != '/'); \ nm_assert (_path[0] != '/'); \ diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 20592b1f15..993918df4c 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -6486,6 +6486,26 @@ 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); + } else if (nm_streq0 (qdisc->kind, "tbf")) { + nm_utils_strbuf_append (&buf, &len, " rate %"G_GUINT64_FORMAT, qdisc->tbf.rate); + nm_utils_strbuf_append (&buf, &len, " burst %u", qdisc->tbf.burst); + if (qdisc->tbf.limit) + nm_utils_strbuf_append (&buf, &len, " limit %u", qdisc->tbf.limit); + if (qdisc->tbf.latency) + nm_utils_strbuf_append (&buf, &len, " latency %uns", qdisc->tbf.latency); } return buf0; @@ -6512,6 +6532,20 @@ 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); + } else if (nm_streq0 (obj->kind, "tbf")) { + nm_hash_update_vals (h, + obj->tbf.rate, + obj->tbf.burst, + obj->tbf.limit, + obj->tbf.latency); } } @@ -6538,6 +6572,18 @@ 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); + } else if (nm_streq0 (a->kind, "tbf")) { + NM_CMP_FIELD (a, b, tbf.rate); + NM_CMP_FIELD (a, b, tbf.burst); + NM_CMP_FIELD (a, b, tbf.limit); + NM_CMP_FIELD (a, b, tbf.latency); } return 0; diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index c4eda16cc4..691551c57f 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -630,6 +630,22 @@ typedef struct { } NMPlatformQdiscFqCodel; typedef struct { + unsigned quantum; + int perturb_period; + guint32 limit; + unsigned divisor; + unsigned flows; + unsigned depth; +} NMPlatformQdiscSfq; + +typedef struct { + guint64 rate; + guint32 burst; + guint32 limit; + guint32 latency; +} NMPlatformQdiscTbf; + +typedef struct { __NMPlatformObjWithIfindex_COMMON; /* beware, kind is embedded in an NMPObject, hence you must @@ -642,6 +658,8 @@ typedef struct { guint32 info; union { NMPlatformQdiscFqCodel fq_codel; + NMPlatformQdiscSfq sfq; + NMPlatformQdiscTbf tbf; }; } NMPlatformQdisc; diff --git a/src/platform/tests/test-tc.c b/src/platform/tests/test-tc.c index dc2bf9f214..0c90fcfab6 100644 --- a/src/platform/tests/test-tc.c +++ b/src/platform/tests/test-tc.c @@ -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,94 @@ 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); +} + +static void +test_qdisc_tbf (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, "tbf", TC_H_ROOT); + obj->qdisc.handle = TC_H_MAKE (0x8143 << 16, 0); + obj->qdisc.tbf.rate = 1000000; + obj->qdisc.tbf.burst = 2000; + obj->qdisc.tbf.limit = 3000; + g_ptr_array_add (known, obj); + + obj = qdisc_new (ifindex, "sfq", TC_H_MAKE (0x8143 << 16, 0)); + obj->qdisc.handle = TC_H_MAKE (0x8005 << 16, 0); + 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, ==, 2); + + obj = plat->pdata[0]; + qdisc = NMP_OBJECT_CAST_QDISC (obj); + g_assert_cmpstr (qdisc->kind, ==, "tbf"); + g_assert_cmpint (qdisc->handle, ==, TC_H_MAKE (0x8143 << 16, 0)); + g_assert_cmpint (qdisc->parent, ==, TC_H_ROOT); + g_assert_cmpint (qdisc->tbf.rate, ==, 1000000); + g_assert_cmpint (qdisc->tbf.burst, ==, 2000); + g_assert_cmpint (qdisc->tbf.limit, ==, 3000); + + obj = plat->pdata[1]; + qdisc = NMP_OBJECT_CAST_QDISC (obj); + g_assert_cmpstr (qdisc->kind, ==, "sfq"); + g_assert_cmpint (qdisc->parent, ==, TC_H_MAKE (0x8143 << 16, 0)); + g_assert_cmpint (qdisc->handle, ==, TC_H_MAKE (0x8005 << 16, 0)); +} + /*****************************************************************************/ NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; @@ -128,6 +216,8 @@ _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); + nmtstp_env1_add_test_func ("/link/qdisc/tbf", test_qdisc_tbf, TRUE); } } |