diff options
-rw-r--r-- | libnm-core/nm-utils.c | 9 | ||||
-rw-r--r-- | libnm-core/tests/test-setting.c | 13 | ||||
-rw-r--r-- | src/NetworkManagerUtils.c | 6 | ||||
-rw-r--r-- | src/platform/nm-linux-platform.c | 92 | ||||
-rw-r--r-- | src/platform/nm-platform.c | 18 | ||||
-rw-r--r-- | src/platform/nm-platform.h | 8 | ||||
-rw-r--r-- | src/platform/tests/test-tc.c | 50 |
7 files changed, 189 insertions, 7 deletions
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index e8f4205b56..05846c50aa 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -2318,6 +2318,14 @@ static const NMVariantAttributeSpec *const tc_qdisc_sfq_spec[] = { 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, +}; + 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, ), @@ -2346,6 +2354,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 }, + &(const NMQdiscAttributeSpec) { "tbf", tc_qdisc_tbf_spec }, NULL, }; diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index 4db21ad374..f04e67dcb9 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -2315,6 +2315,19 @@ test_tc_config_qdisc (void) 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 } diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 324e71d2d9..bcb12a564d 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -1180,8 +1180,12 @@ nm_utils_qdiscs_from_tc_setting (NMPlatform *platform, 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); diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 1c3d4d2557..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), @@ -3701,7 +3736,7 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) int remaining; if (nm_streq0 (obj->qdisc.kind, "sfq")) { - struct tc_sfq_qopt_v1 opt = {}; + 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)); @@ -3712,6 +3747,27 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) 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 (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)) @@ -3749,7 +3805,7 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) } } - return obj; + return g_steal_pointer (&obj); } static NMPObject * @@ -3825,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: @@ -4676,6 +4732,29 @@ _nl_msg_new_qdisc (int nlmsg_type, opt.depth = qdisc->sfq.depth; 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; @@ -4816,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 21c4440062..f941b12429 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -6491,6 +6491,13 @@ nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len) 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; @@ -6525,6 +6532,12 @@ nm_platform_qdisc_hash_update (const NMPlatformQdisc *obj, NMHashState *h) 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); } } @@ -6558,6 +6571,11 @@ nm_platform_qdisc_cmp_full (const NMPlatformQdisc *a, 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 8b0ec0eb4f..691551c57f 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -639,6 +639,13 @@ typedef struct { } 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 @@ -652,6 +659,7 @@ typedef struct { 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 0fa4d8d96b..0c90fcfab6 100644 --- a/src/platform/tests/test-tc.c +++ b/src/platform/tests/test-tc.c @@ -152,6 +152,55 @@ test_qdisc_sfq (void) 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; @@ -169,5 +218,6 @@ _nmtstp_setup_tests (void) nmtstp_env1_add_test_func ("/link/qdisc/1", test_qdisc1, 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); } } |