diff options
| author | Michael Biebl <biebl@debian.org> | 2020-06-28 18:58:14 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2020-06-28 18:58:14 +0200 |
| commit | a54ac63bbf9b2c71026ac9028a8ffaf186cf3c82 (patch) | |
| tree | 6a32883bd916c4096357b35298beff6db8bcd0b5 /src/platform | |
| parent | 45e8e1149027529194982212c804c0468aa01d98 (diff) | |
New upstream version 1.25.90 upstream/1.25.90
Diffstat (limited to 'src/platform')
| -rw-r--r-- | src/platform/nm-fake-platform.c | 15 | ||||
| -rw-r--r-- | src/platform/nm-linux-platform.c | 235 | ||||
| -rw-r--r-- | src/platform/nm-netlink.h | 14 | ||||
| -rw-r--r-- | src/platform/nm-platform-utils.c | 232 | ||||
| -rw-r--r-- | src/platform/nm-platform-utils.h | 23 | ||||
| -rw-r--r-- | src/platform/nm-platform.c | 223 | ||||
| -rw-r--r-- | src/platform/nm-platform.h | 47 | ||||
| -rw-r--r-- | src/platform/tests/meson.build | 2 | ||||
| -rw-r--r-- | src/platform/tests/test-common.c | 5 | ||||
| -rw-r--r-- | src/platform/tests/test-link.c | 10 | ||||
| -rw-r--r-- | src/platform/tests/test-tc.c | 223 |
11 files changed, 887 insertions, 142 deletions
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 4a81dc49..d9cf2de5 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -268,7 +268,6 @@ link_add_pre (NMPlatform *platform, g_assert (address_len == 0); device->obj = o; - device->udi = g_strdup_printf ("fake:%d", ifindex); device->ip6_lladdr = *nmtst_inet6_from_string (ip6_lladdr); return device; @@ -420,7 +419,6 @@ link_delete (NMPlatform *platform, int ifindex) return FALSE; obj_old = g_steal_pointer (&device->obj); - nm_clear_g_free (&device->udi); cache_op = nmp_cache_remove (nm_platform_get_cache (platform), obj_old, @@ -633,16 +631,6 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) return 0; } -static const char * -link_get_udi (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - if (!device) - return NULL; - return device->udi; -} - static gboolean link_get_driver_info (NMPlatform *platform, int ifindex, @@ -1345,7 +1333,6 @@ finalize (GObject *object) for (i = 0; i < priv->links->len; i++) { NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); - g_free (device->udi); nm_clear_pointer (&device->obj, nmp_object_unref); } g_array_unref (priv->links); @@ -1371,8 +1358,6 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->link_get_udi = link_get_udi; - platform_class->link_set_up = link_set_up; platform_class->link_set_down = link_set_down; platform_class->link_set_arp = link_set_arp; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index a721fc7c..e2c45c88 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: @@ -4644,36 +4714,72 @@ _nl_msg_new_qdisc (int nlmsg_type, .tcm_info = qdisc->info, }; - msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags | NMP_NLM_FLAG_F_ECHO); if (nlmsg_append_struct (msg, &tcm) < 0) goto nla_put_failure; 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); @@ -4697,7 +4803,7 @@ _nl_msg_new_tfilter (int nlmsg_type, .tcm_info = tfilter->info, }; - msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags | NMP_NLM_FLAG_F_ECHO); if (nlmsg_append_struct (msg, &tcm) < 0) goto nla_put_failure; @@ -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] != '/'); \ @@ -7058,18 +7165,6 @@ link_set_noarp (NMPlatform *platform, int ifindex) return (link_change_flags (platform, ifindex, IFF_NOARP, IFF_NOARP) >= 0); } -static const char * -link_get_udi (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj = nm_platform_link_get_obj (platform, ifindex, TRUE); - - if ( !obj - || !obj->_link.netlink.is_in_netlink - || !obj->_link.udev.device) - return NULL; - return udev_device_get_syspath (obj->_link.udev.device); -} - static int link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled) { @@ -9211,8 +9306,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_arp = link_set_arp; platform_class->link_set_noarp = link_set_noarp; - platform_class->link_get_udi = link_get_udi; - platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; platform_class->link_set_token = link_set_token; diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h index 0ac5b3b7..a5124f52 100644 --- a/src/platform/nm-netlink.h +++ b/src/platform/nm-netlink.h @@ -77,17 +77,13 @@ struct nla_policy { /* static asserts that @tb and @policy are suitable arguments to nla_parse(). */ #define _nl_static_assert_tb(tb, policy) \ G_STMT_START { \ - \ G_STATIC_ASSERT_EXPR (G_N_ELEMENTS (tb) > 0); \ \ - /* we allow @policy to be either NULL or a C array. */ \ - G_STATIC_ASSERT_EXPR ( sizeof (policy) == sizeof (NULL) \ - || G_N_ELEMENTS (tb) == (sizeof (policy) / sizeof (struct nla_policy))); \ - \ - /* For above check to work, we don't support policy being an array with same size as - * sizeof(NULL), otherwise, the compile time check breaks down. */ \ - G_STATIC_ASSERT_EXPR (sizeof (NULL) != G_N_ELEMENTS (tb) * sizeof (struct nla_policy)); \ - \ + /* We allow @policy to be either a C array or NULL. The sizeof() + * must either match the expected array size or the sizeof(NULL), + * but not both. */ \ + G_STATIC_ASSERT_EXPR ( (sizeof (policy) == G_N_ELEMENTS (tb) * sizeof (struct nla_policy)) \ + ^ (sizeof (policy) == sizeof (NULL))); \ } G_STMT_END /*****************************************************************************/ diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c index 98884cd7..6c14bc5c 100644 --- a/src/platform/nm-platform-utils.c +++ b/src/platform/nm-platform-utils.c @@ -264,16 +264,20 @@ out: static NM_UTILS_ENUM2STR_DEFINE (_ethtool_cmd_to_string, guint32, + NM_UTILS_ENUM2STR (ETHTOOL_GCOALESCE, "ETHTOOL_GCOALESCE"), NM_UTILS_ENUM2STR (ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"), NM_UTILS_ENUM2STR (ETHTOOL_GFEATURES, "ETHTOOL_GFEATURES"), NM_UTILS_ENUM2STR (ETHTOOL_GLINK, "ETHTOOL_GLINK"), NM_UTILS_ENUM2STR (ETHTOOL_GPERMADDR, "ETHTOOL_GPERMADDR"), + NM_UTILS_ENUM2STR (ETHTOOL_GRINGPARAM, "ETHTOOL_GRINGPARAM"), NM_UTILS_ENUM2STR (ETHTOOL_GSET, "ETHTOOL_GSET"), NM_UTILS_ENUM2STR (ETHTOOL_GSSET_INFO, "ETHTOOL_GSSET_INFO"), NM_UTILS_ENUM2STR (ETHTOOL_GSTATS, "ETHTOOL_GSTATS"), NM_UTILS_ENUM2STR (ETHTOOL_GSTRINGS, "ETHTOOL_GSTRINGS"), NM_UTILS_ENUM2STR (ETHTOOL_GWOL, "ETHTOOL_GWOL"), + NM_UTILS_ENUM2STR (ETHTOOL_SCOALESCE, "ETHTOOL_SCOALESCE"), NM_UTILS_ENUM2STR (ETHTOOL_SFEATURES, "ETHTOOL_SFEATURES"), + NM_UTILS_ENUM2STR (ETHTOOL_SRINGPARAM, "ETHTOOL_SRINGPARAM"), NM_UTILS_ENUM2STR (ETHTOOL_SSET, "ETHTOOL_SSET"), NM_UTILS_ENUM2STR (ETHTOOL_SWOL, "ETHTOOL_SWOL"), ); @@ -516,8 +520,8 @@ _ASSERT_ethtool_feature_infos (void) for (k = 0; k < i; k++) g_assert (inf->ethtool_id != _ethtool_feature_infos[k].ethtool_id); - g_assert (!found[inf->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST]); - found[inf->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST] = TRUE; + g_assert (!found[_NM_ETHTOOL_ID_FEATURE_AS_IDX (inf->ethtool_id)]); + found[_NM_ETHTOOL_ID_FEATURE_AS_IDX (inf->ethtool_id)] = TRUE; kstate.idx_kernel_name = inf->n_kernel_names - 1; g_assert ((guint) kstate.idx_kernel_name == (guint) (inf->n_kernel_names - 1)); @@ -607,13 +611,13 @@ ethtool_get_features (SocketHandle *shandle) nm_assert (states_plist_n < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS (_ethtool_feature_infos)); - if (!states->states_indexed[info->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST]) - states->states_indexed[info->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST] = &states_plist0[states_plist_n]; + if (!states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX (info->ethtool_id)]) + states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX (info->ethtool_id)] = &states_plist0[states_plist_n]; ((const NMEthtoolFeatureState **) states_plist0)[states_plist_n] = kstate; states_plist_n++; } - if (states && states->states_indexed[info->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST]) { + if (states && states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX (info->ethtool_id)]) { nm_assert (states_plist_n < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS (_ethtool_feature_infos)); nm_assert (!states_plist0[states_plist_n]); states_plist_n++; @@ -804,6 +808,224 @@ nmp_utils_ethtool_set_features (int ifindex, return success; } +static gboolean +ethtool_get_coalesce (SocketHandle *shandle, + NMEthtoolCoalesceState *coalesce) +{ + struct ethtool_coalesce eth_data; + + eth_data.cmd = ETHTOOL_GCOALESCE; + + if (_ethtool_call_handle (shandle, + ð_data, + sizeof (struct ethtool_coalesce)) != 0) + return FALSE; + + *coalesce = (NMEthtoolCoalesceState) { + .s = { + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_USECS)] = eth_data.rx_coalesce_usecs, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_FRAMES)] = eth_data.rx_max_coalesced_frames, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ)] = eth_data.rx_coalesce_usecs_irq, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ)] = eth_data.rx_max_coalesced_frames_irq, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_USECS)] = eth_data.tx_coalesce_usecs, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_FRAMES)] = eth_data.tx_max_coalesced_frames, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ)] = eth_data.tx_coalesce_usecs_irq, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ)] = eth_data.tx_max_coalesced_frames_irq, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS)] = eth_data.stats_block_coalesce_usecs, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX)] = eth_data.use_adaptive_rx_coalesce, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX)] = eth_data.use_adaptive_tx_coalesce, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW)] = eth_data.pkt_rate_low, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW)] = eth_data.rx_coalesce_usecs_low, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW)] = eth_data.rx_max_coalesced_frames_low, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW)] = eth_data.tx_coalesce_usecs_low, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW)] = eth_data.tx_max_coalesced_frames_low, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH)] = eth_data.pkt_rate_high, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH)] = eth_data.rx_coalesce_usecs_high, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH)] = eth_data.rx_max_coalesced_frames_high, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH)] = eth_data.tx_coalesce_usecs_high, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH)] = eth_data.tx_max_coalesced_frames_high, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL)] = eth_data.rate_sample_interval, + } + }; + return TRUE; +} + + +gboolean +nmp_utils_ethtool_get_coalesce (int ifindex, + NMEthtoolCoalesceState *coalesce) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (coalesce, FALSE); + + if (!ethtool_get_coalesce (&shandle, coalesce)) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failure getting coalesce settings", + ifindex, + "get-coalesce"); + return FALSE; + } + + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: retrieved kernel coalesce settings", + ifindex, + "get-coalesce"); + return TRUE; +} + +static gboolean +ethtool_set_coalesce (SocketHandle *shandle, + const NMEthtoolCoalesceState *coalesce) +{ + struct ethtool_coalesce eth_data; + gboolean success; + + nm_assert (shandle); + nm_assert (coalesce); + + eth_data = (struct ethtool_coalesce) { + .cmd = ETHTOOL_SCOALESCE, + .rx_coalesce_usecs = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_USECS)], + .rx_max_coalesced_frames = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_FRAMES)], + .rx_coalesce_usecs_irq = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ)], + .rx_max_coalesced_frames_irq = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ)], + .tx_coalesce_usecs = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_USECS)], + .tx_max_coalesced_frames = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_FRAMES)], + .tx_coalesce_usecs_irq = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ)], + .tx_max_coalesced_frames_irq = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ)], + .stats_block_coalesce_usecs = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS)], + .use_adaptive_rx_coalesce = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX)], + .use_adaptive_tx_coalesce = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX)], + .pkt_rate_low = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW)], + .rx_coalesce_usecs_low = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW)], + .rx_max_coalesced_frames_low = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW)], + .tx_coalesce_usecs_low = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW)], + .tx_max_coalesced_frames_low = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW)], + .pkt_rate_high = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH)], + .rx_coalesce_usecs_high = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH)], + .rx_max_coalesced_frames_high = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH)], + .tx_coalesce_usecs_high = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH)], + .tx_max_coalesced_frames_high = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH)], + .rate_sample_interval = coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX (NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL)], + }; + + success = (_ethtool_call_handle (shandle, + ð_data, + sizeof (struct ethtool_coalesce)) == 0); + return success; +} + +gboolean +nmp_utils_ethtool_set_coalesce (int ifindex, + const NMEthtoolCoalesceState *coalesce) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (coalesce, FALSE); + + if (!ethtool_set_coalesce (&shandle, coalesce)) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failure setting coalesce settings", + ifindex, + "set-coalesce"); + return FALSE; + } + + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: set kernel coalesce settings", + ifindex, + "set-coalesce"); + return TRUE; +} + +static gboolean +ethtool_get_ring (SocketHandle *shandle, + NMEthtoolRingState *ring) +{ + struct ethtool_ringparam eth_data; + + eth_data.cmd = ETHTOOL_GRINGPARAM; + + if (_ethtool_call_handle (shandle, + ð_data, + sizeof (struct ethtool_ringparam)) != 0) + return FALSE; + + ring->rx_pending = eth_data.rx_pending; + ring->rx_jumbo_pending = eth_data.rx_jumbo_pending; + ring->rx_mini_pending = eth_data.rx_mini_pending; + ring->tx_pending = eth_data.tx_pending; + + return TRUE; +} + +gboolean +nmp_utils_ethtool_get_ring (int ifindex, + NMEthtoolRingState *ring) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (ring, FALSE); + + if (!ethtool_get_ring (&shandle, ring)) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failure getting ring settings", + ifindex, + "get-ring"); + return FALSE; + } + + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: retrieved kernel ring settings", + ifindex, + "get-ring"); + return TRUE; +} + +static gboolean +ethtool_set_ring (SocketHandle *shandle, + const NMEthtoolRingState *ring) +{ + gboolean success; + struct ethtool_ringparam eth_data; + + g_return_val_if_fail (shandle, FALSE); + g_return_val_if_fail (ring, FALSE); + + eth_data = (struct ethtool_ringparam) { + .cmd = ETHTOOL_SRINGPARAM, + .rx_pending = ring->rx_pending, + .rx_jumbo_pending = ring->rx_jumbo_pending, + .rx_mini_pending = ring->rx_mini_pending, + .tx_pending = ring->tx_pending, + }; + + success = (_ethtool_call_handle (shandle, + ð_data, + sizeof (struct ethtool_ringparam)) == 0); + return success; +} + +gboolean +nmp_utils_ethtool_set_ring (int ifindex, + const NMEthtoolRingState *ring) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (ring, FALSE); + + if (!ethtool_set_ring (&shandle, ring)) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failure setting ring settings", + ifindex, + "set-ring"); + return FALSE; + } + + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: set kernel ring settings", + ifindex, + "set-ring"); + return TRUE; +} + /*****************************************************************************/ gboolean diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h index a62d828c..062c8597 100644 --- a/src/platform/nm-platform-utils.h +++ b/src/platform/nm-platform-utils.h @@ -92,6 +92,29 @@ gboolean nmp_utils_ethtool_set_features (int ifindex, const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, gboolean do_set /* or reset */); +struct _NMEthtoolCoalesceState { + guint32 s[_NM_ETHTOOL_ID_COALESCE_NUM /* indexed by (NMEthtoolID - _NM_ETHTOOL_ID_COALESCE_FIRST) */]; +}; + +gboolean nmp_utils_ethtool_get_coalesce (int ifindex, + NMEthtoolCoalesceState *coalesce); + +gboolean nmp_utils_ethtool_set_coalesce (int ifindex, + const NMEthtoolCoalesceState *coalesce); + +struct _NMEthtoolRingState { + guint32 rx_pending; + guint32 rx_mini_pending; + guint32 rx_jumbo_pending; + guint32 tx_pending; +}; + +gboolean nmp_utils_ethtool_get_ring (int ifindex, + NMEthtoolRingState *ring); + +gboolean nmp_utils_ethtool_set_ring (int ifindex, + const NMEthtoolRingState *ring); + /*****************************************************************************/ gboolean nmp_utils_mii_supports_carrier_detect (int ifindex); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 9bbc3745..c023f435 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -41,6 +41,14 @@ G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_ G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Route, network)); G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Route, network)); +G_STATIC_ASSERT (_nm_alignof (NMPlatformIPRoute) == _nm_alignof (NMPlatformIP4Route)); +G_STATIC_ASSERT (_nm_alignof (NMPlatformIPRoute) == _nm_alignof (NMPlatformIP6Route)); +G_STATIC_ASSERT (_nm_alignof (NMPlatformIPRoute) == _nm_alignof (NMPlatformIPXRoute)); + +G_STATIC_ASSERT (_nm_alignof (NMPlatformIPAddress) == _nm_alignof (NMPlatformIP4Address)); +G_STATIC_ASSERT (_nm_alignof (NMPlatformIPAddress) == _nm_alignof (NMPlatformIP6Address)); +G_STATIC_ASSERT (_nm_alignof (NMPlatformIPAddress) == _nm_alignof (NMPlatformIPXAddress)); + /*****************************************************************************/ G_STATIC_ASSERT (sizeof ( ((NMPLinkAddress *) NULL)->data ) == NM_UTILS_HWADDR_LEN_MAX); @@ -310,6 +318,7 @@ NM_UTILS_LOOKUP_STR_DEFINE (_nmp_nlm_flag_to_string_lookup, NMPNlmFlags, NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_F_APPEND), NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_FMASK), NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE), + NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_F_ECHO), ); #define _nmp_nlm_flag_to_string(flags) \ @@ -1403,6 +1412,27 @@ nm_platform_link_get_type_name (NMPlatform *self, int ifindex) return obj->link.kind ?: "unknown"; } +static gboolean +link_get_udev_property (NMPlatform *self, + int ifindex, + const char *name, + const char **out_value) +{ + struct udev_device *udevice = NULL; + const char *uproperty; + + udevice = nm_platform_link_get_udev_device (self, ifindex); + if (!udevice) + return FALSE; + + uproperty = udev_device_get_property_value (udevice, name); + if (!uproperty) + return FALSE; + + NM_SET_OUT (out_value, uproperty); + return TRUE; +} + /** * nm_platform_link_get_unmanaged: * @self: platform instance @@ -1415,26 +1445,14 @@ nm_platform_link_get_type_name (NMPlatform *self, int ifindex) gboolean nm_platform_link_get_unmanaged (NMPlatform *self, int ifindex, gboolean *unmanaged) { - const NMPObject *link; - struct udev_device *udevice = NULL; - const char *uproperty; + const char *value; - _CHECK_SELF (self, klass, FALSE); - - link = nmp_cache_lookup_link (nm_platform_get_cache (self), ifindex); - if (!link) - return FALSE; - - udevice = link->_link.udev.device; - if (!udevice) - return FALSE; - - uproperty = udev_device_get_property_value (udevice, "NM_UNMANAGED"); - if (!uproperty) - return FALSE; + if (link_get_udev_property (self, ifindex, "NM_UNMANAGED", &value)) { + NM_SET_OUT (unmanaged, nm_udev_utils_property_as_boolean (value)); + return TRUE; + } - *unmanaged = nm_udev_utils_property_as_boolean (uproperty); - return TRUE; + return FALSE; } /** @@ -1571,7 +1589,6 @@ nm_platform_link_set_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6Iface _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (iid.id, FALSE); if (klass->link_set_token) return klass->link_set_token (self, ifindex, iid); @@ -1581,13 +1598,20 @@ nm_platform_link_set_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6Iface const char * nm_platform_link_get_udi (NMPlatform *self, int ifindex) { - _CHECK_SELF (self, klass, FALSE); + struct udev_device *device; - g_return_val_if_fail (ifindex >= 0, NULL); + device = nm_platform_link_get_udev_device (self, ifindex); + return device ? udev_device_get_syspath (device) : NULL; +} - if (klass->link_get_udi) - return klass->link_get_udi (self, ifindex); - return NULL; +const char * +nm_platform_link_get_path (NMPlatform *self, int ifindex) +{ + const char *value = NULL; + + link_get_udev_property (self, ifindex, "ID_PATH", &value); + + return value; } struct udev_device * @@ -3214,6 +3238,56 @@ nm_platform_ethtool_set_features (NMPlatform *self, return nmp_utils_ethtool_set_features (ifindex, features, requested, do_set); } +gboolean +nm_platform_ethtool_get_link_coalesce (NMPlatform *self, + int ifindex, + NMEthtoolCoalesceState *coalesce) +{ + _CHECK_SELF_NETNS (self, klass, netns, FALSE); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (coalesce, FALSE); + + return nmp_utils_ethtool_get_coalesce (ifindex, coalesce); +} + +gboolean +nm_platform_ethtool_set_coalesce (NMPlatform *self, + int ifindex, + const NMEthtoolCoalesceState *coalesce) +{ + _CHECK_SELF_NETNS (self, klass, netns, FALSE); + + g_return_val_if_fail (ifindex > 0, FALSE); + + return nmp_utils_ethtool_set_coalesce (ifindex, coalesce); +} + +gboolean +nm_platform_ethtool_get_link_ring (NMPlatform *self, + int ifindex, + NMEthtoolRingState *ring) +{ + _CHECK_SELF_NETNS (self, klass, netns, FALSE); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (ring, FALSE); + + return nmp_utils_ethtool_get_ring (ifindex, ring); +} + +gboolean +nm_platform_ethtool_set_ring (NMPlatform *self, + int ifindex, + const NMEthtoolRingState *ring) +{ + _CHECK_SELF_NETNS (self, klass, netns, FALSE); + + g_return_val_if_fail (ifindex > 0, FALSE); + + return nmp_utils_ethtool_set_ring (ifindex, ring); +} + /*****************************************************************************/ const NMDedupMultiHeadEntry * @@ -4978,12 +5052,15 @@ nm_platform_qdisc_sync (NMPlatform *self, known_qdiscs_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash, (GEqualFunc) nmp_object_id_equal); - if (known_qdiscs) { for (i = 0; i < known_qdiscs->len; i++) { const NMPObject *q = g_ptr_array_index (known_qdiscs, i); - g_hash_table_insert (known_qdiscs_idx, (gpointer) q, (gpointer) q); + if (!g_hash_table_insert (known_qdiscs_idx, (gpointer) q, (gpointer) q)) { + _LOGW ("duplicate qdisc %s", nm_platform_qdisc_to_string (&q->qdisc, NULL, 0)); + return FALSE; + } + } } @@ -4992,13 +5069,34 @@ nm_platform_qdisc_sync (NMPlatform *self, NMP_OBJECT_TYPE_QDISC, ifindex), NULL, NULL); - if (plat_qdiscs) { for (i = 0; i < plat_qdiscs->len; i++) { - const NMPObject *q = g_ptr_array_index (plat_qdiscs, i); + const NMPObject *p = g_ptr_array_index (plat_qdiscs, i); + const NMPObject *k; - if (!g_hash_table_lookup (known_qdiscs_idx, q)) - success &= nm_platform_object_delete (self, q); + /* look up known qdisc with same parent */ + k = g_hash_table_lookup (known_qdiscs_idx, p); + + if (k) { + const NMPlatformQdisc *qdisc_k = NMP_OBJECT_CAST_QDISC (k); + const NMPlatformQdisc *qdisc_p = NMP_OBJECT_CAST_QDISC (p); + + /* check other fields */ + if ( nm_platform_qdisc_cmp_full (qdisc_k, qdisc_p, FALSE) != 0 + || ( qdisc_k->handle != qdisc_p->handle + && qdisc_k != 0)) { + k = NULL; + } + } + + if (k) { + g_hash_table_remove (known_qdiscs_idx, k); + } else { + /* can't delete qdisc with zero handle */ + if (TC_H_MAJ (p->qdisc.handle) != 0) { + success &= nm_platform_object_delete (self, p); + } + } } } @@ -5006,8 +5104,10 @@ nm_platform_qdisc_sync (NMPlatform *self, for (i = 0; i < known_qdiscs->len; i++) { const NMPObject *q = g_ptr_array_index (known_qdiscs, i); - success &= (nm_platform_qdisc_add (self, NMP_NLM_FLAG_ADD, - NMP_OBJECT_CAST_QDISC (q)) >= 0); + if (g_hash_table_contains (known_qdiscs_idx, q)) { + success &= (nm_platform_qdisc_add (self, NMP_NLM_FLAG_ADD, + NMP_OBJECT_CAST_QDISC (q)) >= 0); + } } } @@ -6402,6 +6502,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; @@ -6428,18 +6548,35 @@ 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); } } int -nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b) +nm_platform_qdisc_cmp_full (const NMPlatformQdisc *a, + const NMPlatformQdisc *b, + gboolean compare_handle) { NM_CMP_SELF (a, b); NM_CMP_FIELD (a, b, ifindex); NM_CMP_FIELD (a, b, parent); NM_CMP_FIELD_STR_INTERNED (a, b, kind); NM_CMP_FIELD (a, b, addr_family); - NM_CMP_FIELD (a, b, handle); + if (compare_handle) + NM_CMP_FIELD (a, b, handle); NM_CMP_FIELD (a, b, info); if (nm_streq0 (a->kind, "fq_codel")) { @@ -6451,11 +6588,29 @@ nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b) 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; } +int +nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b) +{ + return nm_platform_qdisc_cmp_full (a, b, TRUE); +} + const char * nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsize len) { diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 7d10d909..aa3551a6 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -69,6 +69,8 @@ typedef gboolean (*NMPObjectPredicateFunc) (const NMPObject *obj, #define NM_GRE_KEY 0x2000 typedef enum { + NMP_NLM_FLAG_F_ECHO = 0x08, /* NLM_F_ECHO, Echo this request */ + /* use our own platform enum for the nlmsg-flags. Otherwise, we'd have * to include <linux/netlink.h> */ NMP_NLM_FLAG_F_REPLACE = 0x100, /* NLM_F_REPLACE, Override existing */ @@ -628,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 @@ -640,6 +658,8 @@ typedef struct { guint32 info; union { NMPlatformQdiscFqCodel fq_codel; + NMPlatformQdiscSfq sfq; + NMPlatformQdiscTbf tbf; }; } NMPlatformQdisc; @@ -990,9 +1010,6 @@ typedef struct { gboolean (*link_set_arp) (NMPlatform *self, int ifindex); gboolean (*link_set_noarp) (NMPlatform *self, int ifindex); - const char *(*link_get_udi) (NMPlatform *self, int ifindex); - struct udev_device *(*link_get_udev_device) (NMPlatform *self, int ifindex); - int (*link_set_user_ipv6ll_enabled) (NMPlatform *self, int ifindex, gboolean enabled); gboolean (*link_set_token) (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid); @@ -1613,6 +1630,7 @@ gboolean nm_platform_link_set_arp (NMPlatform *self, int ifindex); gboolean nm_platform_link_set_noarp (NMPlatform *self, int ifindex); const char *nm_platform_link_get_udi (NMPlatform *self, int ifindex); +const char *nm_platform_link_get_path (NMPlatform *self, int ifindex); struct udev_device *nm_platform_link_get_udev_device (NMPlatform *self, int ifindex); @@ -1907,6 +1925,9 @@ nm_platform_routing_rule_cmp_full (const NMPlatformRoutingRule *a, const NMPlatf } int nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b); +int nm_platform_qdisc_cmp_full (const NMPlatformQdisc *a, + const NMPlatformQdisc *b, + gboolean compare_handle); int nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b); void nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h); @@ -1954,6 +1975,26 @@ gboolean nm_platform_ethtool_set_features (NMPlatform *self, const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, gboolean do_set /* or reset */); +typedef struct _NMEthtoolCoalesceState NMEthtoolCoalesceState; + +gboolean nm_platform_ethtool_get_link_coalesce (NMPlatform *self, + int ifindex, + NMEthtoolCoalesceState *coalesce); + +gboolean nm_platform_ethtool_set_coalesce (NMPlatform *self, + int ifindex, + const NMEthtoolCoalesceState *coalesce); + +typedef struct _NMEthtoolRingState NMEthtoolRingState; + +gboolean nm_platform_ethtool_get_link_ring (NMPlatform *self, + int ifindex, + NMEthtoolRingState *ring); + +gboolean nm_platform_ethtool_set_ring (NMPlatform *self, + int ifindex, + const NMEthtoolRingState *ring); + const char * nm_platform_link_duplex_type_to_string (NMPlatformLinkDuplexType duplex); void nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, diff --git a/src/platform/tests/meson.build b/src/platform/tests/meson.build index 4a50bca9..f96850cc 100644 --- a/src/platform/tests/meson.build +++ b/src/platform/tests/meson.build @@ -14,6 +14,8 @@ test_units = [ ['test-platform-general', 'test-platform-general.c', test_c_flags, default_test_timeout], ['test-route-fake', 'test-route.c', test_fake_c_flags, default_test_timeout], ['test-route-linux', 'test-route.c', test_linux_c_flags, default_test_timeout], + ['test-tc-fake', 'test-tc.c', test_fake_c_flags, default_test_timeout], + ['test-tc-linux', 'test-tc.c', test_linux_c_flags, default_test_timeout], ] foreach test_unit: test_units diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index f28dfa3a..9f6a29bc 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -557,6 +557,7 @@ nmtstp_wait_for_signal (NMPlatform *platform, gint64 timeout_msec) { WaitForSignalData data = { 0 }; gulong id_link, id_ip4_address, id_ip6_address, id_ip4_route, id_ip6_route; + gulong id_qdisc, id_tfilter; _init_platform (&platform, FALSE); @@ -567,6 +568,8 @@ nmtstp_wait_for_signal (NMPlatform *platform, gint64 timeout_msec) id_ip6_address = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); id_ip4_route = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); id_ip6_route = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); + id_qdisc = g_signal_connect (platform, NM_PLATFORM_SIGNAL_QDISC_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); + id_tfilter = g_signal_connect (platform, NM_PLATFORM_SIGNAL_TFILTER_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); /* if timeout_msec is negative, it means the wait-time already expired. * Maybe, we should do nothing and return right away, without even @@ -589,6 +592,8 @@ nmtstp_wait_for_signal (NMPlatform *platform, gint64 timeout_msec) g_assert (nm_clear_g_signal_handler (platform, &id_ip6_address)); g_assert (nm_clear_g_signal_handler (platform, &id_ip4_route)); g_assert (nm_clear_g_signal_handler (platform, &id_ip6_route)); + g_assert (nm_clear_g_signal_handler (platform, &id_tfilter)); + g_assert (nm_clear_g_signal_handler (platform, &id_qdisc)); nm_clear_pointer (&data.loop, g_main_loop_unref); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index ea4a2c5a..d7285c83 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -3290,14 +3290,14 @@ test_ethtool_features_get (void) g_ptr_array_add (gfree_keeper, requested); if (i_run == 0) { - requested[NM_ETHTOOL_ID_FEATURE_RX] = NM_TERNARY_FALSE; - requested[NM_ETHTOOL_ID_FEATURE_TSO] = NM_TERNARY_FALSE; - requested[NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION] = NM_TERNARY_FALSE; + requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX (NM_ETHTOOL_ID_FEATURE_RX)] = NM_TERNARY_FALSE; + requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX (NM_ETHTOOL_ID_FEATURE_TSO)] = NM_TERNARY_FALSE; + requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX (NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION)] = NM_TERNARY_FALSE; } else if (i_run == 1) do_set = FALSE; else if (i_run == 2) { - requested[NM_ETHTOOL_ID_FEATURE_TSO] = NM_TERNARY_FALSE; - requested[NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION] = NM_TERNARY_TRUE; + requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX (NM_ETHTOOL_ID_FEATURE_TSO)] = NM_TERNARY_FALSE; + requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX (NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION)] = NM_TERNARY_TRUE; } else if (i_run == 3) do_set = FALSE; diff --git a/src/platform/tests/test-tc.c b/src/platform/tests/test-tc.c new file mode 100644 index 00000000..0c90fcfa --- /dev/null +++ b/src/platform/tests/test-tc.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: LGPL-2.1+ + +#include "nm-default.h" + +#include <linux/pkt_sched.h> + +#include "nm-test-utils-core.h" +#include "platform/nmp-object.h" +#include "platform/nmp-netns.h" +#include "platform/nm-platform-utils.h" +#include "test-common.h" + +static NMPObject * +qdisc_new (int ifindex, const char *kind, guint32 parent) +{ + NMPObject *obj; + + obj = nmp_object_new (NMP_OBJECT_TYPE_QDISC, NULL); + obj->qdisc = (NMPlatformQdisc) { + .ifindex = ifindex, + .kind = kind, + .parent = parent, + }; + + return obj; +} + +static GPtrArray * +qdiscs_lookup (int ifindex) +{ + NMPLookup lookup; + + return nm_platform_lookup_clone (NM_PLATFORM_GET, + nmp_lookup_init_object (&lookup, + NMP_OBJECT_TYPE_QDISC, + ifindex), + NULL, NULL); +} + +static void +test_qdisc1 (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_run_command_check ("tc qdisc add dev %s root sfq", DEVICE_NAME); + + nmtstp_wait_for_signal (NM_PLATFORM_GET, 0); + + known = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + g_ptr_array_add (known, qdisc_new (ifindex, "fq_codel", TC_H_ROOT)); + g_ptr_array_add (known, qdisc_new (ifindex, "ingress", TC_H_INGRESS)); + + 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_cmpint (qdisc->parent, ==, TC_H_ROOT); + g_assert_cmpstr (qdisc->kind, ==, "fq_codel"); + + obj = plat->pdata[1]; + qdisc = NMP_OBJECT_CAST_QDISC (obj); + g_assert_cmpint (qdisc->parent, ==, TC_H_INGRESS); + g_assert_cmpstr (qdisc->kind, ==, "ingress"); +} + +static void +test_qdisc_fq_codel (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, "fq_codel", TC_H_ROOT); + obj->qdisc.handle = TC_H_MAKE (0x8142 << 16, 0); + obj->qdisc.fq_codel.limit = 2048; + obj->qdisc.fq_codel.flows = 64; + obj->qdisc.fq_codel.quantum = 1000; + 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, ==, "fq_codel"); + g_assert_cmpint (qdisc->handle, ==, TC_H_MAKE (0x8142 << 16, 0)); + g_assert_cmpint (qdisc->parent, ==, TC_H_ROOT); + g_assert_cmpint (qdisc->fq_codel.limit, ==, 2048); + g_assert_cmpint (qdisc->fq_codel.flows, ==, 64); + 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; + +void +_nmtstp_init_tests (int *argc, char ***argv) +{ + nmtst_init_with_logging (argc, argv, NULL, "ALL"); +} + +void +_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/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); + } +} |