diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index d10f353..2ce55d5 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -83,6 +83,21 @@ struct tc_ratespec __u32 rate; }; +struct tc_sizespec +{ + unsigned int cell_log; + unsigned int addend; +}; + +enum { + TCA_STAB_UNSPEC, + TCA_STAB_BASE, + TCA_STAB_DATA, + __TCA_STAB_MAX +}; + +#define TCA_STAB_MAX (__TCA_STAB_MAX - 1) + /* FIFO section */ struct tc_fifo_qopt diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 5e33a20..addf5fb 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -821,6 +821,7 @@ enum TCA_RATE, TCA_FCNT, TCA_STATS2, + TCA_STAB, __TCA_MAX }; diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c index e9174ab..c38fa87 100644 --- a/tc/tc_qdisc.c +++ b/tc/tc_qdisc.c @@ -41,10 +41,79 @@ static int usage(void) return -1; } +static int parse_stab(int *argcp, char ***argvp, struct tc_sizespec *stab, + __u32 **datap) +{ + int argc = *argcp; + char **argv = *argvp; + + NEXT_ARG(); + while (argc > 0) { + if (matches("overhead", *argv) == 0) { + NEXT_ARG(); + if (stab->addend) + duparg("overhead", *argv); + if (get_size(&stab->addend, *argv)) + return -1; + NEXT_ARG(); + } else if (matches("cell_log", *argv) == 0) { + NEXT_ARG(); + if (stab->cell_log) + duparg("cell_log", *argv); + if (get_u32(&stab->cell_log, *argv, 0)) + return -1; + NEXT_ARG(); + } else if (get_size(*datap, *argv) == 0) { + argv++, argc--; + ++*datap; + } else + break; + } + if (!stab->addend && !stab->cell_log) + return -1; + *argcp = argc; + *argvp = argv; + return 0; +} + +static void print_stab(FILE *f, char *prefix, struct rtattr *tab) +{ + struct rtattr *tb[TCA_STAB_MAX+1]; + struct tc_sizespec *size; + unsigned int i; + __u32 *data; + SPRINT_BUF(buf); + + parse_rtattr_nested(tb, TCA_STAB_MAX, tab); + if (tb[TCA_STAB_BASE] == NULL || + RTA_PAYLOAD(tb[TCA_STAB_BASE]) < sizeof(struct tc_sizespec)) + return; + fprintf(f, "%s", prefix); + size = RTA_DATA(tb[TCA_STAB_BASE]); + if (size->addend) { + print_size(buf, SPRINT_BSIZE-1, size->addend); + fprintf(f, "overhead %s ", buf); + } + if (size->cell_log) + fprintf(f, "cell_log %u ", size->cell_log); + if (tb[TCA_STAB_DATA] == NULL) + return; + data = RTA_DATA(tb[TCA_STAB_DATA]); + for (i = 0; i < RTA_PAYLOAD(tb[TCA_STAB_DATA]) / sizeof(__u32); i++) { + print_size(buf, SPRINT_BSIZE-1, data[i]); + fprintf(f, "%s ", buf); + } +} + int tc_qdisc_modify(int cmd, unsigned flags, int argc, char **argv) { struct qdisc_util *q = NULL; struct tc_estimator est; + struct { + struct tc_sizespec size; + __u32 data[256]; + } stab; + __u32 *stabdata = &stab.data[0]; char d[16]; char k[16]; struct { @@ -55,6 +124,7 @@ int tc_qdisc_modify(int cmd, unsigned fl memset(&req, 0, sizeof(req)); memset(&est, 0, sizeof(est)); + memset(&stab, 0, sizeof(stab)); memset(&d, 0, sizeof(d)); memset(&k, 0, sizeof(k)); @@ -108,6 +178,10 @@ #endif } else if (matches(*argv, "estimator") == 0) { if (parse_estimator(&argc, &argv, &est)) return -1; + } else if (matches(*argv, "stab") == 0) { + if (parse_stab(&argc, &argv, &stab.size, &stabdata)) + return -1; + continue; } else if (matches(*argv, "help") == 0) { usage(); } else { @@ -124,6 +198,16 @@ #endif addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); if (est.ewma_log) addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); + if (stab.size.addend || stab.size.cell_log) { + struct rtattr *tail = NLMSG_TAIL(&req.n); + + addattr_l(&req.n, sizeof(req), TCA_STAB, NULL, 0); + addattr_l(&req.n, sizeof(req), TCA_STAB_BASE, &stab.size, + sizeof(stab.size)); + addattr_l(&req.n, sizeof(req), TCA_STAB_DATA, stab.data, + (void *)stabdata - (void *)stab.data); + tail->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail; + } if (q) { if (!q->parse_qopt) { @@ -215,7 +299,7 @@ static int print_qdisc(const struct sock q = get_qdisc_kind("prio"); else q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND])); - + if (tb[TCA_OPTIONS]) { if (q) q->print_qopt(q, fp, tb[TCA_OPTIONS]); @@ -223,6 +307,12 @@ static int print_qdisc(const struct sock fprintf(fp, "[cannot parse qdisc parameters]"); } fprintf(fp, "\n"); + + if (tb[TCA_STAB]) { + print_stab(fp, " ", tb[TCA_STAB]); + fprintf(fp, "\n"); + } + if (show_stats) { struct rtattr *xstats = NULL;