From patchwork Thu Mar 14 23:02:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sharon K X-Patchwork-Id: 1056734 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="r1Joj5YV"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 44L4412ZXxz9ryj for ; Fri, 15 Mar 2019 10:02:40 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 7E78BD39; Thu, 14 Mar 2019 23:02:39 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id C34BECDE for ; Thu, 14 Mar 2019 23:02:37 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-vs1-f67.google.com (mail-vs1-f67.google.com [209.85.217.67]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 4CA2A826 for ; Thu, 14 Mar 2019 23:02:36 +0000 (UTC) Received: by mail-vs1-f67.google.com with SMTP id w14so4309547vso.6 for ; Thu, 14 Mar 2019 16:02:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:from:date:message-id:subject:to; bh=YETct1+yhAFJXyy3+Id9IHYMNsbMH3EfoLevL4qDUDQ=; b=r1Joj5YVR2/XOBGng3p4+33s9pKvycYefl7RjdefUSdmEm6Wid1tzBX0dZAclHzztc 6bfhhabCb6qTPz05MGwZOKEZ9OQhPzMdUDE151EQUp86FCLQxKJfpHo9WvSyEo2rVdmt SOLmarjvmArRlx95h//7y5t/S4DGkItxgzjzzumKspeyWMu+VELY5PPBPMCN1YFzXfnj w3DZpfXYTnO6V/xbFQTN1OYcnJpdQUT75LM0SLfaPQcoAT5eP5HQJ/SWkA/tphNo5w4D 9tiZ86ejER7T1kdtmVzbogiNqxFBES5Kez+m7HvoXWdMHMuwANKT9EZcoTTReaF3q19u rWJw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=YETct1+yhAFJXyy3+Id9IHYMNsbMH3EfoLevL4qDUDQ=; b=GFYastX+vQ2/Oxe1GgApcZjLuNRt/fz1lKlaWm6KXxz7RVL8OoNRKMEfh7e2wycEW9 tVKSl+Dfnwrxmv0Xn8dGmjuBkt665pB4ZEZqS+gHQuvpy7r0tO931Nn1Y+yKHDJ7IIEl wO/IljC+EXM2LcLwiWA2XKDiq3d9OBslAngkjb1J7p/ngGfBgO6G3i+FFA23p0Rvg950 /AgGkVigXB3PPH4XOl0K/cW4PkK4YmS2zk1qzMRTqqUPg/ocQCfYxcTYX+N4Chdq+fNn ExU4wKxQBwvDu4Hs6BZcY80AZAKwkyUVF4IOJMIrq4DaTib7Elz42t8Z7U3FhdoK0cYs X75g== X-Gm-Message-State: APjAAAVzpXD5iWuYn+nu+DTX2jSo30VkoazMBJCN5kzcUbxe55qxUSy1 L6wORRUKtvSEDVY7krNzSaKr6Yl3K5/tSmMgjfG69D5N X-Google-Smtp-Source: APXvYqxk0d1MlTIdIE8XFNdtfpLPKludfRv3rnaZVDTN2Vecyz6EqDI6mnjxBEF7Vf989GP6sp4cPJT2ba6yLReuT8I= X-Received: by 2002:a67:fdd6:: with SMTP id l22mr430642vsq.124.1552604554940; Thu, 14 Mar 2019 16:02:34 -0700 (PDT) MIME-Version: 1.0 From: Sharon K Date: Fri, 15 Mar 2019 01:02:24 +0200 Message-ID: To: dev@openvswitch.org X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, HTML_MESSAGE, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org X-Content-Filtered-By: Mailman/MimeDel 2.1.12 Subject: [ovs-dev] [PATCH] netdev-linux: netem QoS support X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Signed-off-by: Sharon Krendel Linux ``No operation.'' By default, Open vSwitch manages quality of @@ -4437,6 +4445,25 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \ + +

+ The linux-netem QoS supports the following key-value pairs: +

+ + + Adds the chosen delay to the packets outgoing to chosen network + interface. The latency value expressed in us. + + + Maximum number of packets the qdisc may hold queued at a time. + The default value is 1000. + + + Adds an independent loss probability to the packets outgoing from the + chosen network interface. + +
+ The overall purpose of these columns is described under Common Columns at the beginning of this document. diff --git a/.gitignore b/.gitignore index 60e7818c3..e28f6c9fc 100644 --- a/.gitignore +++ b/.gitignore @@ -77,3 +77,4 @@ testsuite.tmp.orig /Documentation/_build /.venv /cxx-check +.idea/ diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 0d0d045f7..81dca5deb 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -437,6 +438,7 @@ static const struct tc_ops tc_ops_hfsc; static const struct tc_ops tc_ops_codel; static const struct tc_ops tc_ops_fqcodel; static const struct tc_ops tc_ops_sfq; +static const struct tc_ops tc_ops_netem; static const struct tc_ops tc_ops_default; static const struct tc_ops tc_ops_noop; static const struct tc_ops tc_ops_other; @@ -447,6 +449,7 @@ static const struct tc_ops *const tcs[] = { &tc_ops_codel, /* Controlled delay */ &tc_ops_fqcodel, /* Fair queue controlled delay */ &tc_ops_sfq, /* Stochastic fair queueing */ + &tc_ops_netem, /* Network Emulator */ &tc_ops_noop, /* Non operating qos type. */ &tc_ops_default, /* Default qdisc (see tc-pfifo_fast(8)). */ &tc_ops_other, /* Some other qdisc. */ @@ -456,6 +459,7 @@ static const struct tc_ops *const tcs[] = { static unsigned int tc_ticks_to_bytes(unsigned int rate, unsigned int ticks); static unsigned int tc_bytes_to_ticks(unsigned int rate, unsigned int size); static unsigned int tc_buffer_per_jiffy(unsigned int rate); +static uint32_t tc_time_to_ticks(uint32_t time); static struct tcmsg *netdev_linux_tc_make_request(const struct netdev *, int type, @@ -3956,7 +3960,178 @@ static const struct tc_ops tc_ops_sfq = { .qdisc_get = sfq_qdisc_get, .qdisc_set = sfq_qdisc_set, }; - + +/* netem traffic control class. */ + +#define NETEM_N_QUEUES 0x0000 + +struct netem { + struct tc tc; + uint32_t latency; + uint32_t limit; + uint32_t loss; +}; + +static struct netem * +netem_get__(const struct netdev *netdev_) +{ + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + return CONTAINER_OF(netdev->tc, struct netem, tc); +} + +static void +netem_install__(struct netdev *netdev_, uint32_t latency, uint32_t limit, uint32_t loss) +{ + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netem *netem; + + netem = xmalloc(sizeof *netem); + tc_init(&netem->tc, &tc_ops_netem); + netem->latency = latency; + netem->limit = limit; + netem->loss = loss; + + netdev->tc = &netem->tc; +} + +static int +netem_setup_qdisc__(struct netdev *netdev, uint32_t latency, uint32_t limit, uint32_t loss) +{ + struct tc_netem_qopt opt; + struct ofpbuf request; + struct tcmsg *tcmsg; + int error; + + tc_del_qdisc(netdev); + + tcmsg = netdev_linux_tc_make_request(netdev, RTM_NEWQDISC, + NLM_F_EXCL | NLM_F_CREATE, &request); + if (!tcmsg) { + return ENODEV; + } + tcmsg->tcm_handle = tc_make_handle(1, 0); + tcmsg->tcm_parent = TC_H_ROOT; + + memset(&opt, 0, sizeof opt); + + if (!limit) { + opt.limit = 1000; + } else { + opt.limit = limit; + } + + if (loss) { + if (loss > 100) { + VLOG_WARN_RL(&rl, "loss should be a percentage value between 0 to 100, " + "loss was %u", loss); + return EINVAL; + } + opt.loss = floor(UINT32_MAX * (loss / 100.0)); + } + + opt.latency = tc_time_to_ticks(latency); + + nl_msg_put_string(&request, TCA_KIND, "netem"); + nl_msg_put_unspec(&request, TCA_OPTIONS, &opt, sizeof opt); + + error = tc_transact(&request, NULL); + if (error) { + VLOG_WARN_RL(&rl, "failed to replace %s qdisc, " + "latency %u, limit %u, loss %u error %d(%s)", + netdev_get_name(netdev), + opt.latency, opt.limit, opt.loss, + error, ovs_strerror(error)); + } + return error; +} + +static void +netem_parse_qdisc_details__(struct netdev *netdev OVS_UNUSED, + const struct smap *details, struct netem *netem) +{ + netem->latency = smap_get_ullong(details, "latency", 0); + netem->limit = smap_get_ullong(details, "limit", 0); + netem->loss = smap_get_ullong(details, "loss", 0); + + if(!netem->limit) { + netem->limit = 1000; + } +} + +static int +netem_tc_install(struct netdev *netdev, const struct smap *details) +{ + int error; + struct netem netem; + + netem_parse_qdisc_details__(netdev, details, &netem); + error = netem_setup_qdisc__(netdev, netem.latency, netem.limit, netem.loss); + if (!error) { + netem_install__(netdev, netem.latency, netem.limit, netem.loss); + } + return error; +} + +static int +netem_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg) +{ + const struct tc_netem_qopt *netem; + struct nlattr *nlattr; + const char * kind; + int error; + + error = tc_parse_qdisc(nlmsg, &kind, &nlattr); + if (error == 0) { + netem = nl_attr_get(nlattr); + netem_install__(netdev, netem->latency, netem->limit, netem->loss); + return 0; + } + + return error; +} + +static void +netem_tc_destroy(struct tc *tc) +{ + struct netem *netem = CONTAINER_OF(tc, struct netem, tc); + tc_destroy(tc); + free(netem); +} + +static int +netem_qdisc_get(const struct netdev *netdev, struct smap *details) +{ + const struct netem *netem = netem_get__(netdev); + smap_add_format(details, "latency", "%u", netem->latency); + smap_add_format(details, "limit", "%u", netem->limit); + smap_add_format(details, "loss", "%u", netem->loss); + return 0; +} + +static int +netem_qdisc_set(struct netdev *netdev, const struct smap *details) +{ + struct netem netem; + + netem_parse_qdisc_details__(netdev, details, &netem); + netem_install__(netdev, netem.latency, netem.limit, netem.loss); + netem_get__(netdev)->latency = netem.latency; + netem_get__(netdev)->limit = netem.limit; + netem_get__(netdev)->loss = netem.loss; + return 0; +} + +static const struct tc_ops tc_ops_netem = { + .linux_name = "netem", + .ovs_name = "linux-netem", + .n_queues = NETEM_N_QUEUES, + .tc_install = netem_tc_install, + .tc_load = netem_tc_load, + .tc_destroy = netem_tc_destroy, + .qdisc_get = netem_qdisc_get, + .qdisc_set = netem_qdisc_set, +}; + /* HTB traffic control class. */ #define HTB_N_QUEUES 0xf000 @@ -5240,6 +5415,12 @@ tc_buffer_per_jiffy(unsigned int rate) return rate / buffer_hz; } +static uint32_t +tc_time_to_ticks(uint32_t time) { + read_psched(); + return time * (ticks_per_s / 1000000); +} + /* Given Netlink 'msg' that describes a qdisc, extracts the name of the qdisc, * e.g. "htb", into '*kind' (if it is nonnull). If 'options' is nonnull, * extracts 'msg''s TCA_OPTIONS attributes into '*options' if it is present or diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 654b5f8c9..56318ec21 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -4327,6 +4327,14 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \ for information on how this classifier works. +
linux-netem
+
+ Linux ``Network Emulator'' classifier. See + tc-netem(8) (also at + http://man7.org/linux/man-pages/man8/tc-netem.8.html) + for information on how this classifier works. +
+
linux-noop