From c8f201e3d2329a29f8f309dba1d4948d4ead3c58 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 19 Nov 2018 15:03:28 -0800 Subject: [PATCH 1/8] tc: gred: remove unclear comment The comment about providing a proper message seems similar to the comment in the kernel which says: /* hack -- fix at some point with proper message This is how we indicate to tc that there is no VQ at this DP */ it's unclear what that message would be, and whether it's needed. Remove the confusing comment. Signed-off-by: Jakub Kicinski Reviewed-by: Quentin Monnet Signed-off-by: David Ahern --- tc/q_gred.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tc/q_gred.c b/tc/q_gred.c index e63fac72..80a9ccbb 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -302,8 +302,6 @@ static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return -1; } -/* Bad hack! should really return a proper message as shown above*/ - fprintf(f, "vqs %u default %u %s", sopt->DPs, sopt->def_DP, From b640e85d2d98d96549364130a45dce7300a3e0c9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 19 Nov 2018 15:03:29 -0800 Subject: [PATCH 2/8] json: add %hhu helpers Add helpers for printing char-size values. Signed-off-by: Jakub Kicinski Reviewed-by: Quentin Monnet Signed-off-by: David Ahern --- include/json_print.h | 1 + include/json_writer.h | 2 ++ lib/json_print.c | 1 + lib/json_writer.c | 11 +++++++++++ 4 files changed, 15 insertions(+) diff --git a/include/json_print.h b/include/json_print.h index 218da31a..25954070 100644 --- a/include/json_print.h +++ b/include/json_print.h @@ -64,6 +64,7 @@ _PRINT_FUNC(null, const char*); _PRINT_FUNC(string, const char*); _PRINT_FUNC(uint, unsigned int); _PRINT_FUNC(u64, uint64_t); +_PRINT_FUNC(hhu, unsigned char); _PRINT_FUNC(hu, unsigned short); _PRINT_FUNC(hex, unsigned int); _PRINT_FUNC(0xhex, unsigned long long int); diff --git a/include/json_writer.h b/include/json_writer.h index 0c8831c1..354c2754 100644 --- a/include/json_writer.h +++ b/include/json_writer.h @@ -38,6 +38,7 @@ void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num); void jsonw_uint(json_writer_t *self, unsigned int number); void jsonw_u64(json_writer_t *self, uint64_t number); void jsonw_xint(json_writer_t *self, uint64_t number); +void jsonw_hhu(json_writer_t *self, unsigned char num); void jsonw_hu(json_writer_t *self, unsigned short number); void jsonw_int(json_writer_t *self, int number); void jsonw_s64(json_writer_t *self, int64_t number); @@ -52,6 +53,7 @@ void jsonw_float_field(json_writer_t *self, const char *prop, double num); void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num); void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num); void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num); +void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num); void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num); void jsonw_int_field(json_writer_t *self, const char *prop, int num); void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num); diff --git a/lib/json_print.c b/lib/json_print.c index f7ef41c1..4f5fef19 100644 --- a/lib/json_print.c +++ b/lib/json_print.c @@ -118,6 +118,7 @@ void close_json_array(enum output_type type, const char *str) } _PRINT_FUNC(int, int); _PRINT_FUNC(s64, int64_t); +_PRINT_FUNC(hhu, unsigned char); _PRINT_FUNC(hu, unsigned short); _PRINT_FUNC(uint, unsigned int); _PRINT_FUNC(u64, uint64_t); diff --git a/lib/json_writer.c b/lib/json_writer.c index 68890b34..46eff6ad 100644 --- a/lib/json_writer.c +++ b/lib/json_writer.c @@ -211,6 +211,11 @@ void jsonw_float(json_writer_t *self, double num) jsonw_printf(self, "%g", num); } +void jsonw_hhu(json_writer_t *self, unsigned char num) +{ + jsonw_printf(self, "%hhu", num); +} + void jsonw_hu(json_writer_t *self, unsigned short num) { jsonw_printf(self, "%hu", num); @@ -288,6 +293,12 @@ void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num) jsonw_xint(self, num); } +void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num) +{ + jsonw_name(self, prop); + jsonw_hhu(self, num); +} + void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num) { jsonw_name(self, prop); From 33021752cd85f85086c8a9352225eecccd6bb85f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 19 Nov 2018 15:03:30 -0800 Subject: [PATCH 3/8] tc: move RED flag printing to helper Number of qdiscs use the same set of flags to control shared RED implementation. Add a helper for printing those flags. Signed-off-by: Jakub Kicinski Reviewed-by: Quentin Monnet Signed-off-by: David Ahern --- tc/q_choke.c | 3 +-- tc/q_red.c | 14 ++------------ tc/q_sfq.c | 3 +-- tc/tc_red.c | 20 ++++++++++++++++++++ tc/tc_red.h | 1 + 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/tc/q_choke.c b/tc/q_choke.c index b269b133..1353c80c 100644 --- a/tc/q_choke.c +++ b/tc/q_choke.c @@ -188,8 +188,7 @@ static int choke_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) fprintf(f, "limit %up min %up max %up ", qopt->limit, qopt->qth_min, qopt->qth_max); - if (qopt->flags & TC_RED_ECN) - fprintf(f, "ecn "); + tc_red_print_flags(qopt->flags); if (show_details) { fprintf(f, "ewma %u ", qopt->Wlog); diff --git a/tc/q_red.c b/tc/q_red.c index 49fd4ac8..3b3a1204 100644 --- a/tc/q_red.c +++ b/tc/q_red.c @@ -189,18 +189,8 @@ static int red_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) print_uint(PRINT_JSON, "max", NULL, qopt->qth_max); print_string(PRINT_FP, NULL, "max %s ", sprint_size(qopt->qth_max, b3)); - if (qopt->flags & TC_RED_ECN) - print_bool(PRINT_ANY, "ecn", "ecn ", true); - else - print_bool(PRINT_ANY, "ecn", NULL, false); - if (qopt->flags & TC_RED_HARDDROP) - print_bool(PRINT_ANY, "harddrop", "harddrop ", true); - else - print_bool(PRINT_ANY, "harddrop", NULL, false); - if (qopt->flags & TC_RED_ADAPTATIVE) - print_bool(PRINT_ANY, "adaptive", "adaptive ", true); - else - print_bool(PRINT_ANY, "adaptive", NULL, false); + tc_red_print_flags(qopt->flags); + if (show_details) { print_uint(PRINT_ANY, "ewma", "ewma %u ", qopt->Wlog); if (max_P) diff --git a/tc/q_sfq.c b/tc/q_sfq.c index 6a1d853b..eee31ec5 100644 --- a/tc/q_sfq.c +++ b/tc/q_sfq.c @@ -235,8 +235,7 @@ static int sfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) sprint_size(qopt_ext->qth_min, b2), sprint_size(qopt_ext->qth_max, b3), qopt_ext->max_P / pow(2, 32)); - if (qopt_ext->flags & TC_RED_ECN) - fprintf(f, "ecn "); + tc_red_print_flags(qopt_ext->flags); if (show_stats) { fprintf(f, "\n prob_mark %u prob_mark_head %u prob_drop %u", qopt_ext->stats.prob_mark, diff --git a/tc/tc_red.c b/tc/tc_red.c index 178fe088..3ce3ca42 100644 --- a/tc/tc_red.c +++ b/tc/tc_red.c @@ -20,7 +20,9 @@ #include #include +#include "utils.h" #include "tc_core.h" +#include "tc_util.h" #include "tc_red.h" /* @@ -97,3 +99,21 @@ int tc_red_eval_idle_damping(int Wlog, unsigned int avpkt, unsigned int bps, __u sbuf[255] = 31; return clog; } + +void tc_red_print_flags(__u32 flags) +{ + if (flags & TC_RED_ECN) + print_bool(PRINT_ANY, "ecn", "ecn ", true); + else + print_bool(PRINT_ANY, "ecn", NULL, false); + + if (flags & TC_RED_HARDDROP) + print_bool(PRINT_ANY, "harddrop", "harddrop ", true); + else + print_bool(PRINT_ANY, "harddrop", NULL, false); + + if (flags & TC_RED_ADAPTATIVE) + print_bool(PRINT_ANY, "adaptive", "adaptive ", true); + else + print_bool(PRINT_ANY, "adaptive", NULL, false); +} diff --git a/tc/tc_red.h b/tc/tc_red.h index 6c6e6b03..3882c831 100644 --- a/tc/tc_red.h +++ b/tc/tc_red.h @@ -6,5 +6,6 @@ int tc_red_eval_P(unsigned qmin, unsigned qmax, double prob); int tc_red_eval_ewma(unsigned qmin, unsigned burst, unsigned avpkt); int tc_red_eval_idle_damping(int wlog, unsigned avpkt, unsigned bandwidth, __u8 *sbuf); +void tc_red_print_flags(__u32 flags); #endif From 6475e6a5800bf2e1f5da3abc4d7f07dd906cbc26 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 19 Nov 2018 15:03:31 -0800 Subject: [PATCH 4/8] tc: gred: jsonify GRED output Make GRED dump JSON-compatible. Signed-off-by: Jakub Kicinski Reviewed-by: Quentin Monnet Signed-off-by: David Ahern --- tc/q_gred.c | 105 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 31 deletions(-) diff --git a/tc/q_gred.c b/tc/q_gred.c index 80a9ccbb..768b77ba 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -275,8 +275,6 @@ static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) unsigned int i; SPRINT_BUF(b1); - SPRINT_BUF(b2); - SPRINT_BUF(b3); if (opt == NULL) return 0; @@ -302,45 +300,90 @@ static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return -1; } - fprintf(f, "vqs %u default %u %s", - sopt->DPs, - sopt->def_DP, - sopt->grio ? "grio " : ""); + print_uint(PRINT_ANY, "dp_cnt", "vqs %u ", sopt->DPs); + print_uint(PRINT_ANY, "dp_default", "default %u ", sopt->def_DP); - if (limit) - fprintf(f, "limit %s ", - sprint_size(*limit, b1)); + if (sopt->grio) + print_bool(PRINT_ANY, "grio", "grio ", true); + else + print_bool(PRINT_ANY, "grio", NULL, false); + if (limit) { + print_uint(PRINT_JSON, "limit", NULL, *limit); + print_string(PRINT_FP, NULL, "limit %s ", + sprint_size(*limit, b1)); + } + + open_json_array(PRINT_JSON, "vqs"); for (i = 0; i < MAX_DPs; i++, qopt++) { - if (qopt->DP >= MAX_DPs) continue; - fprintf(f, "\n vq %u prio %hhu limit %s min %s max %s ", - qopt->DP, - qopt->prio, - sprint_size(qopt->limit, b1), - sprint_size(qopt->qth_min, b2), - sprint_size(qopt->qth_max, b3)); + if (qopt->DP >= MAX_DPs) + continue; + + open_json_object(NULL); + + print_uint(PRINT_ANY, "vq", "\n vq %u ", qopt->DP); + print_hhu(PRINT_ANY, "prio", "prio %hhu ", qopt->prio); + + print_uint(PRINT_JSON, "limit", NULL, qopt->limit); + print_string(PRINT_FP, NULL, "limit %s ", + sprint_size(qopt->limit, b1)); + + print_uint(PRINT_JSON, "min", NULL, qopt->qth_min); + print_string(PRINT_FP, NULL, "min %s ", + sprint_size(qopt->qth_min, b1)); + + print_uint(PRINT_JSON, "max", NULL, qopt->qth_max); + print_string(PRINT_FP, NULL, "max %s ", + sprint_size(qopt->qth_max, b1)); + if (show_details) { - fprintf(f, "ewma %u ", qopt->Wlog); + print_uint(PRINT_ANY, "ewma", "ewma %u ", qopt->Wlog); if (max_p) - fprintf(f, "probability %lg ", max_p[i] / pow(2, 32)); + print_float(PRINT_ANY, "probability", + "probability %lg ", + max_p[i] / pow(2, 32)); else - fprintf(f, "Plog %u ", qopt->Plog); - fprintf(f, "Scell_log %u ", qopt->Scell_log); + print_uint(PRINT_ANY, "Plog", "Plog %u ", + qopt->Plog); + print_uint(PRINT_ANY, "Scell_log", "Scell_log %u ", + qopt->Scell_log); } if (show_stats) { - fprintf(f, "\n Queue size: average %s current %s ", - sprint_size(qopt->qave, b1), - sprint_size(qopt->backlog, b2)); - fprintf(f, "\n Dropped packets: forced %u early %u pdrop %u other %u ", - qopt->forced, - qopt->early, - qopt->pdrop, - qopt->other); - fprintf(f, "\n Total packets: %u (%s) ", - qopt->packets, - sprint_size(qopt->bytesin, b1)); + if (!is_json_context()) + printf("\n Queue size: "); + + print_uint(PRINT_JSON, "qave", NULL, qopt->qave); + print_string(PRINT_FP, NULL, "average %s ", + sprint_size(qopt->qave, b1)); + + print_uint(PRINT_JSON, "backlog", NULL, qopt->backlog); + print_string(PRINT_FP, NULL, "current %s ", + sprint_size(qopt->backlog, b1)); + + if (!is_json_context()) + printf("\n Dropped packets: "); + + print_uint(PRINT_ANY, "forced_drop", "forced %u ", + qopt->forced); + print_uint(PRINT_ANY, "prob_drop", "early %u ", + qopt->early); + print_uint(PRINT_ANY, "pdrop", "pdrop %u ", + qopt->pdrop); + print_uint(PRINT_ANY, "other", "other %u ", + qopt->other); + + if (!is_json_context()) + printf("\n Total packets: "); + + print_uint(PRINT_ANY, "packets", "%u ", qopt->packets); + + print_uint(PRINT_JSON, "bytes", NULL, qopt->bytesin); + print_string(PRINT_FP, NULL, "(%s) ", + sprint_size(qopt->bytesin, b1)); } + close_json_object(); } + close_json_array(PRINT_JSON, "vqs"); return 0; } From c3e1cd28c195c2dc9ba230b11c516e5ca804562e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 19 Nov 2018 15:03:32 -0800 Subject: [PATCH 5/8] tc: gred: separate out stats printing Printing GRED statistics is long and deserves a function on its own. Signed-off-by: Jakub Kicinski Reviewed-by: Quentin Monnet Signed-off-by: David Ahern --- tc/q_gred.c | 67 +++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/tc/q_gred.c b/tc/q_gred.c index 768b77ba..501437bc 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -265,6 +265,38 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n return 0; } +static void gred_print_stats(struct tc_gred_qopt *qopt) +{ + SPRINT_BUF(b1); + + if (!is_json_context()) + printf("\n Queue size: "); + + print_uint(PRINT_JSON, "qave", NULL, qopt->qave); + print_string(PRINT_FP, NULL, "average %s ", + sprint_size(qopt->qave, b1)); + + print_uint(PRINT_JSON, "backlog", NULL, qopt->backlog); + print_string(PRINT_FP, NULL, "current %s ", + sprint_size(qopt->backlog, b1)); + + if (!is_json_context()) + printf("\n Dropped packets: "); + + print_uint(PRINT_ANY, "forced_drop", "forced %u ", qopt->forced); + print_uint(PRINT_ANY, "prob_drop", "early %u ", qopt->early); + print_uint(PRINT_ANY, "pdrop", "pdrop %u ", qopt->pdrop); + print_uint(PRINT_ANY, "other", "other %u ", qopt->other); + + if (!is_json_context()) + printf("\n Total packets: "); + + print_uint(PRINT_ANY, "packets", "%u ", qopt->packets); + + print_uint(PRINT_JSON, "bytes", NULL, qopt->bytesin); + print_string(PRINT_FP, NULL, "(%s) ", sprint_size(qopt->bytesin, b1)); +} + static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_GRED_MAX + 1]; @@ -348,39 +380,8 @@ static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) print_uint(PRINT_ANY, "Scell_log", "Scell_log %u ", qopt->Scell_log); } - if (show_stats) { - if (!is_json_context()) - printf("\n Queue size: "); - - print_uint(PRINT_JSON, "qave", NULL, qopt->qave); - print_string(PRINT_FP, NULL, "average %s ", - sprint_size(qopt->qave, b1)); - - print_uint(PRINT_JSON, "backlog", NULL, qopt->backlog); - print_string(PRINT_FP, NULL, "current %s ", - sprint_size(qopt->backlog, b1)); - - if (!is_json_context()) - printf("\n Dropped packets: "); - - print_uint(PRINT_ANY, "forced_drop", "forced %u ", - qopt->forced); - print_uint(PRINT_ANY, "prob_drop", "early %u ", - qopt->early); - print_uint(PRINT_ANY, "pdrop", "pdrop %u ", - qopt->pdrop); - print_uint(PRINT_ANY, "other", "other %u ", - qopt->other); - - if (!is_json_context()) - printf("\n Total packets: "); - - print_uint(PRINT_ANY, "packets", "%u ", qopt->packets); - - print_uint(PRINT_JSON, "bytes", NULL, qopt->bytesin); - print_string(PRINT_FP, NULL, "(%s) ", - sprint_size(qopt->bytesin, b1)); - } + if (show_stats) + gred_print_stats(qopt); close_json_object(); } close_json_array(PRINT_JSON, "vqs"); From fdaff63c6a4eaa874bd79af458e8093ac4dd6672 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 19 Nov 2018 15:03:33 -0800 Subject: [PATCH 6/8] tc: gred: use extended stats if available Use the extended attributes with extra and better stats, when possible. Signed-off-by: Jakub Kicinski Reviewed-by: Quentin Monnet Signed-off-by: David Ahern --- tc/q_gred.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 114 insertions(+), 8 deletions(-) diff --git a/tc/q_gred.c b/tc/q_gred.c index 501437bc..fda41a57 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -265,8 +265,90 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n return 0; } -static void gred_print_stats(struct tc_gred_qopt *qopt) +struct tc_gred_info { + __u64 bytes; + __u32 packets; + __u32 backlog; + __u32 prob_drop; + __u32 prob_mark; + __u32 forced_drop; + __u32 forced_mark; + __u32 pdrop; + __u32 other; +}; + +static void +gred_parse_vqs(struct tc_gred_info *info, struct rtattr *vqs) { + int rem = RTA_PAYLOAD(vqs); + unsigned int offset = 0; + + while (rem > offset) { + struct rtattr *tb_entry[TCA_GRED_VQ_ENTRY_MAX + 1] = {}; + struct rtattr *tb[TCA_GRED_VQ_MAX + 1] = {}; + struct rtattr *entry; + unsigned int len; + unsigned int dp; + + entry = RTA_DATA(vqs) + offset; + + parse_rtattr(tb_entry, TCA_GRED_VQ_ENTRY_MAX, entry, + rem - offset); + len = RTA_LENGTH(RTA_PAYLOAD(entry)); + offset += len; + + if (!tb_entry[TCA_GRED_VQ_ENTRY]) { + fprintf(stderr, + "ERROR: Failed to parse Virtual Queue entry\n"); + continue; + } + + parse_rtattr_nested(tb, TCA_GRED_VQ_MAX, + tb_entry[TCA_GRED_VQ_ENTRY]); + + if (!tb[TCA_GRED_VQ_DP]) { + fprintf(stderr, + "ERROR: Virtual Queue without DP attribute\n"); + continue; + } + + dp = rta_getattr_u32(tb[TCA_GRED_VQ_DP]); + + if (tb[TCA_GRED_VQ_STAT_BYTES]) + info[dp].bytes = + rta_getattr_u32(tb[TCA_GRED_VQ_STAT_BYTES]); + if (tb[TCA_GRED_VQ_STAT_PACKETS]) + info[dp].packets = + rta_getattr_u32(tb[TCA_GRED_VQ_STAT_PACKETS]); + if (tb[TCA_GRED_VQ_STAT_BACKLOG]) + info[dp].backlog = + rta_getattr_u32(tb[TCA_GRED_VQ_STAT_BACKLOG]); + if (tb[TCA_GRED_VQ_STAT_PROB_DROP]) + info[dp].prob_drop = + rta_getattr_u32(tb[TCA_GRED_VQ_STAT_PROB_DROP]); + if (tb[TCA_GRED_VQ_STAT_PROB_MARK]) + info[dp].prob_mark = + rta_getattr_u32(tb[TCA_GRED_VQ_STAT_PROB_MARK]); + if (tb[TCA_GRED_VQ_STAT_FORCED_DROP]) + info[dp].forced_drop = + rta_getattr_u32(tb[TCA_GRED_VQ_STAT_FORCED_DROP]); + if (tb[TCA_GRED_VQ_STAT_FORCED_MARK]) + info[dp].forced_mark = + rta_getattr_u32(tb[TCA_GRED_VQ_STAT_FORCED_MARK]); + if (tb[TCA_GRED_VQ_STAT_PDROP]) + info[dp].pdrop = + rta_getattr_u32(tb[TCA_GRED_VQ_STAT_PDROP]); + if (tb[TCA_GRED_VQ_STAT_OTHER]) + info[dp].other = + rta_getattr_u32(tb[TCA_GRED_VQ_STAT_OTHER]); + } +} + +static void +gred_print_stats(struct tc_gred_info *info, struct tc_gred_qopt *qopt) +{ + __u64 bytes = info ? info->bytes : qopt->bytesin; + SPRINT_BUF(b1); if (!is_json_context()) @@ -283,25 +365,44 @@ static void gred_print_stats(struct tc_gred_qopt *qopt) if (!is_json_context()) printf("\n Dropped packets: "); - print_uint(PRINT_ANY, "forced_drop", "forced %u ", qopt->forced); - print_uint(PRINT_ANY, "prob_drop", "early %u ", qopt->early); - print_uint(PRINT_ANY, "pdrop", "pdrop %u ", qopt->pdrop); - print_uint(PRINT_ANY, "other", "other %u ", qopt->other); + if (info) { + print_uint(PRINT_ANY, "forced_drop", "forced %u ", + info->forced_drop); + print_uint(PRINT_ANY, "prob_drop", "early %u ", + info->prob_drop); + print_uint(PRINT_ANY, "pdrop", "pdrop %u ", info->pdrop); + print_uint(PRINT_ANY, "other", "other %u ", info->other); + + if (!is_json_context()) + printf("\n Marked packets: "); + print_uint(PRINT_ANY, "forced_mark", "forced %u ", + info->forced_mark); + print_uint(PRINT_ANY, "prob_mark", "early %u ", + info->prob_mark); + } else { + print_uint(PRINT_ANY, "forced_drop", "forced %u ", + qopt->forced); + print_uint(PRINT_ANY, "prob_drop", "early %u ", qopt->early); + print_uint(PRINT_ANY, "pdrop", "pdrop %u ", qopt->pdrop); + print_uint(PRINT_ANY, "other", "other %u ", qopt->other); + } if (!is_json_context()) printf("\n Total packets: "); print_uint(PRINT_ANY, "packets", "%u ", qopt->packets); - print_uint(PRINT_JSON, "bytes", NULL, qopt->bytesin); - print_string(PRINT_FP, NULL, "(%s) ", sprint_size(qopt->bytesin, b1)); + print_uint(PRINT_JSON, "bytes", NULL, bytes); + print_string(PRINT_FP, NULL, "(%s) ", sprint_size(bytes, b1)); } static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) { + struct tc_gred_info infos[MAX_DPs] = {}; struct rtattr *tb[TCA_GRED_MAX + 1]; struct tc_gred_sopt *sopt; struct tc_gred_qopt *qopt; + bool vq_info = false; __u32 *max_p = NULL; __u32 *limit = NULL; unsigned int i; @@ -332,6 +433,11 @@ static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return -1; } + if (tb[TCA_GRED_VQ_LIST] && show_stats) { + gred_parse_vqs(infos, tb[TCA_GRED_VQ_LIST]); + vq_info = true; + } + print_uint(PRINT_ANY, "dp_cnt", "vqs %u ", sopt->DPs); print_uint(PRINT_ANY, "dp_default", "default %u ", sopt->def_DP); @@ -381,7 +487,7 @@ static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) qopt->Scell_log); } if (show_stats) - gred_print_stats(qopt); + gred_print_stats(vq_info ? &infos[i] : NULL, qopt); close_json_object(); } close_json_array(PRINT_JSON, "vqs"); From 2d7c564a1e632771f72b3a9bf46bbf3e45558089 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 19 Nov 2018 15:03:34 -0800 Subject: [PATCH 7/8] tc: gred: support controlling RED flags Kernel GRED qdisc supports ECN marking, and the harddrop flag but setting and dumping this flag is not possible with iproute2. Add the support. Signed-off-by: Jakub Kicinski Reviewed-by: Quentin Monnet Signed-off-by: David Ahern --- bash-completion/tc | 2 +- tc/q_gred.c | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bash-completion/tc b/bash-completion/tc index 29bca5d9..007e1c2e 100644 --- a/bash-completion/tc +++ b/bash-completion/tc @@ -302,7 +302,7 @@ _tc_qdisc_options() ;; gred) _tc_once_attr 'setup vqs default grio vq prio limit min max avpkt \ - burst probability bandwidth' + burst probability bandwidth ecn harddrop' return 0 ;; hhf) diff --git a/tc/q_gred.c b/tc/q_gred.c index fda41a57..dfa3252b 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -37,7 +37,7 @@ static void explain(void) { fprintf(stderr, "Usage: tc qdisc { add | replace | change } ... gred setup vqs NUMBER\n"); - fprintf(stderr, " default DEFAULT_VQ [ grio ] [ limit BYTES ]\n"); + fprintf(stderr, " default DEFAULT_VQ [ grio ] [ limit BYTES ] [ecn] [harddrop]\n"); fprintf(stderr, " tc qdisc change ... gred vq VQ [ prio VALUE ] limit BYTES\n"); fprintf(stderr, " min BYTES max BYTES avpkt BYTES [ burst PACKETS ]\n"); fprintf(stderr, " [ probability PROBABILITY ] [ bandwidth KBPS ]\n"); @@ -87,6 +87,10 @@ static int init_gred(struct qdisc_util *qu, int argc, char **argv, fprintf(stderr, "Illegal \"limit\"\n"); return -1; } + } else if (strcmp(*argv, "ecn") == 0) { + opt.flags |= TC_RED_ECN; + } else if (strcmp(*argv, "harddrop") == 0) { + opt.flags |= TC_RED_HARDDROP; } else if (strcmp(*argv, "help") == 0) { explain(); return -1; @@ -452,6 +456,8 @@ static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) sprint_size(*limit, b1)); } + tc_red_print_flags(sopt->flags); + open_json_array(PRINT_JSON, "vqs"); for (i = 0; i < MAX_DPs; i++, qopt++) { if (qopt->DP >= MAX_DPs) From f7a8749affbe4a8f542e8f76080db1a64c64e594 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 19 Nov 2018 15:03:35 -0800 Subject: [PATCH 8/8] tc: gred: allow controlling and dumping per-DP RED flags Kernel now support setting ECN and HARDDROP flags per-virtual queue. Allow users to tweak the settings, and print them on dump. Signed-off-by: Jakub Kicinski Reviewed-by: Quentin Monnet Signed-off-by: David Ahern --- tc/q_gred.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tc/q_gred.c b/tc/q_gred.c index dfa3252b..e297b866 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -40,7 +40,7 @@ static void explain(void) fprintf(stderr, " default DEFAULT_VQ [ grio ] [ limit BYTES ] [ecn] [harddrop]\n"); fprintf(stderr, " tc qdisc change ... gred vq VQ [ prio VALUE ] limit BYTES\n"); fprintf(stderr, " min BYTES max BYTES avpkt BYTES [ burst PACKETS ]\n"); - fprintf(stderr, " [ probability PROBABILITY ] [ bandwidth KBPS ]\n"); + fprintf(stderr, " [ probability PROBABILITY ] [ bandwidth KBPS ] [ecn] [harddrop]\n"); } static int init_gred(struct qdisc_util *qu, int argc, char **argv, @@ -121,15 +121,16 @@ 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, const char *dev) { + struct rtattr *tail, *entry, *vqs; int ok = 0; struct tc_gred_qopt opt = { 0 }; unsigned int burst = 0; unsigned int avpkt = 0; + unsigned int flags = 0; double probability = 0.02; unsigned int rate = 0; int parm; __u8 sbuf[256]; - struct rtattr *tail; __u32 max_P; opt.DP = MAX_DPs; @@ -212,6 +213,10 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n return -1; } ok++; + } else if (strcmp(*argv, "ecn") == 0) { + flags |= TC_RED_ECN; + } else if (strcmp(*argv, "harddrop") == 0) { + flags |= TC_RED_HARDDROP; } else if (strcmp(*argv, "help") == 0) { explain(); return -1; @@ -265,11 +270,20 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n addattr_l(n, 1024, TCA_GRED_STAB, sbuf, 256); max_P = probability * pow(2, 32); addattr32(n, 1024, TCA_GRED_MAX_P, max_P); + + vqs = addattr_nest(n, 1024, TCA_GRED_VQ_LIST); + entry = addattr_nest(n, 1024, TCA_GRED_VQ_ENTRY); + addattr32(n, 1024, TCA_GRED_VQ_DP, opt.DP); + addattr32(n, 1024, TCA_GRED_VQ_FLAGS, flags); + addattr_nest_end(n, entry); + addattr_nest_end(n, vqs); + addattr_nest_end(n, tail); return 0; } struct tc_gred_info { + bool flags_present; __u64 bytes; __u32 packets; __u32 backlog; @@ -279,6 +293,7 @@ struct tc_gred_info { __u32 forced_mark; __u32 pdrop; __u32 other; + __u32 flags; }; static void @@ -345,6 +360,10 @@ gred_parse_vqs(struct tc_gred_info *info, struct rtattr *vqs) if (tb[TCA_GRED_VQ_STAT_OTHER]) info[dp].other = rta_getattr_u32(tb[TCA_GRED_VQ_STAT_OTHER]); + info[dp].flags_present = !!tb[TCA_GRED_VQ_FLAGS]; + if (tb[TCA_GRED_VQ_FLAGS]) + info[dp].flags = + rta_getattr_u32(tb[TCA_GRED_VQ_FLAGS]); } } @@ -437,7 +456,7 @@ static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) return -1; } - if (tb[TCA_GRED_VQ_LIST] && show_stats) { + if (tb[TCA_GRED_VQ_LIST]) { gred_parse_vqs(infos, tb[TCA_GRED_VQ_LIST]); vq_info = true; } @@ -480,6 +499,9 @@ static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) print_string(PRINT_FP, NULL, "max %s ", sprint_size(qopt->qth_max, b1)); + if (infos[i].flags_present) + tc_red_print_flags(infos[i].flags); + if (show_details) { print_uint(PRINT_ANY, "ewma", "ewma %u ", qopt->Wlog); if (max_p)