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]tc: B.W limits can now be specified in %
Date: Sat, 28 Oct 2017 22:57:00 +0530 [thread overview]
Message-ID: <20171028172657.GA5039@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 move 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 return 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 | 32 ++++++++++++++++++++++++++++++++
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 | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
tc/tc_util.h | 8 ++++++--
35 files changed, 237 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 ac155bf..444c978 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -39,6 +39,38 @@
int timestamp_short;
+int read_prop(char *dev, char *prop, long *value)
+{
+ char fname[41], 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;
+}
+
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 56e7ad8..5aa73db 100644
--- a/tc/q_atm.c
+++ b/tc/q_atm.c
@@ -45,7 +45,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 f148175..48adb15 100644
--- a/tc/q_cbq.c
+++ b/tc/q_cbq.c
@@ -47,7 +47,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 = {};
@@ -63,7 +63,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;
}
@@ -177,7 +182,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 = {};
@@ -197,13 +202,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 a234d2e..b3bba68 100644
--- a/tc/q_choke.c
+++ b/tc/q_choke.c
@@ -32,7 +32,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;
@@ -54,7 +54,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 09222a1..9395322 100644
--- a/tc/q_codel.c
+++ b/tc/q_codel.c
@@ -59,7 +59,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 79c81a2..9da18fb 100644
--- a/tc/q_drr.c
+++ b/tc/q_drr.c
@@ -34,7 +34,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) {
@@ -50,7 +50,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 79dfd9a..3dea769 100644
--- a/tc/q_dsmark.c
+++ b/tc/q_dsmark.c
@@ -26,7 +26,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;
@@ -85,7 +85,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 3ee8ce9..eba1d38 100644
--- a/tc/q_fifo.c
+++ b/tc/q_fifo.c
@@ -28,7 +28,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 45b2ffd..b2ffbad 100644
--- a/tc/q_fq.c
+++ b/tc/q_fq.c
@@ -72,7 +72,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;
@@ -119,7 +119,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;
}
@@ -133,7 +138,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 500e620..a105121 100644
--- a/tc/q_fq_codel.c
+++ b/tc/q_fq_codel.c
@@ -57,7 +57,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 0a98949..1cbaad8 100644
--- a/tc/q_gred.c
+++ b/tc/q_gred.c
@@ -117,7 +117,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 };
@@ -200,7 +200,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 cf784f1..c4b3151 100644
--- a/tc/q_hfsc.c
+++ b/tc/q_hfsc.c
@@ -24,7 +24,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
@@ -71,7 +71,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 = {};
@@ -142,7 +142,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;
@@ -151,21 +151,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;
}
@@ -174,7 +174,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;
}
@@ -282,7 +282,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;
@@ -290,7 +290,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;
}
@@ -308,7 +313,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;
}
@@ -325,7 +335,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;
@@ -351,7 +361,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;
}
@@ -387,10 +402,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 738b563..b2b9bc1 100644
--- a/tc/q_hhf.c
+++ b/tc/q_hhf.c
@@ -26,7 +26,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 a811c28..8a3ff09 100644
--- a/tc/q_htb.c
+++ b/tc/q_htb.c
@@ -60,7 +60,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 = {
@@ -109,7 +109,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 = {};
@@ -179,7 +179,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;
}
@@ -190,7 +195,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 d6718fb..ceb533d 100644
--- a/tc/q_mqprio.c
+++ b/tc/q_mqprio.c
@@ -30,7 +30,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 9c09c9a..9bb8890 100644
--- a/tc/q_multiq.c
+++ b/tc/q_multiq.c
@@ -41,7 +41,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 cdaddce..e05b3a5 100644
--- a/tc/q_netem.c
+++ b/tc/q_netem.c
@@ -170,7 +170,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;
@@ -399,7 +399,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 a697db7..08a8a62 100644
--- a/tc/q_pie.c
+++ b/tc/q_pie.c
@@ -40,7 +40,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 a28928a..ad5f33e 100644
--- a/tc/q_prio.c
+++ b/tc/q_prio.c
@@ -28,7 +28,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 0e02674..673f584 100644
--- a/tc/q_qfq.c
+++ b/tc/q_qfq.c
@@ -36,7 +36,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)
@@ -49,7 +49,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 ec706aa..2eb7f7b 100644
--- a/tc/q_red.c
+++ b/tc/q_red.c
@@ -33,7 +33,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;
@@ -84,7 +84,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 f330311..e473635 100644
--- a/tc/q_rr.c
+++ b/tc/q_rr.c
@@ -29,7 +29,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 05c5f13..136b8e9 100644
--- a/tc/q_sfb.c
+++ b/tc/q_sfb.c
@@ -49,7 +49,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 b5a9895..7c48e85 100644
--- a/tc/q_sfq.c
+++ b/tc/q_sfq.c
@@ -35,7 +35,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 18b2193..3b5af8f 100644
--- a/tc/q_tbf.c
+++ b/tc/q_tbf.c
@@ -36,7 +36,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 = {};
@@ -126,7 +126,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;
}
@@ -137,7 +142,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 8e64a82..83c775a 100644
--- a/tc/tc.c
+++ b/tc/tc.c
@@ -61,7 +61,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 0214775..6fbd217 100644
--- a/tc/tc_class.c
+++ b/tc/tc_class.c
@@ -129,7 +129,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 c52114a..8f856b7 100644
--- a/tc/tc_qdisc.c
+++ b/tc/tc_qdisc.c
@@ -141,7 +141,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 b39e550..1cd22f4 100644
--- a/tc/tc_util.c
+++ b/tc/tc_util.c
@@ -191,6 +191,60 @@ static const struct rate_suffix {
{ NULL }
};
+int parse_percent_rate(char *rate, const char *str, char *dev)
+{
+ long max_rate_bits;
+ int ret;
+ double perc, rate_bits;
+
+ 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, "%lf%%", &perc);
+ if (ret != 1) {
+ fprintf(stderr, "Specified rate value could not be read or is malformed\n");
+ return -1;
+ }
+
+ if (perc > 100.0 || perc < 0.0) {
+ 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;
+}
+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-10-28 17:45 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-10-28 17:27 Nishanth Devarajan [this message]
2017-10-31 16:35 ` [PATCH iproute2/net-next]tc: B.W limits can now be specified in % 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=20171028172657.GA5039@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.