From patchwork Mon Aug 27 02:42:28 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yousuk Seung X-Patchwork-Id: 962323 X-Patchwork-Delegate: dsahern@gmail.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="u8uFI+RR"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 41zGQM4wNZz9s3Z for ; Mon, 27 Aug 2018 12:42:51 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727077AbeH0G1S (ORCPT ); Mon, 27 Aug 2018 02:27:18 -0400 Received: from mail-it0-f73.google.com ([209.85.214.73]:56827 "EHLO mail-it0-f73.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726968AbeH0G1R (ORCPT ); Mon, 27 Aug 2018 02:27:17 -0400 Received: by mail-it0-f73.google.com with SMTP id 11-v6so2093646itk.6 for ; Sun, 26 Aug 2018 19:42:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=sWstpziTmvYfU9vNEkBjMIwFXxPSEqSl1opx6iA6H5o=; b=u8uFI+RRYhNKfgJGvVyQme3RzHbDcJuFYE74YbiPJ3FyzNprGleqDf/HQfJnIdShJH qXqgSPD+LqpDnTSWm4+2cJUI26oBM1nQK/EQEA6SbpjkSUQG0xQcJiV7KmyfKA5UCPZs s6pq8uYIXMz9ueGUGHmOE62iwqfcjCaQEpxd37i2g4OGG51cDV2mOWA2B9PqErdqwpte AqjpniCzW9AcAefHJfC5Bh6neR3gng9P9tG/Oc2/9SG1GubYLKWAi8OxDY1yPdvVb3Iw 2HsjNh51DifCovBJa+6JXp131gywZaZr7Xmqi4MSty3VjrrCV8JJedze1W744QPy55Q0 ftZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=sWstpziTmvYfU9vNEkBjMIwFXxPSEqSl1opx6iA6H5o=; b=R/qAgJH2KU21EBtgXuOIAlv7kmMh9909JN09bXUZJ2j5NW53aj+5JIY0vINEwTiUSp l2PWCavMCuTFziaoDCub0ZXoYXi/F5va2B0DleXKJi3zAJI8BJDm400HhLXzYLsKusfB X8MtAJ0TPJnAKgqzFXj9UCjbK01U9e6F+T9h7HudNxOkF11/tfP03CuhYFN4hbE0IKT8 +86UQvjJVSaf+i3/Co/hDGEeX0oEZg1LCIjxH7K/uOirhuL7JntBhPMZIEuPNlJB8v1G P+3gGdh0hiRVI9Hw/PbmrV985jt9VqlbGLO8zBrzOlYYYZm7rxQEAs1QzHwamY+dEitx FPBw== X-Gm-Message-State: APzg51AFGZ4S59ELHGzoKGfyi/FM0JFJUXKl4pxhLCESGRgMwWxTSE83 TKbYSWadTReJQA6/TnhmV9JAWW7hGa2nqI7muawElf5zXxxudNzihYMSgt9du0+SZwakmuxG1aG AK4qjZ4RM2wLhBA6J5DSeu911Hm2TUKx0e33Y3p1e9Xseg2xWZn85wesbQ/jTinC9 X-Google-Smtp-Source: ANB0VdbZ0E3EUp4/IXo8PwXVWMUl9mlajt/IFAU6UU2fo5w9ar00C/t2h6Hbab1yxKmRzJA+SitfzFYoGslc X-Received: by 2002:a24:4d89:: with SMTP id l131-v6mr3286216itb.38.1535337761315; Sun, 26 Aug 2018 19:42:41 -0700 (PDT) Date: Sun, 26 Aug 2018 19:42:28 -0700 In-Reply-To: <20180827024230.246445-1-ysseung@google.com> Message-Id: <20180827024230.246445-2-ysseung@google.com> Mime-Version: 1.0 References: <20180827024230.246445-1-ysseung@google.com> X-Mailer: git-send-email 2.19.0.rc0.228.g281dcd1b4d0-goog Subject: [PATCH v2 iproute2-next 1/3] tc: support conversions to or from 64 bit nanosecond-based time From: Yousuk Seung To: netdev@vger.kernel.org Cc: Stephen Hemminger , David Ahern , Michael McLennan , Priyaranjan Jha , Dave Taht , Yousuk Seung , Neal Cardwell Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Dave Taht Using a 32 bit field to represent time in nanoseconds results in a maximum value of about 4.3 seconds, which is well below many observed delays in WiFi and LTE, and barely in the ballpark for a trip past the Earth's moon, Luna. Using 64 bit time fields in nanoseconds allows us to simulate network diameters of several hundred light-years. However, only conversions to and from ns, us, ms, and seconds are provided. The iproute2 64 bit api uses signed values for time. Being able to represent positive or negative time allows us to calculate +/- deltas between, for example, the CLOCK_TAI and CLOCK_REALTIME clocks. Time related utility functions in tc_util.c are moved to lib/utils.c. Signed-off-by: Yousuk Seung Signed-off-by: Dave Taht Signed-off-by: Neal Cardwell --- include/utils.h | 12 ++++++ lib/utils.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++ tc/tc_cbq.c | 1 + tc/tc_core.c | 1 + tc/tc_core.h | 2 - tc/tc_estimator.c | 1 + tc/tc_util.c | 46 -------------------- tc/tc_util.h | 3 -- 8 files changed, 119 insertions(+), 51 deletions(-) diff --git a/include/utils.h b/include/utils.h index 8cb4349e8a89..eba67b6ecf44 100644 --- a/include/utils.h +++ b/include/utils.h @@ -46,6 +46,11 @@ void incomplete_command(void) __attribute__((noreturn)); #define NEXT_ARG_FWD() do { argv++; argc--; } while(0) #define PREV_ARG() do { argv--; argc++; } while(0) +#define TIME_UNITS_PER_SEC 1000000 +#define NSEC_PER_USEC 1000 +#define NSEC_PER_MSEC 1000000 +#define NSEC_PER_SEC 1000000000LL + typedef struct { __u16 flags; @@ -310,4 +315,11 @@ size_t strlcat(char *dst, const char *src, size_t size); void drop_cap(void); +int get_time(unsigned int *time, const char *str); +int get_time64(__s64 *time, const char *str); +void print_time(char *buf, int len, __u32 time); +void print_time64(char *buf, int len, __s64 time); +char *sprint_time(__u32 time, char *buf); +char *sprint_time64(__s64 time, char *buf); + #endif /* __UTILS_H__ */ diff --git a/lib/utils.c b/lib/utils.c index 02ce67721915..34ec4ab12646 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -1633,3 +1633,107 @@ void drop_cap(void) } #endif } + +int get_time(unsigned int *time, const char *str) +{ + double t; + char *p; + + t = strtod(str, &p); + if (p == str) + return -1; + + if (*p) { + if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec") == 0 || + strcasecmp(p, "secs") == 0) + t *= TIME_UNITS_PER_SEC; + else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec") == 0 || + strcasecmp(p, "msecs") == 0) + t *= TIME_UNITS_PER_SEC/1000; + else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec") == 0 || + strcasecmp(p, "usecs") == 0) + t *= TIME_UNITS_PER_SEC/1000000; + else + return -1; + } + + *time = t; + return 0; +} + + +void print_time(char *buf, int len, __u32 time) +{ + double tmp = time; + + if (tmp >= TIME_UNITS_PER_SEC) + snprintf(buf, len, "%.1fs", tmp/TIME_UNITS_PER_SEC); + else if (tmp >= TIME_UNITS_PER_SEC/1000) + snprintf(buf, len, "%.1fms", tmp/(TIME_UNITS_PER_SEC/1000)); + else + snprintf(buf, len, "%uus", time); +} + +char *sprint_time(__u32 time, char *buf) +{ + print_time(buf, SPRINT_BSIZE-1, time); + return buf; +} + +/* 64 bit times are represented internally in nanoseconds */ +int get_time64(__s64 *time, const char *str) +{ + double nsec; + char *p; + + nsec = strtod(str, &p); + if (p == str) + return -1; + + if (*p) { + if (strcasecmp(p, "s") == 0 || + strcasecmp(p, "sec") == 0 || + strcasecmp(p, "secs") == 0) + nsec *= NSEC_PER_SEC; + else if (strcasecmp(p, "ms") == 0 || + strcasecmp(p, "msec") == 0 || + strcasecmp(p, "msecs") == 0) + nsec *= NSEC_PER_MSEC; + else if (strcasecmp(p, "us") == 0 || + strcasecmp(p, "usec") == 0 || + strcasecmp(p, "usecs") == 0) + nsec *= NSEC_PER_USEC; + else if (strcasecmp(p, "ns") == 0 || + strcasecmp(p, "nsec") == 0 || + strcasecmp(p, "nsecs") == 0) + nsec *= 1; + else + return -1; + } + + *time = nsec; + return 0; +} + +void print_time64(char *buf, int len, __s64 time) +{ + double nsec = time; + + if (time >= NSEC_PER_SEC) + snprintf(buf, len, "%.3fs", nsec/NSEC_PER_SEC); + else if (time >= NSEC_PER_MSEC) + snprintf(buf, len, "%.3fms", nsec/NSEC_PER_MSEC); + else if (time >= NSEC_PER_USEC) + snprintf(buf, len, "%.3fus", nsec/NSEC_PER_USEC); + else + snprintf(buf, len, "%lldns", time); +} + +char *sprint_time64(__s64 time, char *buf) +{ + print_time64(buf, SPRINT_BSIZE-1, time); + return buf; +} + + + diff --git a/tc/tc_cbq.c b/tc/tc_cbq.c index 4cd584a91a26..c811456b1627 100644 --- a/tc/tc_cbq.c +++ b/tc/tc_cbq.c @@ -20,6 +20,7 @@ #include #include +#include "utils.h" #include "tc_core.h" #include "tc_cbq.h" diff --git a/tc/tc_core.c b/tc/tc_core.c index 1bde4d51e5dc..8eb11223eb9d 100644 --- a/tc/tc_core.c +++ b/tc/tc_core.c @@ -21,6 +21,7 @@ #include #include +#include "utils.h" #include "tc_core.h" #include diff --git a/tc/tc_core.h b/tc/tc_core.h index 1dfa9a4f773b..bd4a99f0d8dd 100644 --- a/tc/tc_core.h +++ b/tc/tc_core.h @@ -5,8 +5,6 @@ #include #include -#define TIME_UNITS_PER_SEC 1000000 - enum link_layer { LINKLAYER_UNSPEC, LINKLAYER_ETHERNET, diff --git a/tc/tc_estimator.c b/tc/tc_estimator.c index e4edfc7e98d9..f494b7caa44e 100644 --- a/tc/tc_estimator.c +++ b/tc/tc_estimator.c @@ -20,6 +20,7 @@ #include #include +#include "utils.h" #include "tc_core.h" int tc_setup_estimator(unsigned int A, unsigned int time_const, struct tc_estimator *est) diff --git a/tc/tc_util.c b/tc/tc_util.c index d7578528a31b..cafbe49f3ec8 100644 --- a/tc/tc_util.c +++ b/tc/tc_util.c @@ -334,52 +334,6 @@ char *sprint_rate(__u64 rate, char *buf) return buf; } -int get_time(unsigned int *time, const char *str) -{ - double t; - char *p; - - t = strtod(str, &p); - if (p == str) - return -1; - - if (*p) { - if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec") == 0 || - strcasecmp(p, "secs") == 0) - t *= TIME_UNITS_PER_SEC; - else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec") == 0 || - strcasecmp(p, "msecs") == 0) - t *= TIME_UNITS_PER_SEC/1000; - else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec") == 0 || - strcasecmp(p, "usecs") == 0) - t *= TIME_UNITS_PER_SEC/1000000; - else - return -1; - } - - *time = t; - return 0; -} - - -void print_time(char *buf, int len, __u32 time) -{ - double tmp = time; - - if (tmp >= TIME_UNITS_PER_SEC) - snprintf(buf, len, "%.1fs", tmp/TIME_UNITS_PER_SEC); - else if (tmp >= TIME_UNITS_PER_SEC/1000) - snprintf(buf, len, "%.1fms", tmp/(TIME_UNITS_PER_SEC/1000)); - else - snprintf(buf, len, "%uus", time); -} - -char *sprint_time(__u32 time, char *buf) -{ - print_time(buf, SPRINT_BSIZE-1, time); - return buf; -} - char *sprint_ticks(__u32 ticks, char *buf) { return sprint_time(tc_core_tick2time(ticks), buf); diff --git a/tc/tc_util.h b/tc/tc_util.h index 6632c4f9c528..76fd986d6e4c 100644 --- a/tc/tc_util.h +++ b/tc/tc_util.h @@ -81,13 +81,11 @@ int get_rate64(__u64 *rate, const char *str); int get_percent_rate64(__u64 *rate, const char *str, const char *dev); int get_size(unsigned int *size, const char *str); int get_size_and_cell(unsigned int *size, int *cell_log, char *str); -int get_time(unsigned int *time, const char *str); int get_linklayer(unsigned int *val, const char *arg); void print_rate(char *buf, int len, __u64 rate); void print_size(char *buf, int len, __u32 size); void print_qdisc_handle(char *buf, int len, __u32 h); -void print_time(char *buf, int len, __u32 time); void print_linklayer(char *buf, int len, unsigned int linklayer); void print_devname(enum output_type type, int ifindex); @@ -95,7 +93,6 @@ char *sprint_rate(__u64 rate, char *buf); char *sprint_size(__u32 size, char *buf); char *sprint_qdisc_handle(__u32 h, char *buf); char *sprint_tc_classid(__u32 h, char *buf); -char *sprint_time(__u32 time, char *buf); char *sprint_ticks(__u32 ticks, char *buf); char *sprint_linklayer(unsigned int linklayer, char *buf); From patchwork Mon Aug 27 02:42:29 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yousuk Seung X-Patchwork-Id: 962324 X-Patchwork-Delegate: dsahern@gmail.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="TTQFxSdL"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 41zGQN43qXz9ryt for ; Mon, 27 Aug 2018 12:42:52 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727094AbeH0G1U (ORCPT ); Mon, 27 Aug 2018 02:27:20 -0400 Received: from mail-yb0-f201.google.com ([209.85.213.201]:44712 "EHLO mail-yb0-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726968AbeH0G1U (ORCPT ); Mon, 27 Aug 2018 02:27:20 -0400 Received: by mail-yb0-f201.google.com with SMTP id 189-v6so9300103ybz.11 for ; Sun, 26 Aug 2018 19:42:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=RZcWpcIUeaho1tZ9yZDeNpznXtZ4TOP3lIR2wg7Lgx8=; b=TTQFxSdLxa1MkGN7tmy8eXqC+OinoPnMOviGQBFOYti2DgiI8rvpMEs9JNmaA17/dh m7/GQWhyBztzB7fe9uIdNyB7C7aBNWe48HYsuokYIdBRK9d9xDLD9qUTQ3anrymPxG7h gJsc1mChvGgUKfJIyrZsapVaHp4cV9lWCoAE7qjVqWjEV5gQMqEP4/dTpoKjOLUz48+g BMD7CV1GqFE3s+OPuqgzGTookZXakuxlj770upzhuZchWhMNZqD75+VpDBa8jKpAWT/a WHLicy8w+gMCVVkG06dPi7c417ENy1i4O/GLLGVETeDFJUDsWPKccGva0uyBFyE8/iUN cX2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=RZcWpcIUeaho1tZ9yZDeNpznXtZ4TOP3lIR2wg7Lgx8=; b=kMfC9qkRrcCcPLhX/ZijsaCwFcQM7bG+63ZGB4M8cXjKvaOpsZo/8YqNZy0wZx6s5F WkEtpbW6g0zhPAP+neG26ukWes+5BqKv83H96aBFlG3vioquIRqY3VovxFfqDQtjbwmo ap8AZpvza+XF92UPi2Awl9/Kr5MITmV0eqs2gn3VdLR418tMhqshz7r/s91JIGR15vZi hemolGHImtsbHQOzV1WXnQ9mgAEP02ZUw3/vv78VSOK5LFEJBtNqz+WZ9Ax9CnI6ym5Y T5vRChKXNc8R1MlwBgBzyR4i5jdBlqou7+qvuRZfKKyyp25OX7GaHL3yj6cBVDBmWvSD MvZA== X-Gm-Message-State: APzg51BsXZRpejoL6JC+sb4CWhtht88HpYCVOZz1mnWqpYOgJqssOEh9 FxpGkyzPKqxK6KHjMfi2e7fcl7SU68rnKQpW1J9bZEfWCfIDTcvidm6phSLr3AVQljVC2whWMxp IRzehNFuDot4fJqJkVz7UhnoTGheVLOpqyqNeNMbOhOIcWKyg7/aZcX6TQQUh0jjS X-Google-Smtp-Source: ANB0VdZwca7p9eOGatrtsfKjy6k8HJMWE89+S5KFXC+xXKc7Lxgr22Oqhh2z61cPz72rsf0ckhF0u+TdH1Rl X-Received: by 2002:a25:2d5e:: with SMTP id s30-v6mr3385806ybe.41.1535337764060; Sun, 26 Aug 2018 19:42:44 -0700 (PDT) Date: Sun, 26 Aug 2018 19:42:29 -0700 In-Reply-To: <20180827024230.246445-1-ysseung@google.com> Message-Id: <20180827024230.246445-3-ysseung@google.com> Mime-Version: 1.0 References: <20180827024230.246445-1-ysseung@google.com> X-Mailer: git-send-email 2.19.0.rc0.228.g281dcd1b4d0-goog Subject: [PATCH v2 iproute2-next 2/3] q_netem: support delivering packets in delayed time slots From: Yousuk Seung To: netdev@vger.kernel.org Cc: Stephen Hemminger , David Ahern , Michael McLennan , Priyaranjan Jha , Dave Taht , Yousuk Seung , Neal Cardwell Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Dave Taht Slotting is a crude approximation of the behaviors of shared media such as cable, wifi, and LTE, which gather up a bunch of packets within a varying delay window and deliver them, relative to that, nearly all at once. It works within the existing loss, duplication, jitter and delay parameters of netem. Some amount of inherent latency must be specified, regardless. The new "slot" parameter specifies a minimum and maximum delay between transmission attempts. The "bytes" and "packets" parameters can be used to limit the amount of information transferred per slot. Examples of use: tc qdisc add dev eth0 root netem delay 200us \ slot 800us 10ms bytes 64k packets 42 A more correct example, using stacked netem instances and a packet limit to emulate a tail drop wifi queue with slots and variable packet delivery, with a 200Mbit isochronous underlying rate, and 20ms path delay: tc qdisc add dev eth0 root handle 1: netem delay 20ms rate 200mbit \ limit 10000 tc qdisc add dev eth0 parent 1:1 handle 10:1 netem delay 200us \ slot 800us 10ms bytes 64k packets 42 limit 512 Signed-off-by: Yousuk Seung Signed-off-by: Dave Taht Signed-off-by: Neal Cardwell --- man/man8/tc-netem.8 | 32 ++++++++++++++++++++++- tc/q_netem.c | 64 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/man/man8/tc-netem.8 b/man/man8/tc-netem.8 index f2cd86b6ed8a..8d485b026751 100644 --- a/man/man8/tc-netem.8 +++ b/man/man8/tc-netem.8 @@ -8,7 +8,8 @@ NetEm \- Network Emulator .I OPTIONS .IR OPTIONS " := [ " LIMIT " ] [ " DELAY " ] [ " LOSS \ -" ] [ " CORRUPT " ] [ " DUPLICATION " ] [ " REORDERING " ][ " RATE " ]" +" ] [ " CORRUPT " ] [ " DUPLICATION " ] [ " REORDERING " ] [ " RATE \ +" ] [ " SLOT " ]" .IR LIMIT " := " .B limit @@ -51,6 +52,14 @@ NetEm \- Network Emulator .B rate .IR RATE " [ " PACKETOVERHEAD " [ " CELLSIZE " [ " CELLOVERHEAD " ]]]]" +.IR SLOT " := " +.BR slot +.IR MIN_DELAY " [ " MAX_DELAY " ] [" +.BR packets +.IR PACKETS " ] [ " +.BR bytes +.IR BYTES " ]" + .SH DESCRIPTION NetEm is an enhancement of the Linux traffic control facilities @@ -162,6 +171,27 @@ granularity avoid a perfect shaping at a specific level. This will show up in an artificial packet compression (bursts). Another influence factor are network adapter buffers which can also add artificial delay. +.SS slot +defer delivering accumulated packets to within a slot, with each available slot +configured with a minimum delay to acquire, and an optional maximum delay. Slot +delays can be specified in nanoseconds, microseconds, milliseconds or seconds +(e.g. 800us). Values for the optional parameters +.I BYTES +will limit the number of bytes delivered per slot, and/or +.I PACKETS +will limit the number of packets delivered per slot. + +These slot options can provide a crude approximation of bursty MACs such as +DOCSIS, WiFi, and LTE. + +Note that slotting is limited by several factors: the kernel clock granularity, +as with a rate, and attempts to deliver many packets within a slot will be +smeared by the timer resolution, and by the underlying native bandwidth also. + +It is possible to combine slotting with a rate, in which case complex behaviors +where either the rate, or the slot limits on bytes or packets per slot, govern +the actual delivered rate. + .SH LIMITATIONS The main known limitation of Netem are related to timer granularity, since Linux is not a real-time operating system. diff --git a/tc/q_netem.c b/tc/q_netem.c index 9f9a9b3df255..53a7a1056f5d 100644 --- a/tc/q_netem.c +++ b/tc/q_netem.c @@ -40,7 +40,10 @@ static void explain(void) " [ loss gemodel PERCENT [R [1-H [1-K]]]\n" \ " [ ecn ]\n" \ " [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n" \ -" [ rate RATE [PACKETOVERHEAD] [CELLSIZE] [CELLOVERHEAD]]\n"); +" [ rate RATE [PACKETOVERHEAD] [CELLSIZE] [CELLOVERHEAD]]\n" \ +" [ slot MIN_DELAY [MAX_DELAY] [packets MAX_PACKETS]" \ +" [bytes MAX_BYTES]]\n" \ + ); } static void explain1(const char *arg) @@ -164,6 +167,7 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct tc_netem_gimodel gimodel; struct tc_netem_gemodel gemodel; struct tc_netem_rate rate = {}; + struct tc_netem_slot slot = {}; __s16 *dist_data = NULL; __u16 loss_type = NETEM_LOSS_UNSPEC; int present[__TCA_NETEM_MAX] = {}; @@ -412,6 +416,45 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, return -1; } } + } else if (matches(*argv, "slot") == 0) { + NEXT_ARG(); + present[TCA_NETEM_SLOT] = 1; + if (get_time64(&slot.min_delay, *argv)) { + explain1("slot min_delay"); + return -1; + } + if (NEXT_IS_NUMBER()) { + NEXT_ARG(); + if (get_time64(&slot.max_delay, *argv) || + slot.max_delay < slot.min_delay) { + explain1("slot max_delay"); + return -1; + } + } else { + slot.max_delay = slot.min_delay; + } + if (NEXT_ARG_OK() && + matches(*(argv+1), "packets") == 0) { + NEXT_ARG(); + if (!NEXT_ARG_OK() || + get_s32(&slot.max_packets, *(argv+1), 0)) { + explain1("slot packets"); + return -1; + } + NEXT_ARG(); + } + if (NEXT_ARG_OK() && + matches(*(argv+1), "bytes") == 0) { + unsigned int max_bytes; + NEXT_ARG(); + if (!NEXT_ARG_OK() || + get_size(&max_bytes, *(argv+1))) { + explain1("slot bytes"); + return -1; + } + slot.max_bytes = (int) max_bytes; + NEXT_ARG(); + } } else if (strcmp(*argv, "help") == 0) { explain(); return -1; @@ -472,6 +515,10 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, addattr_l(n, 1024, TCA_NETEM_CORRUPT, &corrupt, sizeof(corrupt)) < 0) return -1; + if (present[TCA_NETEM_SLOT] && + addattr_l(n, 1024, TCA_NETEM_SLOT, &slot, sizeof(slot)) < 0) + return -1; + if (loss_type != NETEM_LOSS_UNSPEC) { struct rtattr *start; @@ -526,6 +573,7 @@ static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) int *ecn = NULL; struct tc_netem_qopt qopt; const struct tc_netem_rate *rate = NULL; + const struct tc_netem_slot *slot = NULL; int len; __u64 rate64 = 0; @@ -586,6 +634,11 @@ static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return -1; rate64 = rta_getattr_u64(tb[TCA_NETEM_RATE64]); } + if (tb[TCA_NETEM_SLOT]) { + if (RTA_PAYLOAD(tb[TCA_NETEM_SLOT]) < sizeof(*slot)) + return -1; + slot = RTA_DATA(tb[TCA_NETEM_SLOT]); + } } fprintf(f, "limit %d", qopt.limit); @@ -659,6 +712,15 @@ static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) fprintf(f, " celloverhead %d", rate->cell_overhead); } + if (slot) { + fprintf(f, " slot %s", sprint_time64(slot->min_delay, b1)); + fprintf(f, " %s", sprint_time64(slot->max_delay, b1)); + if(slot->max_packets) + fprintf(f, " packets %d", slot->max_packets); + if(slot->max_bytes) + fprintf(f, " bytes %d", slot->max_bytes); + } + if (ecn) fprintf(f, " ecn "); From patchwork Mon Aug 27 02:42:30 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yousuk Seung X-Patchwork-Id: 962325 X-Patchwork-Delegate: dsahern@gmail.com Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="WzoOLl0d"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 41zGQP1t4Bz9s3Z for ; Mon, 27 Aug 2018 12:42:53 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727119AbeH0G1Y (ORCPT ); Mon, 27 Aug 2018 02:27:24 -0400 Received: from mail-qk0-f202.google.com ([209.85.220.202]:47988 "EHLO mail-qk0-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726968AbeH0G1X (ORCPT ); Mon, 27 Aug 2018 02:27:23 -0400 Received: by mail-qk0-f202.google.com with SMTP id u195-v6so13882471qka.14 for ; Sun, 26 Aug 2018 19:42:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=oyG01FVjadQsmoQ/oYx6xjAGRW3yXEaj7Y9hLckWkkM=; b=WzoOLl0dbhHRDFztAjPNeoaHoxEx/VoMgBMOTlLQQO//z1N6mCzc2FZLK5kpxk7sOt OlNufCvbQwpvrAaeWpL/OeMyhs3E2VNsfxzpBsJrKW+ZGiPsFVHXGeF3kWfFnEB68vD8 wYId8YgVWO7SFodvwTK9uxryrwm2Ev6Q1giwzxmKlSyHzfh9B0MzwaqOYG2kjQhZhv/k BhtOU2ZzebpInzFX1mYOr9GDdBAX7UYplRIRN4XXauayYds4TPJsfy/DQLUlb+qsOQEY ewBh7OwLIJGa6MM7YNgF3CuKruqvUssoUBZrjCt+ycAIUWMw6UChqbNwX43m1zEaBBGe N/GQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=oyG01FVjadQsmoQ/oYx6xjAGRW3yXEaj7Y9hLckWkkM=; b=k1yuVaS9RW4tbPvaz1cuPCwuTlrpb1Y79Cx3alX2G1Wc+l/utItQ0IPWljCMn0JUUA gYM/41nOh4FLzl6bLP86fKwocNS+BOJxeAIRvr+s7eM6+qgFG1YSpXBUdz0dnPS4eGtL vngQxo/47AFzLZLk7qL9Z0KHawwUBBCaa7Andak+1ZA1gNKR/3I39zUlADiSJRx+/Xyz YwjIjL3HXat7A76XWemxNXq8H7s74lZLfC+wTLKmtc0yXI3qv/q3eT5dRMiPPHXNXgDT 9ZBgNsq2bz6A9yLWgRAaMyKl2hgqql40shcCD8Afpkaoik3KgJEDmO+A05nsAzZ1XcrM KttQ== X-Gm-Message-State: APzg51Aj+nXz4vXyjdw7Ayrm5itEkPW38lDvwFICqY0v3D760dFGluC+ OTl7Ey4vuSRwertNmgL9fzaTuEZTsjQZ9QzIo99FE7hMVTLB1syRMlmfD5HfTrLUVRvRphhDZil O7I6nxjMvVOODzaduexUXcuxGKRQgEgUTrsHXxzrcGQXSqVQ2JKnPz2faqFG6vstK X-Google-Smtp-Source: ANB0VdZre5eSp/nDgv14U+C06HduZ03S/0hKT6C4tEhAsZRpbpyIniRPJtNuM0nfNYabCUhQ9owm97+OehZI X-Received: by 2002:a0c:add1:: with SMTP id x17-v6mr6838818qvc.50.1535337767012; Sun, 26 Aug 2018 19:42:47 -0700 (PDT) Date: Sun, 26 Aug 2018 19:42:30 -0700 In-Reply-To: <20180827024230.246445-1-ysseung@google.com> Message-Id: <20180827024230.246445-4-ysseung@google.com> Mime-Version: 1.0 References: <20180827024230.246445-1-ysseung@google.com> X-Mailer: git-send-email 2.19.0.rc0.228.g281dcd1b4d0-goog Subject: [PATCH v2 iproute2-next 3/3] q_netem: slotting with non-uniform distribution From: Yousuk Seung To: netdev@vger.kernel.org Cc: Stephen Hemminger , David Ahern , Michael McLennan , Priyaranjan Jha , Yousuk Seung , Neal Cardwell , Dave Taht Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Extend slotting with support for non-uniform distributions. This is similar to netem's non-uniform distribution delay feature. Syntax: slot distribution DISTRIBUTION DELAY JITTER [packets MAX_PACKETS] \ [bytes MAX_BYTES] The syntax and use of the distribution table is the same as in the non-uniform distribution delay feature. A file DISTRIBUTION must be present in TC_LIB_DIR (e.g. /usr/lib/tc) containing numbers scaled by NETEM_DIST_SCALE. A random value x is selected from the table and it takes DELAY + ( x * JITTER ) as delay. Correlation between values is not supported. Examples: Normal distribution delay with mean = 800us and stdev = 100us. > tc qdisc add dev eth0 root netem slot distribution normal \ 800us 100us Optionally set the max slot size in bytes and/or packets. > tc qdisc add dev eth0 root netem slot distribution normal \ 800us 100us bytes 64k packets 42 Signed-off-by: Yousuk Seung Signed-off-by: Neal Cardwell Signed-off-by: Dave Taht --- man/man8/tc-netem.8 | 20 ++++++++---- tc/q_netem.c | 77 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 78 insertions(+), 19 deletions(-) diff --git a/man/man8/tc-netem.8 b/man/man8/tc-netem.8 index 8d485b026751..111109cf042f 100644 --- a/man/man8/tc-netem.8 +++ b/man/man8/tc-netem.8 @@ -53,9 +53,13 @@ NetEm \- Network Emulator .IR RATE " [ " PACKETOVERHEAD " [ " CELLSIZE " [ " CELLOVERHEAD " ]]]]" .IR SLOT " := " -.BR slot -.IR MIN_DELAY " [ " MAX_DELAY " ] [" -.BR packets +.BR slot " { " +.IR MIN_DELAY " [ " MAX_DELAY " ] |" +.br +.RB " " distribution " { "uniform " | " normal " | " pareto " | " paretonormal " | " +.IR FILE " } " DELAY " " JITTER " } " +.br +.RB " [ " packets .IR PACKETS " ] [ " .BR bytes .IR BYTES " ]" @@ -172,9 +176,13 @@ an artificial packet compression (bursts). Another influence factor are network adapter buffers which can also add artificial delay. .SS slot -defer delivering accumulated packets to within a slot, with each available slot -configured with a minimum delay to acquire, and an optional maximum delay. Slot -delays can be specified in nanoseconds, microseconds, milliseconds or seconds +defer delivering accumulated packets to within a slot. Each available slot can be +configured with a minimum delay to acquire, and an optional maximum delay. +Alternatively it can be configured with the distribution similar to +.BR distribution +for +.BR delay +option. Slot delays can be specified in nanoseconds, microseconds, milliseconds or seconds (e.g. 800us). Values for the optional parameters .I BYTES will limit the number of bytes delivered per slot, and/or diff --git a/tc/q_netem.c b/tc/q_netem.c index 53a7a1056f5d..5bfdfcd5478c 100644 --- a/tc/q_netem.c +++ b/tc/q_netem.c @@ -43,7 +43,9 @@ static void explain(void) " [ rate RATE [PACKETOVERHEAD] [CELLSIZE] [CELLOVERHEAD]]\n" \ " [ slot MIN_DELAY [MAX_DELAY] [packets MAX_PACKETS]" \ " [bytes MAX_BYTES]]\n" \ - ); +" [ slot distribution" \ +" {uniform|normal|pareto|paretonormal|custom} DELAY JITTER" \ +" [packets MAX_PACKETS] [bytes MAX_BYTES]]\n"); } static void explain1(const char *arg) @@ -159,6 +161,7 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { int dist_size = 0; + int slot_dist_size = 0; struct rtattr *tail; struct tc_netem_qopt opt = { .limit = 1000 }; struct tc_netem_corr cor = {}; @@ -169,6 +172,7 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct tc_netem_rate rate = {}; struct tc_netem_slot slot = {}; __s16 *dist_data = NULL; + __s16 *slot_dist_data = NULL; __u16 loss_type = NETEM_LOSS_UNSPEC; int present[__TCA_NETEM_MAX] = {}; __u64 rate64 = 0; @@ -417,21 +421,55 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, } } } else if (matches(*argv, "slot") == 0) { - NEXT_ARG(); - present[TCA_NETEM_SLOT] = 1; - if (get_time64(&slot.min_delay, *argv)) { - explain1("slot min_delay"); - return -1; - } if (NEXT_IS_NUMBER()) { NEXT_ARG(); - if (get_time64(&slot.max_delay, *argv) || - slot.max_delay < slot.min_delay) { - explain1("slot max_delay"); + present[TCA_NETEM_SLOT] = 1; + if (get_time64(&slot.min_delay, *argv)) { + explain1("slot min_delay"); return -1; } + if (NEXT_IS_NUMBER()) { + NEXT_ARG(); + if (get_time64(&slot.max_delay, *argv) || + slot.max_delay < slot.min_delay) { + explain1("slot max_delay"); + return -1; + } + } else { + slot.max_delay = slot.min_delay; + } } else { - slot.max_delay = slot.min_delay; + NEXT_ARG(); + if (strcmp(*argv, "distribution") == 0) { + present[TCA_NETEM_SLOT] = 1; + NEXT_ARG(); + slot_dist_data = calloc(sizeof(slot_dist_data[0]), MAX_DIST); + if (!slot_dist_data) + return -1; + slot_dist_size = get_distribution(*argv, slot_dist_data, MAX_DIST); + if (slot_dist_size <= 0) { + free(slot_dist_data); + return -1; + } + NEXT_ARG(); + if (get_time64(&slot.dist_delay, *argv)) { + explain1("slot delay"); + return -1; + } + NEXT_ARG(); + if (get_time64(&slot.dist_jitter, *argv)) { + explain1("slot jitter"); + return -1; + } + if (slot.dist_jitter <= 0) { + fprintf(stderr, "Non-positive jitter\n"); + return -1; + } + } else { + fprintf(stderr, "Unknown slot parameter: %s\n", + *argv); + return -1; + } } if (NEXT_ARG_OK() && matches(*(argv+1), "packets") == 0) { @@ -559,6 +597,14 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, return -1; free(dist_data); } + + if (slot_dist_data) { + if (addattr_l(n, MAX_DIST * sizeof(slot_dist_data[0]), + TCA_NETEM_SLOT_DIST, + slot_dist_data, slot_dist_size * sizeof(slot_dist_data[0])) < 0) + return -1; + free(slot_dist_data); + } tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; return 0; } @@ -713,8 +759,13 @@ static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) } if (slot) { - fprintf(f, " slot %s", sprint_time64(slot->min_delay, b1)); - fprintf(f, " %s", sprint_time64(slot->max_delay, b1)); + if (slot->dist_jitter > 0) { + fprintf(f, " slot distribution %s", sprint_time64(slot->dist_delay, b1)); + fprintf(f, " %s", sprint_time64(slot->dist_jitter, b1)); + } else { + fprintf(f, " slot %s", sprint_time64(slot->min_delay, b1)); + fprintf(f, " %s", sprint_time64(slot->max_delay, b1)); + } if(slot->max_packets) fprintf(f, " packets %d", slot->max_packets); if(slot->max_bytes)