mirror_iproute2/tc/q_sfq.c
Leslie Monis 85fdef052b tc: sfq: add support for JSON output
Enable proper JSON output for the SFQ Qdisc.
Use the long double format specifier to print the value of
"probability".
Also, fix the indentation in the online output of the contents in the
tc_sfqred_stats structure.

Signed-off-by: Leslie Monis <lesliemonis@gmail.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
2019-12-29 09:57:27 -08:00

294 lines
7.6 KiB
C

/*
* q_sfq.c SFQ.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <math.h>
#include "utils.h"
#include "tc_util.h"
#include "tc_red.h"
static void explain(void)
{
fprintf(stderr,
"Usage: ... sfq [ limit NUMBER ] [ perturb SECS ] [ quantum BYTES ]\n"
" [ divisor NUMBER ] [ flows NUMBER] [ depth NUMBER ]\n"
" [ headdrop ]\n"
" [ redflowlimit BYTES ] [ min BYTES ] [ max BYTES ]\n"
" [ avpkt BYTES ] [ burst PACKETS ] [ probability P ]\n"
" [ ecn ] [ harddrop ]\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 = {};
unsigned int burst = 0;
int wlog;
unsigned int avpkt = 1000;
double probability = 0.02;
while (argc > 0) {
if (strcmp(*argv, "quantum") == 0) {
NEXT_ARG();
if (get_size(&opt.v0.quantum, *argv)) {
fprintf(stderr, "Illegal \"limit\"\n");
return -1;
}
ok++;
} else if (strcmp(*argv, "perturb") == 0) {
NEXT_ARG();
if (get_integer(&opt.v0.perturb_period, *argv, 0)) {
fprintf(stderr, "Illegal \"perturb\"\n");
return -1;
}
ok++;
} else if (strcmp(*argv, "limit") == 0) {
NEXT_ARG();
if (get_u32(&opt.v0.limit, *argv, 0)) {
fprintf(stderr, "Illegal \"limit\"\n");
return -1;
}
if (opt.v0.limit < 2) {
fprintf(stderr, "Illegal \"limit\", must be > 1\n");
return -1;
}
ok++;
} else if (strcmp(*argv, "divisor") == 0) {
NEXT_ARG();
if (get_u32(&opt.v0.divisor, *argv, 0)) {
fprintf(stderr, "Illegal \"divisor\"\n");
return -1;
}
ok++;
} else if (strcmp(*argv, "flows") == 0) {
NEXT_ARG();
if (get_u32(&opt.v0.flows, *argv, 0)) {
fprintf(stderr, "Illegal \"flows\"\n");
return -1;
}
ok++;
} else if (strcmp(*argv, "depth") == 0) {
NEXT_ARG();
if (get_u32(&opt.depth, *argv, 0)) {
fprintf(stderr, "Illegal \"flows\"\n");
return -1;
}
ok++;
} else if (strcmp(*argv, "headdrop") == 0) {
opt.headdrop = 1;
ok++;
} else if (strcmp(*argv, "redflowlimit") == 0) {
NEXT_ARG();
if (get_u32(&opt.limit, *argv, 0)) {
fprintf(stderr, "Illegal \"redflowlimit\"\n");
return -1;
}
red++;
} else if (strcmp(*argv, "min") == 0) {
NEXT_ARG();
if (get_u32(&opt.qth_min, *argv, 0)) {
fprintf(stderr, "Illegal \"min\"\n");
return -1;
}
red++;
} else if (strcmp(*argv, "max") == 0) {
NEXT_ARG();
if (get_u32(&opt.qth_max, *argv, 0)) {
fprintf(stderr, "Illegal \"max\"\n");
return -1;
}
red++;
} else if (strcmp(*argv, "burst") == 0) {
NEXT_ARG();
if (get_unsigned(&burst, *argv, 0)) {
fprintf(stderr, "Illegal \"burst\"\n");
return -1;
}
red++;
} else if (strcmp(*argv, "avpkt") == 0) {
NEXT_ARG();
if (get_size(&avpkt, *argv)) {
fprintf(stderr, "Illegal \"avpkt\"\n");
return -1;
}
red++;
} else if (strcmp(*argv, "probability") == 0) {
NEXT_ARG();
if (sscanf(*argv, "%lg", &probability) != 1) {
fprintf(stderr, "Illegal \"probability\"\n");
return -1;
}
red++;
} else if (strcmp(*argv, "ecn") == 0) {
opt.flags |= TC_RED_ECN;
red++;
} else if (strcmp(*argv, "harddrop") == 0) {
opt.flags |= TC_RED_HARDDROP;
red++;
} else if (strcmp(*argv, "help") == 0) {
explain();
return -1;
} else {
fprintf(stderr, "What is \"%s\"?\n", *argv);
explain();
return -1;
}
argc--; argv++;
}
if (red) {
if (!opt.limit) {
fprintf(stderr, "Required parameter (redflowlimit) is missing\n");
return -1;
}
/* Compute default min/max thresholds based on
Sally Floyd's recommendations:
http://www.icir.org/floyd/REDparameters.txt
*/
if (!opt.qth_max)
opt.qth_max = opt.limit / 4;
if (!opt.qth_min)
opt.qth_min = opt.qth_max / 3;
if (!burst)
burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt);
if (opt.qth_max > opt.limit) {
fprintf(stderr, "\"max\" is larger than \"limit\"\n");
return -1;
}
if (opt.qth_min >= opt.qth_max) {
fprintf(stderr, "\"min\" is not smaller than \"max\"\n");
return -1;
}
wlog = tc_red_eval_ewma(opt.qth_min, burst, avpkt);
if (wlog < 0) {
fprintf(stderr, "SFQ: failed to calculate EWMA constant.\n");
return -1;
}
if (wlog >= 10)
fprintf(stderr, "SFQ: WARNING. Burst %u seems to be too large.\n", burst);
opt.Wlog = wlog;
wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability);
if (wlog < 0) {
fprintf(stderr, "SFQ: failed to calculate probability.\n");
return -1;
}
opt.Plog = wlog;
opt.max_P = probability * pow(2, 32);
}
if (ok || red)
addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
return 0;
}
static int sfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
{
struct tc_sfq_qopt *qopt;
struct tc_sfq_qopt_v1 *qopt_ext = NULL;
SPRINT_BUF(b1);
SPRINT_BUF(b2);
SPRINT_BUF(b3);
if (opt == NULL)
return 0;
if (RTA_PAYLOAD(opt) < sizeof(*qopt))
return -1;
if (RTA_PAYLOAD(opt) >= sizeof(*qopt_ext))
qopt_ext = RTA_DATA(opt);
qopt = RTA_DATA(opt);
print_uint(PRINT_ANY, "limit", "limit %up ", qopt->limit);
print_uint(PRINT_JSON, "quantum", NULL, qopt->quantum);
print_string(PRINT_FP, NULL, "quantum %s ",
sprint_size(qopt->quantum, b1));
if (qopt_ext && qopt_ext->depth)
print_uint(PRINT_ANY, "depth", "depth %u ", qopt_ext->depth);
if (qopt_ext && qopt_ext->headdrop)
print_bool(PRINT_ANY, "headdrop", "headdrop ", true);
if (show_details)
print_uint(PRINT_ANY, "flows", "flows %u ", qopt->flows);
print_uint(PRINT_ANY, "divisor", "divisor %u ", qopt->divisor);
if (qopt->perturb_period)
print_int(PRINT_ANY, "perturb", "perturb %dsec ",
qopt->perturb_period);
if (qopt_ext && qopt_ext->qth_min) {
print_uint(PRINT_ANY, "ewma", "ewma %u ", qopt_ext->Wlog);
print_uint(PRINT_JSON, "min", NULL, qopt_ext->qth_min);
print_string(PRINT_FP, NULL, "min %s ",
sprint_size(qopt_ext->qth_min, b2));
print_uint(PRINT_JSON, "max", NULL, qopt_ext->qth_max);
print_string(PRINT_FP, NULL, "max %s ",
sprint_size(qopt_ext->qth_max, b3));
print_float(PRINT_ANY, "probability", "probability %lg ",
qopt_ext->max_P / pow(2, 32));
tc_red_print_flags(qopt_ext->flags);
if (show_stats) {
print_nl();
print_uint(PRINT_ANY, "prob_mark", " prob_mark %u",
qopt_ext->stats.prob_mark);
print_uint(PRINT_ANY, "prob_mark_head",
" prob_mark_head %u",
qopt_ext->stats.prob_mark_head);
print_uint(PRINT_ANY, "prob_drop", " prob_drop %u",
qopt_ext->stats.prob_drop);
print_nl();
print_uint(PRINT_ANY, "forced_mark",
" forced_mark %u",
qopt_ext->stats.forced_mark);
print_uint(PRINT_ANY, "forced_mark_head",
" forced_mark_head %u",
qopt_ext->stats.forced_mark_head);
print_uint(PRINT_ANY, "forced_drop", " forced_drop %u",
qopt_ext->stats.forced_drop);
}
}
return 0;
}
static int sfq_print_xstats(struct qdisc_util *qu, FILE *f,
struct rtattr *xstats)
{
struct tc_sfq_xstats *st;
if (xstats == NULL)
return 0;
if (RTA_PAYLOAD(xstats) < sizeof(*st))
return -1;
st = RTA_DATA(xstats);
print_int(PRINT_ANY, "allot", " allot %d", st->allot);
return 0;
}
struct qdisc_util sfq_qdisc_util = {
.id = "sfq",
.parse_qopt = sfq_parse_opt,
.print_qopt = sfq_print_opt,
.print_xstats = sfq_print_xstats,
};