All of lore.kernel.org
 help / color / mirror / Atom feed
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 v3]tc: B.W limits can now be specified in %.
Date: Sat, 18 Nov 2017 02:13:38 +0530	[thread overview]
Message-ID: <20171117204335.GA17658@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.

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.

v2:
* Modified and moved int read_prop() from ip/iptuntap.c to lib/utils.c,
to make it accessible to tc. 

v3:
* Modified and moved int parse_percent() from tc/q_netem.c to ib/util.c for
use in tc.

* Changed couple variable names in int parse_percent_rate().

* Handled showing error message when device speed is unknown.

* Updated man page to warn users that when specifying rates in %, tc only
uses the current device speed and does not recalculate if it changes after.

During cases when properties (like device speed) are unknown, read_prop()
assumes that if the property file can be opened but not read, it means
that the property is unknown.

Signed-off by: Nishanth Devarajan<ndev2021@gmail.com>

---
 include/utils.h |  2 ++
 ip/iptuntap.c   | 32 ---------------------------
 lib/utils.c     | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 man/man8/tc.8   |  5 ++++-
 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    | 23 ++++++-------------
 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    | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 tc/tc_util.h    |  7 ++++--
 35 files changed, 283 insertions(+), 110 deletions(-)

diff --git a/include/utils.h b/include/utils.h
index 3d91c50..9377266 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -87,6 +87,8 @@ 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(const char *dev, char *prop, long *value);
+int parse_percent(double *val, const char *str);
 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..9d5ba2a 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -39,6 +39,74 @@
 int resolve_hosts;
 int timestamp_short;
 
+int read_prop(const 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)) {
+		fprintf(stderr, "property \"%s\" in file %s is currently unknown\n", prop, fname);
+		fclose(fp);
+		goto out;
+	}
+
+	nl = strchr(buf, '\n');
+	if (nl)
+		*nl = '\0';
+
+	fclose(fp);
+	result = strtoul(buf, &endp, 0);
+
+	if (*endp || buf == 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;
+}
+
+/* Parse a percent e.g: '30%'
+ * return: 0 = ok, -1 = error, 1 = out of range
+ */
+int parse_percent(double *val, const char *str)
+{
+	char *p;
+
+	*val = strtod(str, &p) / 100.;
+	if (*val == HUGE_VALF || *val == HUGE_VALL)
+		return 1;
+	if (*val == 0.0 || (*p && strcmp(p, "%")))
+		return -1;
+
+	return 0;
+}
+
 int get_hex(char c)
 {
 	if (c >= 'A' && c <= 'F')
diff --git a/man/man8/tc.8 b/man/man8/tc.8
index f96911a..263dc75 100644
--- a/man/man8/tc.8
+++ b/man/man8/tc.8
@@ -443,7 +443,10 @@ 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 by a '%'
+character to specify the rate as a percentage of the device's speed
+(e.g. 5%, 99.5%). Warning: specifying the rate as a percentage means a fraction
+of the current speed; if the speed changes, the value will not be recalculated.
 .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..3331093 100644
--- a/tc/q_netem.c
+++ b/tc/q_netem.c
@@ -59,20 +59,6 @@ static void set_percent(__u32 *percent, double per)
 	*percent = rint(per * UINT32_MAX);
 }
 
-/* Parse either a fraction '.3' or percent '30%
- * return: 0 = ok, -1 = error, 1 = out of range
- */
-static int parse_percent(double *val, const char *str)
-{
-	char *p;
-
-	*val = strtod(str, &p) / 100.;
-	if (*p && strcmp(p, "%"))
-		return -1;
-
-	return 0;
-}
-
 static int get_percent(__u32 *percent, const char *str)
 {
 	double per;
@@ -167,7 +153,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 +382,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..b3184bf 100644
--- a/tc/tc_util.c
+++ b/tc/tc_util.c
@@ -190,6 +190,69 @@ static const struct rate_suffix {
 	{ NULL }
 };
 
+int parse_percent_rate(char *rate, const char *str, const char *dev)
+{
+	long dev_mbit;
+	int ret;
+	double perc, rate_mbit;
+	char *str_perc;
+
+	if (!dev[0]) {
+		fprintf(stderr, "No device specified; specify device to rate limit by percentage\n");
+		return -1;
+	}
+
+	if (read_prop(dev, "speed", &dev_mbit))
+		return -1;
+
+	ret = sscanf(str, "%m[0-9.%]", &str_perc);
+	if (ret != 1)
+		goto malf;
+
+	if (parse_percent(&perc, str_perc))
+		goto malf;
+
+	free(str_perc);
+
+	if (perc > 1.0 || perc < 0.0) {
+		fprintf(stderr, "Invalid rate specified; should be between [0,100]%% but is %s\n", str);
+		return -1;
+	}
+
+	rate_mbit = perc * dev_mbit;
+
+	ret = snprintf(rate, 20, "%lf", rate_mbit);
+	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..7b7420a 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,12 @@ 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 parse_percent_rate(char *rate, const char *str, const 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

             reply	other threads:[~2017-11-17 20:43 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-11-17 20:43 Nishanth Devarajan [this message]
2017-11-17 20:54 ` [PATCH iproute2/net-next v3]tc: B.W limits can now be specified in % Stephen Hemminger
2017-11-17 21:12 ` Stephen Hemminger
2017-11-24 19:25 ` Stephen Hemminger
2017-11-24 20:43   ` Nishanth Devarajan

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=20171117204335.GA17658@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.