From: Nishanth Devarajan <ndev2021@gmail.com>
To: stephen@networkplumber.org
Cc: netdev@vger.kernel.org, doucette@bu.edu, michel.machado@gmail.com
Subject: [PATCH iproute2/net-next v2]tc: B.W limits can now be specified in %.
Date: Wed, 15 Nov 2017 07:06:21 +0530 [thread overview]
Message-ID: <20171115013618.GA19575@gmail.com> (raw)
This patch adapts the tc command line interface to allow bandwidth limits
to be specified as a percentage of the interface's capacity.
For this purpose, we've modified and moved int read_prop() from
ip/iptuntap.c to lib.utils.c to make it accessible to tc.
Additionally, adding this functionality requires passing the specified
device string to each class/qdisc which changes the prototype for a
couple of functions: the .parse_qopt and .parse_copt interfaces. The
device string is a required parameter for tc-qdisc and tc-class, and when
not specified, the kernel returns ENODEV. In this patch, if the user tries
to specify a bandwidth percentage without naming the device, we return an
error from userspace.
Signed-off by: Nishanth Devarajan <ndev2021@gmail.com>
---
include/utils.h | 1 +
ip/iptuntap.c | 32 ----------------------------
lib/utils.c | 51 ++++++++++++++++++++++++++++++++++++++++++++
man/man8/tc.8 | 4 +++-
tc/q_atm.c | 2 +-
tc/q_cbq.c | 25 +++++++++++++++++-----
tc/q_choke.c | 9 ++++++--
tc/q_clsact.c | 2 +-
tc/q_codel.c | 2 +-
tc/q_drr.c | 4 ++--
tc/q_dsmark.c | 4 ++--
tc/q_fifo.c | 2 +-
tc/q_fq.c | 16 +++++++++++---
tc/q_fq_codel.c | 2 +-
tc/q_gred.c | 9 ++++++--
tc/q_hfsc.c | 45 ++++++++++++++++++++++++++-------------
tc/q_hhf.c | 2 +-
tc/q_htb.c | 18 ++++++++++++----
tc/q_ingress.c | 2 +-
tc/q_mqprio.c | 2 +-
tc/q_multiq.c | 2 +-
tc/q_netem.c | 9 ++++++--
tc/q_pie.c | 2 +-
tc/q_prio.c | 2 +-
tc/q_qfq.c | 4 ++--
tc/q_red.c | 9 ++++++--
tc/q_rr.c | 2 +-
tc/q_sfb.c | 2 +-
tc/q_sfq.c | 2 +-
tc/q_tbf.c | 16 +++++++++++---
tc/tc.c | 2 +-
tc/tc_class.c | 2 +-
tc/tc_qdisc.c | 2 +-
tc/tc_util.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tc/tc_util.h | 8 +++++--
35 files changed, 268 insertions(+), 96 deletions(-)
diff --git a/include/utils.h b/include/utils.h
index 3d91c50..63fea7c 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -87,6 +87,7 @@ int get_prefix(inet_prefix *dst, char *arg, int family);
int mask2bits(__u32 netmask);
int get_addr_ila(__u64 *val, const char *arg);
+int read_prop(char *dev, char *prop, long *value);
int get_hex(char c);
int get_integer(int *val, const char *arg, int base);
int get_unsigned(unsigned *val, const char *arg, int base);
diff --git a/ip/iptuntap.c b/ip/iptuntap.c
index b46e452..09f2be2 100644
--- a/ip/iptuntap.c
+++ b/ip/iptuntap.c
@@ -223,38 +223,6 @@ static int do_del(int argc, char **argv)
return tap_del_ioctl(&ifr);
}
-static int read_prop(char *dev, char *prop, long *value)
-{
- char fname[IFNAMSIZ+25], buf[80], *endp;
- ssize_t len;
- int fd;
- long result;
-
- sprintf(fname, "/sys/class/net/%s/%s", dev, prop);
- fd = open(fname, O_RDONLY);
- if (fd < 0) {
- if (strcmp(prop, "tun_flags"))
- fprintf(stderr, "open %s: %s\n", fname,
- strerror(errno));
- return -1;
- }
- len = read(fd, buf, sizeof(buf)-1);
- close(fd);
- if (len < 0) {
- fprintf(stderr, "read %s: %s", fname, strerror(errno));
- return -1;
- }
-
- buf[len] = 0;
- result = strtol(buf, &endp, 0);
- if (*endp != '\n') {
- fprintf(stderr, "Failed to parse %s\n", fname);
- return -1;
- }
- *value = result;
- return 0;
-}
-
static void print_flags(long flags)
{
if (flags & IFF_TUN)
diff --git a/lib/utils.c b/lib/utils.c
index 4f2fa28..1332410 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -39,6 +39,57 @@
int resolve_hosts;
int timestamp_short;
+int read_prop(char *dev, char *prop, long *value)
+{
+ char fname[128], buf[80], *endp, *nl;
+ FILE *fp;
+ long result;
+ int ret;
+
+ ret = snprintf(fname, sizeof(fname), "/sys/class/net/%s/%s",
+ dev, prop);
+
+ if (ret <= 0 || ret >= sizeof(fname)) {
+ fprintf(stderr, "could not build pathname for property\n");
+ return -1;
+ }
+
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "fopen %s: %s\n", fname, strerror(errno));
+ return -1;
+ }
+
+ if (!fgets(buf, sizeof(buf), fp)) {
+ fclose(fp);
+ goto out;
+ }
+
+ nl = strchr(buf, '\n');
+ if (nl)
+ *nl = '\0';
+
+ fclose(fp);
+ result = strtoul(buf, &endp, 0);
+
+ if (buf == endp || *endp) {
+ fprintf(stderr, "value \"%s\" in file %s is not a number\n",
+ buf, fname);
+ goto out;
+ }
+
+ if (result == ULONG_MAX && errno == ERANGE) {
+ fprintf(stderr, "strtoul %s: %s", fname, strerror(errno));
+ goto out;
+ }
+
+ *value = result;
+ return 0;
+out:
+ fprintf(stderr, "Failed to parse %s\n", fname);
+ return -1;
+}
+
int get_hex(char c)
{
if (c >= 'A' && c <= 'F')
diff --git a/man/man8/tc.8 b/man/man8/tc.8
index f96911a..22f699b 100644
--- a/man/man8/tc.8
+++ b/man/man8/tc.8
@@ -443,7 +443,9 @@ see the man pages for individual qdiscs.
RATES
Bandwidths or rates.
These parameters accept a floating point number, possibly followed by
-a unit (both SI and IEC units supported).
+either a unit (both SI and IEC units supported), or a float followed '%'
+character to specify the rate as a percentage of the device's speed
+(e.g. 5%, 99.5%).
.RS
.TP
bit or a bare number
diff --git a/tc/q_atm.c b/tc/q_atm.c
index 570e7be..7dfd811 100644
--- a/tc/q_atm.c
+++ b/tc/q_atm.c
@@ -44,7 +44,7 @@ static void explain(void)
static int atm_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
struct sockaddr_atmsvc addr = {};
struct atm_qos qos;
diff --git a/tc/q_cbq.c b/tc/q_cbq.c
index e00d4e3..284a874 100644
--- a/tc/q_cbq.c
+++ b/tc/q_cbq.c
@@ -46,7 +46,7 @@ static void explain1(char *arg)
}
-static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
struct tc_ratespec r = {};
struct tc_cbq_lssopt lss = {};
@@ -62,7 +62,12 @@ static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl
if (matches(*argv, "bandwidth") == 0 ||
matches(*argv, "rate") == 0) {
NEXT_ARG();
- if (get_rate(&r.rate, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate(&r.rate, *argv, dev)) {
+ explain1("bandwidth");
+ return -1;
+ }
+ } else if (get_rate(&r.rate, *argv)) {
explain1("bandwidth");
return -1;
}
@@ -176,7 +181,7 @@ static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl
return 0;
}
-static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
int wrr_ok = 0, fopt_ok = 0;
struct tc_ratespec r = {};
@@ -196,13 +201,23 @@ static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, str
while (argc > 0) {
if (matches(*argv, "rate") == 0) {
NEXT_ARG();
- if (get_rate(&r.rate, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate(&r.rate, *argv, dev)) {
+ explain1("rate");
+ return -1;
+ }
+ } else if (get_rate(&r.rate, *argv)) {
explain1("rate");
return -1;
}
} else if (matches(*argv, "bandwidth") == 0) {
NEXT_ARG();
- if (get_rate(&bndw, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate(&bndw, *argv, dev)) {
+ explain1("bandwidth");
+ return -1;
+ }
+ } else if (get_rate(&bndw, *argv)) {
explain1("bandwidth");
return -1;
}
diff --git a/tc/q_choke.c b/tc/q_choke.c
index 726914b..17d70a4 100644
--- a/tc/q_choke.c
+++ b/tc/q_choke.c
@@ -31,7 +31,7 @@ static void explain(void)
}
static int choke_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
struct tc_red_qopt opt = {};
unsigned int burst = 0;
@@ -53,7 +53,12 @@ static int choke_parse_opt(struct qdisc_util *qu, int argc, char **argv,
}
} else if (strcmp(*argv, "bandwidth") == 0) {
NEXT_ARG();
- if (get_rate(&rate, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate(&rate, *argv, dev)) {
+ fprintf(stderr, "Illegal \"bandwidth\"\n");
+ return -1;
+ }
+ } else if (get_rate(&rate, *argv)) {
fprintf(stderr, "Illegal \"bandwidth\"\n");
return -1;
}
diff --git a/tc/q_clsact.c b/tc/q_clsact.c
index e2a1a71..89028e6 100644
--- a/tc/q_clsact.c
+++ b/tc/q_clsact.c
@@ -10,7 +10,7 @@ static void explain(void)
}
static int clsact_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
if (argc > 0) {
fprintf(stderr, "What is \"%s\"?\n", *argv);
diff --git a/tc/q_codel.c b/tc/q_codel.c
index 253629e..170cd0a 100644
--- a/tc/q_codel.c
+++ b/tc/q_codel.c
@@ -58,7 +58,7 @@ static void explain(void)
}
static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
unsigned int limit = 0;
unsigned int target = 0;
diff --git a/tc/q_drr.c b/tc/q_drr.c
index 50623c2..3085268 100644
--- a/tc/q_drr.c
+++ b/tc/q_drr.c
@@ -33,7 +33,7 @@ static void explain2(void)
}
-static int drr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int drr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
while (argc) {
if (strcmp(*argv, "help") == 0) {
@@ -49,7 +49,7 @@ static int drr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl
}
static int drr_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
struct rtattr *tail;
__u32 tmp;
diff --git a/tc/q_dsmark.c b/tc/q_dsmark.c
index 0aab387..13d2d31 100644
--- a/tc/q_dsmark.c
+++ b/tc/q_dsmark.c
@@ -25,7 +25,7 @@ static void explain(void)
static int dsmark_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
struct rtattr *tail;
__u16 ind;
@@ -84,7 +84,7 @@ static void explain_class(void)
static int dsmark_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
struct rtattr *tail;
__u8 tmp;
diff --git a/tc/q_fifo.c b/tc/q_fifo.c
index c3e9088..f0ff1ab 100644
--- a/tc/q_fifo.c
+++ b/tc/q_fifo.c
@@ -27,7 +27,7 @@ static void explain(void)
fprintf(stderr, "Usage: ... <[p|b]fifo | pfifo_head_drop> [ limit NUMBER ]\n");
}
-static int fifo_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int fifo_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
int ok = 0;
struct tc_fifo_qopt opt = {};
diff --git a/tc/q_fq.c b/tc/q_fq.c
index 49ebeef..9b0bb93 100644
--- a/tc/q_fq.c
+++ b/tc/q_fq.c
@@ -71,7 +71,7 @@ static unsigned int ilog2(unsigned int val)
}
static int fq_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
unsigned int plimit;
unsigned int flow_plimit;
@@ -118,7 +118,12 @@ static int fq_parse_opt(struct qdisc_util *qu, int argc, char **argv,
}
} else if (strcmp(*argv, "maxrate") == 0) {
NEXT_ARG();
- if (get_rate(&maxrate, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate(&maxrate, *argv, dev)) {
+ fprintf(stderr, "Illegal \"maxrate\"\n");
+ return -1;
+ }
+ } else if (get_rate(&maxrate, *argv)) {
fprintf(stderr, "Illegal \"maxrate\"\n");
return -1;
}
@@ -132,7 +137,12 @@ static int fq_parse_opt(struct qdisc_util *qu, int argc, char **argv,
set_low_rate_threshold = true;
} else if (strcmp(*argv, "defrate") == 0) {
NEXT_ARG();
- if (get_rate(&defrate, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate(&defrate, *argv, dev)) {
+ fprintf(stderr, "Illegal \"defrate\"\n");
+ return -1;
+ }
+ } else if (get_rate(&defrate, *argv)) {
fprintf(stderr, "Illegal \"defrate\"\n");
return -1;
}
diff --git a/tc/q_fq_codel.c b/tc/q_fq_codel.c
index 1eac140..ef700cd 100644
--- a/tc/q_fq_codel.c
+++ b/tc/q_fq_codel.c
@@ -56,7 +56,7 @@ static void explain(void)
}
static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
unsigned int limit = 0;
unsigned int flows = 0;
diff --git a/tc/q_gred.c b/tc/q_gred.c
index 2eb906d..18d96c9 100644
--- a/tc/q_gred.c
+++ b/tc/q_gred.c
@@ -116,7 +116,7 @@ static int init_gred(struct qdisc_util *qu, int argc, char **argv,
/*
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*/
-static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
int ok = 0;
struct tc_gred_qopt opt = { 0 };
@@ -199,7 +199,12 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n
ok++;
} else if (strcmp(*argv, "bandwidth") == 0) {
NEXT_ARG();
- if (get_rate(&rate, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate(&rate, *argv, dev)) {
+ fprintf(stderr, "Illegal \"bandwidth\"\n");
+ return -1;
+ }
+ } else if (get_rate(&rate, *argv)) {
fprintf(stderr, "Illegal \"bandwidth\"\n");
return -1;
}
diff --git a/tc/q_hfsc.c b/tc/q_hfsc.c
index dc9fed9..b0923ab 100644
--- a/tc/q_hfsc.c
+++ b/tc/q_hfsc.c
@@ -23,7 +23,7 @@
#include "utils.h"
#include "tc_util.h"
-static int hfsc_get_sc(int *, char ***, struct tc_service_curve *);
+static int hfsc_get_sc(int *, char ***, struct tc_service_curve *, char *);
static void
@@ -70,7 +70,7 @@ explain1(char *arg)
}
static int
-hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
struct tc_hfsc_qopt qopt = {};
@@ -141,7 +141,7 @@ hfsc_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
static int
hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
struct tc_service_curve rsc = {}, fsc = {}, usc = {};
int rsc_ok = 0, fsc_ok = 0, usc_ok = 0;
@@ -150,21 +150,21 @@ hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
while (argc > 0) {
if (matches(*argv, "rt") == 0) {
NEXT_ARG();
- if (hfsc_get_sc(&argc, &argv, &rsc) < 0) {
+ if (hfsc_get_sc(&argc, &argv, &rsc, dev) < 0) {
explain1("rt");
return -1;
}
rsc_ok = 1;
} else if (matches(*argv, "ls") == 0) {
NEXT_ARG();
- if (hfsc_get_sc(&argc, &argv, &fsc) < 0) {
+ if (hfsc_get_sc(&argc, &argv, &fsc, dev) < 0) {
explain1("ls");
return -1;
}
fsc_ok = 1;
} else if (matches(*argv, "sc") == 0) {
NEXT_ARG();
- if (hfsc_get_sc(&argc, &argv, &rsc) < 0) {
+ if (hfsc_get_sc(&argc, &argv, &rsc, dev) < 0) {
explain1("sc");
return -1;
}
@@ -173,7 +173,7 @@ hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
fsc_ok = 1;
} else if (matches(*argv, "ul") == 0) {
NEXT_ARG();
- if (hfsc_get_sc(&argc, &argv, &usc) < 0) {
+ if (hfsc_get_sc(&argc, &argv, &usc, dev) < 0) {
explain1("ul");
return -1;
}
@@ -281,7 +281,7 @@ struct qdisc_util hfsc_qdisc_util = {
};
static int
-hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc)
+hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc, char *dev)
{
char **argv = *argvp;
int argc = *argcp;
@@ -289,7 +289,12 @@ hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc)
if (matches(*argv, "m1") == 0) {
NEXT_ARG();
- if (get_rate(&m1, *argv) < 0) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate(&m1, *argv, dev)) {
+ explain1("m1");
+ return -1;
+ }
+ } else if (get_rate(&m1, *argv) < 0) {
explain1("m1");
return -1;
}
@@ -307,7 +312,12 @@ hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc)
if (matches(*argv, "m2") == 0) {
NEXT_ARG();
- if (get_rate(&m2, *argv) < 0) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate(&m2, *argv, dev)) {
+ explain1("m2");
+ return -1;
+ }
+ } else if (get_rate(&m2, *argv) < 0) {
explain1("m2");
return -1;
}
@@ -324,7 +334,7 @@ hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc)
}
static int
-hfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc)
+hfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc, char *dev)
{
char **argv = *argvp;
int argc = *argcp;
@@ -350,7 +360,12 @@ hfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc)
if (matches(*argv, "rate") == 0) {
NEXT_ARG();
- if (get_rate(&rate, *argv) < 0) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate(&rate, *argv, dev)) {
+ explain1("rate");
+ return -1;
+ }
+ } else if (get_rate(&rate, *argv) < 0) {
explain1("rate");
return -1;
}
@@ -386,10 +401,10 @@ hfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc)
}
static int
-hfsc_get_sc(int *argcp, char ***argvp, struct tc_service_curve *sc)
+hfsc_get_sc(int *argcp, char ***argvp, struct tc_service_curve *sc, char *dev)
{
- if (hfsc_get_sc1(argcp, argvp, sc) < 0 &&
- hfsc_get_sc2(argcp, argvp, sc) < 0)
+ if (hfsc_get_sc1(argcp, argvp, sc, dev) < 0 &&
+ hfsc_get_sc2(argcp, argvp, sc, dev) < 0)
return -1;
if (sc->m1 == 0 && sc->m2 == 0) {
diff --git a/tc/q_hhf.c b/tc/q_hhf.c
index d1f15f9..c60d425 100644
--- a/tc/q_hhf.c
+++ b/tc/q_hhf.c
@@ -25,7 +25,7 @@ static void explain(void)
}
static int hhf_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
unsigned int limit = 0;
unsigned int quantum = 0;
diff --git a/tc/q_htb.c b/tc/q_htb.c
index db82852..fb52e72 100644
--- a/tc/q_htb.c
+++ b/tc/q_htb.c
@@ -59,7 +59,7 @@ static void explain1(char *arg)
}
-static int htb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int htb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
unsigned int direct_qlen = ~0U;
struct tc_htb_glob opt = {
@@ -108,7 +108,7 @@ static int htb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl
return 0;
}
-static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
int ok = 0;
struct tc_htb_opt opt = {};
@@ -178,7 +178,12 @@ static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, str
fprintf(stderr, "Double \"ceil\" spec\n");
return -1;
}
- if (get_rate64(&ceil64, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate64(&ceil64, *argv, dev)) {
+ explain1("ceil");
+ return -1;
+ }
+ } else if (get_rate64(&ceil64, *argv)) {
explain1("ceil");
return -1;
}
@@ -189,7 +194,12 @@ static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, str
fprintf(stderr, "Double \"rate\" spec\n");
return -1;
}
- if (get_rate64(&rate64, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate64(&rate64, *argv, dev)) {
+ explain1("rate");
+ return -1;
+ }
+ } else if (get_rate64(&rate64, *argv)) {
explain1("rate");
return -1;
}
diff --git a/tc/q_ingress.c b/tc/q_ingress.c
index 31699a8..0ffd82c 100644
--- a/tc/q_ingress.c
+++ b/tc/q_ingress.c
@@ -21,7 +21,7 @@ static void explain(void)
}
static int ingress_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
while (argc > 0) {
if (strcmp(*argv, "handle") == 0) {
diff --git a/tc/q_mqprio.c b/tc/q_mqprio.c
index 9979852..b568eea 100644
--- a/tc/q_mqprio.c
+++ b/tc/q_mqprio.c
@@ -33,7 +33,7 @@ static void explain(void)
}
static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
- char **argv, struct nlmsghdr *n)
+ char **argv, struct nlmsghdr *n, char *dev)
{
int idx;
struct tc_mqprio_qopt opt = {
diff --git a/tc/q_multiq.c b/tc/q_multiq.c
index ce91fe8..f91ad3a 100644
--- a/tc/q_multiq.c
+++ b/tc/q_multiq.c
@@ -40,7 +40,7 @@ static void explain(void)
}
static int multiq_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
struct tc_multiq_qopt opt = {};
diff --git a/tc/q_netem.c b/tc/q_netem.c
index 82eb46f..0b7b1df 100644
--- a/tc/q_netem.c
+++ b/tc/q_netem.c
@@ -167,7 +167,7 @@ static int get_ticks(__u32 *ticks, const char *str)
}
static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
int dist_size = 0;
struct rtattr *tail;
@@ -396,7 +396,12 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
} else if (matches(*argv, "rate") == 0) {
++present[TCA_NETEM_RATE];
NEXT_ARG();
- if (get_rate64(&rate64, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate64(&rate64, *argv, dev)) {
+ explain1("rate");
+ return -1;
+ }
+ } else if (get_rate64(&rate64, *argv)) {
explain1("rate");
return -1;
}
diff --git a/tc/q_pie.c b/tc/q_pie.c
index db72add..b331bb9 100644
--- a/tc/q_pie.c
+++ b/tc/q_pie.c
@@ -39,7 +39,7 @@ static void explain(void)
#define BETA_MAX 32
static int pie_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
unsigned int limit = 0;
unsigned int target = 0;
diff --git a/tc/q_prio.c b/tc/q_prio.c
index 677e25a..bdfc414 100644
--- a/tc/q_prio.c
+++ b/tc/q_prio.c
@@ -27,7 +27,7 @@ static void explain(void)
fprintf(stderr, "Usage: ... prio bands NUMBER priomap P1 P2...[multiqueue]\n");
}
-static int prio_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int prio_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
int pmap_mode = 0;
int idx = 0;
diff --git a/tc/q_qfq.c b/tc/q_qfq.c
index fa270c8..91f683d 100644
--- a/tc/q_qfq.c
+++ b/tc/q_qfq.c
@@ -35,7 +35,7 @@ static void explain_class(void)
}
static int qfq_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
if (argc > 0) {
if (matches(*argv, "help") != 0)
@@ -48,7 +48,7 @@ static int qfq_parse_opt(struct qdisc_util *qu, int argc, char **argv,
}
static int qfq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
struct rtattr *tail;
__u32 tmp;
diff --git a/tc/q_red.c b/tc/q_red.c
index 1564d6e..ddd78b0 100644
--- a/tc/q_red.c
+++ b/tc/q_red.c
@@ -32,7 +32,7 @@ static void explain(void)
fprintf(stderr, " [ecn] [harddrop]\n");
}
-static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
struct tc_red_qopt opt = {};
unsigned int burst = 0;
@@ -83,7 +83,12 @@ static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl
}
} else if (strcmp(*argv, "bandwidth") == 0) {
NEXT_ARG();
- if (get_rate(&rate, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate(&rate, *argv, dev)) {
+ fprintf(stderr, "Illegal \"bandwidth\"\n");
+ return -1;
+ }
+ } else if (get_rate(&rate, *argv)) {
fprintf(stderr, "Illegal \"bandwidth\"\n");
return -1;
}
diff --git a/tc/q_rr.c b/tc/q_rr.c
index 71ce3ce..341d506 100644
--- a/tc/q_rr.c
+++ b/tc/q_rr.c
@@ -28,7 +28,7 @@ static void explain(void)
}
-static int rr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int rr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
int pmap_mode = 0;
int idx = 0;
diff --git a/tc/q_sfb.c b/tc/q_sfb.c
index d074e87..4f9fa5a 100644
--- a/tc/q_sfb.c
+++ b/tc/q_sfb.c
@@ -48,7 +48,7 @@ static int get_prob(__u32 *val, const char *arg)
}
static int sfb_parse_opt(struct qdisc_util *qu, int argc, char **argv,
- struct nlmsghdr *n)
+ struct nlmsghdr *n, char *dev)
{
struct tc_sfb_qopt opt = {
.rehash_interval = 600*1000,
diff --git a/tc/q_sfq.c b/tc/q_sfq.c
index a875abd..facf2ba 100644
--- a/tc/q_sfq.c
+++ b/tc/q_sfq.c
@@ -34,7 +34,7 @@ static void explain(void)
fprintf(stderr, " [ ecn ] [ harddrop ]\n");
}
-static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
int ok = 0, red = 0;
struct tc_sfq_qopt_v1 opt = {};
diff --git a/tc/q_tbf.c b/tc/q_tbf.c
index 4955ee4..bd59e10 100644
--- a/tc/q_tbf.c
+++ b/tc/q_tbf.c
@@ -35,7 +35,7 @@ static void explain1(const char *arg, const char *val)
}
-static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
int ok = 0;
struct tc_tbf_qopt opt = {};
@@ -125,7 +125,12 @@ static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl
fprintf(stderr, "tbf: duplicate \"rate\" specification\n");
return -1;
}
- if (get_rate64(&rate64, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate64(&rate64, *argv, dev)) {
+ explain1("rate", *argv);
+ return -1;
+ }
+ } else if (get_rate64(&rate64, *argv)) {
explain1("rate", *argv);
return -1;
}
@@ -136,7 +141,12 @@ static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl
fprintf(stderr, "tbf: duplicate \"peakrate\" specification\n");
return -1;
}
- if (get_rate64(&prate64, *argv)) {
+ if (strchr(*argv, '%')) {
+ if (get_percent_rate64(&prate64, *argv, dev)) {
+ explain1("peakrate", *argv);
+ return -1;
+ }
+ } else if (get_rate64(&prate64, *argv)) {
explain1("peakrate", *argv);
return -1;
}
diff --git a/tc/tc.c b/tc/tc.c
index fa71250..0943113 100644
--- a/tc/tc.c
+++ b/tc/tc.c
@@ -59,7 +59,7 @@ static int print_noqopt(struct qdisc_util *qu, FILE *f,
return 0;
}
-static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev)
{
if (argc) {
fprintf(stderr, "Unknown qdisc \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv);
diff --git a/tc/tc_class.c b/tc/tc_class.c
index c4a6a25..912c65f 100644
--- a/tc/tc_class.c
+++ b/tc/tc_class.c
@@ -128,7 +128,7 @@ static int tc_class_modify(int cmd, unsigned int flags, int argc, char **argv)
fprintf(stderr, "Error: Qdisc \"%s\" is classless.\n", k);
return 1;
}
- if (q->parse_copt(q, argc, argv, &req.n))
+ if (q->parse_copt(q, argc, argv, &req.n, d))
return 1;
} else {
if (argc) {
diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c
index fcb75f2..727d1bc 100644
--- a/tc/tc_qdisc.c
+++ b/tc/tc_qdisc.c
@@ -140,7 +140,7 @@ static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv)
if (q) {
if (q->parse_qopt) {
- if (q->parse_qopt(q, argc, argv, &req.n))
+ if (q->parse_qopt(q, argc, argv, &req.n, d))
return 1;
} else if (argc) {
fprintf(stderr, "qdisc '%s' does not support option parsing\n", k);
diff --git a/tc/tc_util.c b/tc/tc_util.c
index 472fc5d..f63add1 100644
--- a/tc/tc_util.c
+++ b/tc/tc_util.c
@@ -190,6 +190,72 @@ static const struct rate_suffix {
{ NULL }
};
+int parse_percent_rate(char *rate, const char *str, char *dev)
+{
+ long max_rate_bits;
+ int ret, saved_errno;
+ double perc, rate_bits;
+ char *str_perc, *p;
+
+ if (!dev[0]) {
+ fprintf(stderr, "No device specified; specify device to rate limit by percentage\n");
+ return -1;
+ }
+
+ if (read_prop(dev, "speed", &max_rate_bits))
+ return -1;
+
+ ret = sscanf(str, "%m[0-9.%]", &str_perc);
+ if (ret != 1)
+ goto malf;
+
+ /* Make sure there's only one percent sign and it's at the end */
+ perc = strtod(str_perc, &p);
+ if (*p != '%' || *(p++) != '\0')
+ goto malf;
+
+ saved_errno = errno;
+ free(str_perc);
+
+ if (perc > 100.0 || perc < 0.0 || saved_errno == ERANGE) {
+ fprintf(stderr, "Invalid rate specified; should be between [0,100]%% but is %s\n", str);
+ return -1;
+ }
+
+ rate_bits = (perc * max_rate_bits) / 100.0;
+
+ ret = snprintf(rate, 20, "%lf", rate_bits);
+ if (ret <= 0 || ret >= 20) {
+ fprintf(stderr, "Unable to parse calculated rate\n");
+ return -1;
+ }
+
+ return 0;
+
+malf:
+ fprintf(stderr, "Specified rate value could not be read or is malformed\n");
+ return -1;
+}
+
+int get_percent_rate(unsigned int *rate, const char *str, char *dev)
+{
+ char r_str[20];
+
+ if (parse_percent_rate(r_str, str, dev))
+ return -1;
+
+ return get_rate(rate, r_str);
+}
+
+int get_percent_rate64(__u64 *rate, const char *str, char *dev)
+{
+ char r_str[20];
+
+ if (parse_percent_rate(r_str, str, dev))
+ return -1;
+
+ return get_rate64(rate, r_str);
+}
int get_rate(unsigned int *rate, const char *str)
{
diff --git a/tc/tc_util.h b/tc/tc_util.h
index 583a21a..e79b44e 100644
--- a/tc/tc_util.h
+++ b/tc/tc_util.h
@@ -24,14 +24,14 @@ struct qdisc_util {
struct qdisc_util *next;
const char *id;
int (*parse_qopt)(struct qdisc_util *qu, int argc,
- char **argv, struct nlmsghdr *n);
+ char **argv, struct nlmsghdr *n, char *dev);
int (*print_qopt)(struct qdisc_util *qu,
FILE *f, struct rtattr *opt);
int (*print_xstats)(struct qdisc_util *qu,
FILE *f, struct rtattr *xstats);
int (*parse_copt)(struct qdisc_util *qu, int argc,
- char **argv, struct nlmsghdr *n);
+ char **argv, struct nlmsghdr *n, char *dev);
int (*print_copt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt);
};
@@ -66,9 +66,13 @@ const char *get_tc_lib(void);
struct qdisc_util *get_qdisc_kind(const char *str);
struct filter_util *get_filter_kind(const char *str);
+int read_prop(char *dev, char *prop, long *value);
+int parse_percent_rate(char *rate, const char *str, char *dev);
int get_qdisc_handle(__u32 *h, const char *str);
int get_rate(unsigned int *rate, const char *str);
+int get_percent_rate(unsigned int *rate, const char *str, char *dev);
int get_rate64(__u64 *rate, const char *str);
+int get_percent_rate64(__u64 *rate, const char *str, 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);
--
1.9.1
next reply other threads:[~2017-11-15 1:55 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-11-15 1:36 Nishanth Devarajan [this message]
2017-11-15 8:13 ` [PATCH iproute2/net-next v2]tc: B.W limits can now be specified in % Jakub Kicinski
2017-11-15 17:58 ` Stephen Hemminger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20171115013618.GA19575@gmail.com \
--to=ndev2021@gmail.com \
--cc=doucette@bu.edu \
--cc=michel.machado@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=stephen@networkplumber.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.