mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-05 07:38:20 +02:00
tc: add support for tbf qdisc
Add support for Token Bucket Filter queueing discipline.
This commit is contained in:
parent
b22b4f9101
commit
934777120b
7 changed files with 189 additions and 7 deletions
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2318,6 +2318,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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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] != '/'); \
|
||||
|
|
|
|||
|
|
@ -6499,6 +6499,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;
|
||||
|
|
@ -6533,6 +6540,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6566,6 +6579,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;
|
||||
|
|
|
|||
|
|
@ -638,6 +638,13 @@ typedef struct {
|
|||
unsigned depth;
|
||||
} NMPlatformQdiscSfq;
|
||||
|
||||
typedef struct {
|
||||
guint64 rate;
|
||||
guint32 burst;
|
||||
guint32 limit;
|
||||
guint32 latency;
|
||||
} NMPlatformQdiscTbf;
|
||||
|
||||
typedef struct {
|
||||
__NMPlatformObjWithIfindex_COMMON;
|
||||
|
||||
|
|
@ -652,6 +659,7 @@ typedef struct {
|
|||
union {
|
||||
NMPlatformQdiscFqCodel fq_codel;
|
||||
NMPlatformQdiscSfq sfq;
|
||||
NMPlatformQdiscTbf tbf;
|
||||
};
|
||||
} NMPlatformQdisc;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue