summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2020-06-08 15:32:10 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2020-06-08 15:32:10 +0200
commitd1e8eb791c21eb0f0a690adbc626e10a7291a918 (patch)
treec0ac91db88ba9f6c0a5f46d787b699d9b5c50e29
parentd507563a80d3e171017700f7b268940b63f5708d (diff)
parent934777120b90dbf77f0c6e7b9f25451cc21fbc08 (diff)
downloadNetworkManager-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.c66
-rw-r--r--libnm-core/tests/test-setting.c38
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.c10
-rw-r--r--src/NetworkManagerUtils.c138
-rw-r--r--src/NetworkManagerUtils.h7
-rw-r--r--src/devices/nm-device.c121
-rw-r--r--src/platform/nm-linux-platform.c217
-rw-r--r--src/platform/nm-platform.c46
-rw-r--r--src/platform/nm-platform.h18
-rw-r--r--src/platform/tests/test-tc.c94
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);
}
}