]> git.proxmox.com Git - mirror_iproute2.git/commitdiff
tc: B.W limits can now be specified in %.
authorNishanth Devarajan <ndev2021@gmail.com>
Tue, 21 Nov 2017 02:20:47 +0000 (07:50 +0530)
committerStephen Hemminger <stephen@networkplumber.org>
Fri, 24 Nov 2017 19:22:13 +0000 (11:22 -0800)
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.

Signed-off-by: Nishanth Devarajan<ndev2021@gmail.com>
36 files changed:
include/utils.h
ip/iptuntap.c
lib/utils.c
man/man8/tc.8
tc/q_atm.c
tc/q_cbq.c
tc/q_cbs.c
tc/q_choke.c
tc/q_clsact.c
tc/q_codel.c
tc/q_drr.c
tc/q_dsmark.c
tc/q_fifo.c
tc/q_fq.c
tc/q_fq_codel.c
tc/q_gred.c
tc/q_hfsc.c
tc/q_hhf.c
tc/q_htb.c
tc/q_ingress.c
tc/q_mqprio.c
tc/q_multiq.c
tc/q_netem.c
tc/q_pie.c
tc/q_prio.c
tc/q_qfq.c
tc/q_red.c
tc/q_rr.c
tc/q_sfb.c
tc/q_sfq.c
tc/q_tbf.c
tc/tc.c
tc/tc_class.c
tc/tc_qdisc.c
tc/tc_util.c
tc/tc_util.h

index 10749fbee1b1421b08790b461aa8c12c685e7700..9c37c613dce269abf476c394e04f3e09eb83d811 100644 (file)
@@ -88,6 +88,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);
index b46e452f212780f2194f8e17636031aad1dfcafa..09f2be2426b05b6bf5a38fad186e05ea8b4df081 100644 (file)
@@ -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)
index 48cead19d62b8375a7184e39e81d4335f7b5407e..7ced8c061cb092474b2ba46b8298cb44c883c985 100644 (file)
 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 = strtol(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 == LONG_MAX || result == LONG_MIN) && errno == ERANGE) {
+               fprintf(stderr, "strtol %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')
index f96911ae1d77d445191024b175cdb051faed6e8c..263dc75d54dd6f22a8f0e3dc36ca757580a8ea25 100644 (file)
@@ -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
index 570e7be57a3d8222b4357b60eb30d522e6d0060a..787b46cde78c6c56533f02e098541ca9ab550ed4 100644 (file)
@@ -26,7 +26,7 @@
 #define MAX_HDR_LEN 64
 
 
-static int atm_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int atm_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev)
 {
        if (argc) {
                fprintf(stderr, "Usage: atm\n");
@@ -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, const char *dev)
 {
        struct sockaddr_atmsvc addr = {};
        struct atm_qos qos;
index e00d4e389cd073425608818570a0d66b17a51289..d05fe9c88808d79f696656d4d5c97ec912d32781 100644 (file)
@@ -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, const 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, const 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;
                        }
index ec1cbe834b1df208b20f015f3854456f60db4f4a..e1134c3a2803368c17de8143c51378cdfb668c40 100644 (file)
@@ -34,7 +34,7 @@ static void explain1(const char *arg, const char *val)
        fprintf(stderr, "cbs: illegal value for \"%s\": \"%s\"\n", arg, val);
 }
 
-static int cbs_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+static int cbs_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev)
 {
        struct tc_cbs_qopt opt = {};
        struct rtattr *tail;
index 726914b2146b0f707bcb116cd753ca6fb48f106b..50ac4ad4ecb8093e00b46e439fd50a7f10186c5f 100644 (file)
@@ -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, const 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;
                        }
index e2a1a7100c3086d178e30b30d887177c662ae70d..5e8dd91cb3744b57fb0d12c21c3e053981f0e404 100644 (file)
@@ -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, const char *dev)
 {
        if (argc > 0) {
                fprintf(stderr, "What is \"%s\"?\n", *argv);
index 253629efdc7b11cc31ca8d8f6506e4b483e8f628..62d6dd68a0998cb6f0f0e79bb58105f960f3e911 100644 (file)
@@ -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, const char *dev)
 {
        unsigned int limit = 0;
        unsigned int target = 0;
index 50623c22ba870366d7d554dd8132b4b97c3fb37c..85aa5b600fd81ca29b44669ef30c4e2508e7be03 100644 (file)
@@ -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, const 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, const char *dev)
 {
        struct rtattr *tail;
        __u32 tmp;
index 0aab387d6f3b4d4a912428fae9db49dd332a07cd..65eeb271588b6e8b1516e0895163b1155c7c7e3b 100644 (file)
@@ -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, const 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, const char *dev)
 {
        struct rtattr *tail;
        __u8 tmp;
index c3e90886ecf3824cf9d5f9c410faa60211068c47..c89d186d98d04f2dab2d5c6fb3ab41f21871daf6 100644 (file)
@@ -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, const char *dev)
 {
        int ok = 0;
        struct tc_fifo_qopt opt = {};
index 49ebeefa0b78035ab30b34d9d257244ffd46f821..51b5bc367cab4d7a3a0cb558609498bc50f10721 100644 (file)
--- 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, const 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;
                        }
index 1eac1403d9ddf9458f5a15edeefa2f72ff1bc5f2..86c6fb240436b3a7d10a5753f65d5f51ca8df6df 100644 (file)
@@ -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, const char *dev)
 {
        unsigned int limit = 0;
        unsigned int flows = 0;
index 2eb906d05a5f233a448e667d35d5be1202467d65..5b5761eebd05e307319a05adb469a2c3b5e78dd4 100644 (file)
@@ -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, const 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;
                        }
index dc9fed930fc03ac8afabf8480dfdb8b44ddddc01..597a659a779221081d91894510b8e2387a4af0c1 100644 (file)
@@ -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 *, const 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, const 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, const 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, const 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, const 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, const 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) {
index d1f15f9a17e16dfe7daaa43e2095e488f5cad75a..2ec3d42f042983a90b5dd17400b6cb83139b5874 100644 (file)
@@ -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, const char *dev)
 {
        unsigned int limit = 0;
        unsigned int quantum = 0;
index db82852316f9aa04c61741768ec51ff46be6d95a..3e295a71b1ef374302c7e78293725013558c3260 100644 (file)
@@ -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, const 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, const 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;
                        }
index 31699a81f3fdaa0c373b27f4fc0548b126880c78..1e4222984d891283e5347b74506c85e57d50dc2f 100644 (file)
@@ -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, const char *dev)
 {
        while (argc > 0) {
                if (strcmp(*argv, "handle") == 0) {
index 997985206cdb7512d91fcf73d3c4f9cf55f6edee..89b460020e27ea8e63e07c2d98eff04ef5819ac3 100644 (file)
@@ -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, const char *dev)
 {
        int idx;
        struct tc_mqprio_qopt opt = {
index ce91fe83d406174b5e141d7ded0975aba928bff6..8ad9e0b2fa3ca9ee3b8cb68f3785f9bd46685aff 100644 (file)
@@ -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, const char *dev)
 {
        struct tc_multiq_qopt opt = {};
 
index 82eb46f2dffe07ec61b5f21add46affed0cccf18..9f9a9b3df255f89fac5269c3f1a43ddb86a8cf71 100644 (file)
@@ -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, const 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;
                        }
index db72add3685ee4fa11a1742e2bb8429bc8301b32..b89f53c73a44987641928d547762f38ade23ed84 100644 (file)
@@ -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, const char *dev)
 {
        unsigned int limit   = 0;
        unsigned int target  = 0;
index 677e25a34d100c5e27397c108ea8ceddf4cf0604..992da6f6ff0dfc2f2bd81acd9ae043b85eb2ebda 100644 (file)
@@ -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, const char *dev)
 {
        int pmap_mode = 0;
        int idx = 0;
index fa270c8ae3bfe204c4dc08415f88bc9ba92a6262..d70ca1ba817e25199fbeac9c5616ce110bd1e22e 100644 (file)
@@ -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, const 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, const char *dev)
 {
        struct rtattr *tail;
        __u32 tmp;
index 1564d6ef9eb79a6c9c7840d752b1f087a36fa492..cf482fccaf7331c861f21c4abf3920a9da17515c 100644 (file)
@@ -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, const 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;
                        }
index 71ce3ce5fe79c04d65be3bdbad70270ac06c8b53..843a4faeef41bab5d8cac306057033c40123fba2 100644 (file)
--- 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, const char *dev)
 {
        int pmap_mode = 0;
        int idx = 0;
index d074e87b6441d0ac30309bf11e09c1d517f1cff0..4b366ddd915c967a88774ba63212922f43ac83b5 100644 (file)
@@ -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, const char *dev)
 {
        struct tc_sfb_qopt opt = {
                .rehash_interval = 600*1000,
index a875abd3eeeb75da09613765d701fba0681ea6d1..6a1d853b7c9351534836f20f1d2c86e47369d9e1 100644 (file)
@@ -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, const char *dev)
 {
        int ok = 0, red = 0;
        struct tc_sfq_qopt_v1 opt = {};
index 4955ee49634fafaa55bdc27963226dc29e2d38ed..2c5edbe2161230d4b124903add34f2182b12a249 100644 (file)
@@ -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, const 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 fa71250d5ee3fd1d00b133dfdf5ae9d4c6f24027..793dca9f9e09d4b88b6f3f6308297f5f28a12bac 100644 (file)
--- 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, const char *dev)
 {
        if (argc) {
                fprintf(stderr, "Unknown qdisc \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv);
index 507864ad8ac01240f5d6dda7e26470a4532aa9e8..1b214b82c702baa1eb781ad9fa8b10e0cca40442 100644 (file)
@@ -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) {
index 56034b59921373cf7167b39000fa07741c8550f8..2f88f11cb99a238e139c651b926fb3e40c2490d0 100644 (file)
@@ -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);
index 197953ab449721bef835da34158883b61f02f743..5532d40be3dc60f8984305f6267bbe93e87c1325 100644 (file)
@@ -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, const 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, const 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)
 {
index 3d3d4f1d8de4a8783619396177a2a891e738243f..eae1091329b8a02d61f416a6db45adf61c6f07a4 100644 (file)
@@ -29,14 +29,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, const 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, const char *dev);
        int (*print_copt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt);
 };
 
@@ -71,9 +71,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, const char *dev);
 int get_rate64(__u64 *rate, const char *str);
+int get_percent_rate64(__u64 *rate, const char *str, const char *dev);
 int get_size(unsigned int *size, const char *str);
 int get_size_and_cell(unsigned int *size, int *cell_log, char *str);
 int get_time(unsigned int *time, const char *str);