blob: 4ba63a997775d8ac674811c187f83ed493933373 [file] [log] [blame]
/*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Copyright 2018 Google LLC.
* Author: Kan Yan <kyan@google.com>
*
*/
#include <stdio.h>
#include "utils.h"
#include "tc_util.h"
static const char * const arl_state_names[] = {
"STABLE",
"DRAIN",
"BW_PROBE",
"LATENCY_PROBE",
"UNTHROTTLED",
"UNDEFINED",
};
static void explain(void)
{
fprintf(stderr, "Usage: ... arl [ limit PACKETS] [ buffer TIME ]");
fprintf(stderr, " [ minrate KBPS ] [ maxbw KBPS ]\n");
fprintf(stderr, " [ latency TIME ] ");
fprintf(stderr, "[ latency_hysteresis TIME ]\n");
fprintf(stderr, "[ codel_target TIME ]\n");
fprintf(stderr, "[ ingress ]\n");
}
static int arl_parse_opt(struct qdisc_util *qu, int argc, char **argv,
struct nlmsghdr *n)
{
unsigned int buffer = 0, limit = 0, latency = 0, latency_hysteresis = 0,
mode = 0, codel_target = 0;
__u64 min_rate = 0, max_bw = 0;
struct rtattr *tail;
while (argc > 0) {
if (strcmp(*argv, "buffer") == 0) {
NEXT_ARG();
if (get_time(&buffer, *argv)) {
fprintf(stderr, "Illegal \"buffer\"\n");
return -1;
}
} else if (strcmp(*argv, "minrate") == 0) {
NEXT_ARG();
if (get_rate64(&min_rate, *argv)) {
fprintf(stderr, "Illegal \"minrate\"\n");
return -1;
}
} else if (strcmp(*argv, "maxbw") == 0) {
NEXT_ARG();
if (get_rate64(&max_bw, *argv)) {
fprintf(stderr, "Illegal \"max_bw\"\n");
return -1;
}
} else if (strcmp(*argv, "limit") == 0) {
NEXT_ARG();
if (get_unsigned(&limit, *argv, 0)) {
fprintf(stderr, "Illegal \"limit\"\n");
return -1;
}
} else if (strcmp(*argv, "latency") == 0) {
NEXT_ARG();
if (get_time(&latency, *argv)) {
fprintf(stderr, "Illegal \"latency\"\n");
return -1;
}
} else if (strcmp(*argv, "latency_hysteresis") == 0) {
NEXT_ARG();
if (get_time(&latency_hysteresis, *argv)) {
fprintf(stderr,
"Illegal \"latency hysteresis\"\n");
return -1;
}
} else if (strcmp(*argv, "codel_target") == 0) {
NEXT_ARG();
if (get_time(&codel_target, *argv)) {
fprintf(stderr, "Illegal \"codel_target\"\n");
return -1;
}
} else if (strcmp(*argv, "ingress") == 0) {
mode = 1;
} else if (strcmp(*argv, "help") == 0) {
explain();
return -1;
} else {
fprintf(stderr, "arl: unknown parameter \"%s\"\n",
*argv);
explain();
return -1;
}
argc--; argv++;
}
tail = NLMSG_TAIL(n);
addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
if (buffer)
addattr_l(n, 1024, TCA_ARL_BUFFER, &buffer, sizeof(__u32));
if (min_rate)
addattr_l(n, 1024, TCA_ARL_MIN_RATE, &min_rate, sizeof(__u64));
if (max_bw)
addattr_l(n, 1024, TCA_ARL_MAX_BW, &max_bw, sizeof(__u64));
if (limit)
addattr_l(n, 1024, TCA_ARL_LIMIT, &limit, sizeof(__u32));
if (latency)
addattr_l(n, 1024, TCA_ARL_MAX_LATENCY, &latency,
sizeof(__u32));
if (latency_hysteresis)
addattr_l(n, 1024, TCA_ARL_LATENCY_HYSTERESIS,
&latency_hysteresis, sizeof(__u32));
if (codel_target)
addattr_l(n, 1024, TCA_ARL_CODEL_TARGET, &codel_target,
sizeof(__u32));
if (mode)
addattr_l(n, 1024, TCA_ARL_MODE, &mode, sizeof(__u32));
tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
return 0;
}
static int arl_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
{
unsigned int buffer, limit, latency, latency_hysteresis, mode,
codel_target;
__u64 min_rate = 0, max_bw;
struct rtattr *tb[TCA_ARL_MAX + 1];
SPRINT_BUF(b1);
if (opt == NULL)
return 0;
parse_rtattr_nested(tb, TCA_ARL_MAX, opt);
if (tb[TCA_ARL_MIN_RATE] &&
RTA_PAYLOAD(tb[TCA_ARL_MIN_RATE]) >= sizeof(__u64)) {
min_rate = rta_getattr_u64(tb[TCA_ARL_MIN_RATE]);
fprintf(f, "minrate %s ", sprint_rate(min_rate, b1));
}
if (tb[TCA_ARL_BUFFER] &&
RTA_PAYLOAD(tb[TCA_ARL_BUFFER]) >= sizeof(__u32)) {
buffer = rta_getattr_u32(tb[TCA_ARL_BUFFER]);
fprintf(f, "buffer %s ", sprint_time(buffer, b1));
}
if (tb[TCA_ARL_MAX_BW] &&
RTA_PAYLOAD(tb[TCA_ARL_MAX_BW]) >= sizeof(__u64)) {
max_bw = rta_getattr_u64(tb[TCA_ARL_MAX_BW]);
fprintf(f, "max_bw %s ", sprint_rate(max_bw, b1));
}
if (tb[TCA_ARL_LIMIT] &&
RTA_PAYLOAD(tb[TCA_ARL_LIMIT]) >= sizeof(__u32)) {
limit = rta_getattr_u32(tb[TCA_ARL_LIMIT]);
fprintf(f, "limit %u ", limit);
}
if (tb[TCA_ARL_MAX_LATENCY] &&
RTA_PAYLOAD(tb[TCA_ARL_MAX_LATENCY]) >= sizeof(__u32)) {
latency = rta_getattr_u32(tb[TCA_ARL_MAX_LATENCY]);
fprintf(f, "latency %s ", sprint_time(latency, b1));
}
if (tb[TCA_ARL_LATENCY_HYSTERESIS] &&
RTA_PAYLOAD(tb[TCA_ARL_LATENCY_HYSTERESIS]) >= sizeof(__u32)) {
latency_hysteresis =
rta_getattr_u32(tb[TCA_ARL_LATENCY_HYSTERESIS]);
fprintf(f, "latency_hysteresis %s ",
sprint_time(latency_hysteresis, b1));
}
if (tb[TCA_ARL_CODEL_TARGET] &&
RTA_PAYLOAD(tb[TCA_ARL_CODEL_TARGET]) >= sizeof(__u32)) {
codel_target = rta_getattr_u32(tb[TCA_ARL_CODEL_TARGET]);
fprintf(f, "codel_target %s ", sprint_time(codel_target, b1));
}
if (tb[TCA_ARL_MODE] &&
RTA_PAYLOAD(tb[TCA_ARL_MODE]) >= sizeof(__u32)) {
mode = rta_getattr_u32(tb[TCA_ARL_MODE]);
if (mode)
fprintf(f, "mode ingress");
else
fprintf(f, "mode egress");
}
return 0;
}
static int arl_print_xstats(struct qdisc_util *qu, FILE *f,
struct rtattr *xstats)
{
struct tc_arl_xstats *st;
if (xstats == NULL)
return 0;
if (RTA_PAYLOAD(xstats) < sizeof(*st))
return -1;
st = RTA_DATA(xstats);
fprintf(f, "state %s base_rate %uKbit current_rate %uKbit latency %uus bw %uKbit",
arl_state_names[st->state], st->base_rate, st->current_rate,
st->latency, st->current_bw);
if (st->max_bw)
fprintf(f, " max_bw %uKbit", st->max_bw);
if (st->min_rate)
fprintf(f, " min_base_rate %uKbit", st->min_rate);
return 0;
}
struct qdisc_util arl_qdisc_util = {
.id = "arl",
.parse_qopt = arl_parse_opt,
.print_qopt = arl_print_opt,
.print_xstats = arl_print_xstats,
};