
So far all common tc atttributes were accessed via specific functions, i.e. rtnl_class_set_parent(), rtnl_qdisc_set_parent(), rtnl_cls_set_parent() which implied a lot of code duplication. Since all tc objects are derived from struct rtnl_tc and these common attributes are already stored in there this patch removes all type specific functions and makes rtnl_tc_* attribute functions public. rtnl_qdisc_set_parent(qdisc, 10); becomes: rtnl_tc_set_parent((struct rtnl_tc *) qdisc, 10); This patch also adds the following new attributes to tc objects therefore removing them as tc specific attributes: - mtu - mpu - overhead This allows for the rate table calculations to be unified as well taking into account the new kernel behavior to take care of overhead automatically.
298 lines
7.6 KiB
C
298 lines
7.6 KiB
C
/*
|
|
* lib/route/sch/cbq.c Class Based Queueing
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation version 2.1
|
|
* of the License.
|
|
*
|
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
|
*/
|
|
|
|
#include <netlink-local.h>
|
|
#include <netlink-tc.h>
|
|
#include <netlink/netlink.h>
|
|
#include <netlink/utils.h>
|
|
#include <netlink/route/qdisc.h>
|
|
#include <netlink/route/qdisc-modules.h>
|
|
#include <netlink/route/class.h>
|
|
#include <netlink/route/class-modules.h>
|
|
#include <netlink/route/link.h>
|
|
#include <netlink/route/sch/cbq.h>
|
|
#include <netlink/route/cls/police.h>
|
|
|
|
/**
|
|
* @ingroup qdisc_api
|
|
* @ingroup class_api
|
|
* @defgroup cbq Class Based Queueing (CBQ)
|
|
* @{
|
|
*/
|
|
|
|
static struct trans_tbl ovl_strategies[] = {
|
|
__ADD(TC_CBQ_OVL_CLASSIC,classic)
|
|
__ADD(TC_CBQ_OVL_DELAY,delay)
|
|
__ADD(TC_CBQ_OVL_LOWPRIO,lowprio)
|
|
__ADD(TC_CBQ_OVL_DROP,drop)
|
|
__ADD(TC_CBQ_OVL_RCLASSIC,rclassic)
|
|
};
|
|
|
|
/**
|
|
* Convert a CBQ OVL strategy to a character string
|
|
* @arg type CBQ OVL strategy
|
|
* @arg buf destination buffer
|
|
* @arg len length of destination buffer
|
|
*
|
|
* Converts a CBQ OVL strategy to a character string and stores in the
|
|
* provided buffer. Returns the destination buffer or the type
|
|
* encoded in hex if no match was found.
|
|
*/
|
|
char *nl_ovl_strategy2str(int type, char *buf, size_t len)
|
|
{
|
|
return __type2str(type, buf, len, ovl_strategies,
|
|
ARRAY_SIZE(ovl_strategies));
|
|
}
|
|
|
|
/**
|
|
* Convert a string to a CBQ OVL strategy
|
|
* @arg name CBQ OVL stragegy name
|
|
*
|
|
* Converts a CBQ OVL stragegy name to it's corresponding CBQ OVL strategy
|
|
* type. Returns the type or -1 if none was found.
|
|
*/
|
|
int nl_str2ovl_strategy(const char *name)
|
|
{
|
|
return __str2type(name, ovl_strategies, ARRAY_SIZE(ovl_strategies));
|
|
}
|
|
|
|
static struct nla_policy cbq_policy[TCA_CBQ_MAX+1] = {
|
|
[TCA_CBQ_LSSOPT] = { .minlen = sizeof(struct tc_cbq_lssopt) },
|
|
[TCA_CBQ_RATE] = { .minlen = sizeof(struct tc_ratespec) },
|
|
[TCA_CBQ_WRROPT] = { .minlen = sizeof(struct tc_cbq_wrropt) },
|
|
[TCA_CBQ_OVL_STRATEGY] = { .minlen = sizeof(struct tc_cbq_ovl) },
|
|
[TCA_CBQ_FOPT] = { .minlen = sizeof(struct tc_cbq_fopt) },
|
|
[TCA_CBQ_POLICE] = { .minlen = sizeof(struct tc_cbq_police) },
|
|
};
|
|
|
|
static inline struct rtnl_cbq *cbq_qdisc(struct rtnl_tc *tca)
|
|
{
|
|
return (struct rtnl_cbq *) tca->tc_subdata;
|
|
}
|
|
|
|
static inline struct rtnl_cbq *cbq_alloc(struct rtnl_tc *tca)
|
|
{
|
|
if (!tca->tc_subdata)
|
|
tca->tc_subdata = calloc(1, sizeof(struct rtnl_qdisc));
|
|
|
|
return cbq_qdisc(tca);
|
|
}
|
|
|
|
|
|
static int cbq_msg_parser(struct rtnl_tc *tca)
|
|
{
|
|
struct nlattr *tb[TCA_CBQ_MAX + 1];
|
|
struct rtnl_cbq *cbq;
|
|
int err;
|
|
|
|
err = tca_parse(tb, TCA_CBQ_MAX, tca, cbq_policy);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
cbq = cbq_alloc(tca);
|
|
if (!cbq)
|
|
return -NLE_NOMEM;
|
|
|
|
nla_memcpy(&cbq->cbq_lss, tb[TCA_CBQ_LSSOPT], sizeof(cbq->cbq_lss));
|
|
nla_memcpy(&cbq->cbq_rate, tb[TCA_CBQ_RATE], sizeof(cbq->cbq_rate));
|
|
nla_memcpy(&cbq->cbq_wrr, tb[TCA_CBQ_WRROPT], sizeof(cbq->cbq_wrr));
|
|
nla_memcpy(&cbq->cbq_fopt, tb[TCA_CBQ_FOPT], sizeof(cbq->cbq_fopt));
|
|
nla_memcpy(&cbq->cbq_ovl, tb[TCA_CBQ_OVL_STRATEGY],
|
|
sizeof(cbq->cbq_ovl));
|
|
nla_memcpy(&cbq->cbq_police, tb[TCA_CBQ_POLICE],
|
|
sizeof(cbq->cbq_police));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cbq_qdisc_msg_parser(struct rtnl_qdisc *qdisc)
|
|
{
|
|
return cbq_msg_parser((struct rtnl_tc *) qdisc);
|
|
}
|
|
|
|
static int cbq_class_msg_parser(struct rtnl_class *class)
|
|
{
|
|
return cbq_msg_parser((struct rtnl_tc *) class);
|
|
}
|
|
|
|
static void cbq_qdisc_free_data(struct rtnl_qdisc *qdisc)
|
|
{
|
|
free(qdisc->q_subdata);
|
|
}
|
|
|
|
static int cbq_clone(struct rtnl_tc *_dst, struct rtnl_tc *_src)
|
|
{
|
|
struct rtnl_cbq *src = cbq_qdisc(_src);
|
|
|
|
if (src && !cbq_alloc(_dst))
|
|
return -NLE_NOMEM;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int cbq_qdisc_clone(struct rtnl_qdisc *dst, struct rtnl_qdisc *src)
|
|
{
|
|
return cbq_clone((struct rtnl_tc *) dst, (struct rtnl_tc *) src);
|
|
}
|
|
|
|
static void cbq_class_free_data(struct rtnl_class *class)
|
|
{
|
|
free(class->c_subdata);
|
|
}
|
|
|
|
static int cbq_class_clone(struct rtnl_class *dst, struct rtnl_class *src)
|
|
{
|
|
return cbq_clone((struct rtnl_tc *) dst, (struct rtnl_tc *) src);
|
|
}
|
|
|
|
static void cbq_dump_line(struct rtnl_tc *tca, struct nl_dump_params *p)
|
|
{
|
|
struct rtnl_cbq *cbq;
|
|
double r, rbit;
|
|
char *ru, *rubit;
|
|
|
|
cbq = cbq_qdisc(tca);
|
|
if (!cbq)
|
|
return;
|
|
|
|
r = nl_cancel_down_bytes(cbq->cbq_rate.rate, &ru);
|
|
rbit = nl_cancel_down_bits(cbq->cbq_rate.rate * 8, &rubit);
|
|
|
|
nl_dump(p, " rate %.2f%s/s (%.0f%s) prio %u",
|
|
r, ru, rbit, rubit, cbq->cbq_wrr.priority);
|
|
}
|
|
|
|
static void cbq_qdisc_dump_line(struct rtnl_qdisc *qdisc,
|
|
struct nl_dump_params *p)
|
|
{
|
|
cbq_dump_line((struct rtnl_tc *) qdisc, p);
|
|
}
|
|
|
|
static void cbq_class_dump_line(struct rtnl_class *class,
|
|
struct nl_dump_params *p)
|
|
{
|
|
cbq_dump_line((struct rtnl_tc *) class, p);
|
|
}
|
|
|
|
static void cbq_dump_details(struct rtnl_tc *tca, struct nl_dump_params *p)
|
|
{
|
|
struct rtnl_cbq *cbq;
|
|
char *unit, buf[32];
|
|
double w;
|
|
uint32_t el;
|
|
|
|
cbq = cbq_qdisc(tca);
|
|
if (!cbq)
|
|
return;
|
|
|
|
w = nl_cancel_down_bits(cbq->cbq_wrr.weight * 8, &unit);
|
|
|
|
nl_dump(p, "avgpkt %u mpu %u cell %u allot %u weight %.0f%s\n",
|
|
cbq->cbq_lss.avpkt,
|
|
cbq->cbq_rate.mpu,
|
|
1 << cbq->cbq_rate.cell_log,
|
|
cbq->cbq_wrr.allot, w, unit);
|
|
|
|
el = cbq->cbq_lss.ewma_log;
|
|
nl_dump_line(p, " minidle %uus maxidle %uus offtime "
|
|
"%uus level %u ewma_log %u\n",
|
|
nl_ticks2us(cbq->cbq_lss.minidle >> el),
|
|
nl_ticks2us(cbq->cbq_lss.maxidle >> el),
|
|
nl_ticks2us(cbq->cbq_lss.offtime >> el),
|
|
cbq->cbq_lss.level,
|
|
cbq->cbq_lss.ewma_log);
|
|
|
|
nl_dump_line(p, " penalty %uus strategy %s ",
|
|
nl_ticks2us(cbq->cbq_ovl.penalty),
|
|
nl_ovl_strategy2str(cbq->cbq_ovl.strategy, buf, sizeof(buf)));
|
|
|
|
nl_dump(p, "split %s defmap 0x%08x ",
|
|
rtnl_tc_handle2str(cbq->cbq_fopt.split, buf, sizeof(buf)),
|
|
cbq->cbq_fopt.defmap);
|
|
|
|
nl_dump(p, "police %s",
|
|
nl_police2str(cbq->cbq_police.police, buf, sizeof(buf)));
|
|
}
|
|
|
|
static void cbq_qdisc_dump_details(struct rtnl_qdisc *qdisc,
|
|
struct nl_dump_params *p)
|
|
{
|
|
cbq_dump_details((struct rtnl_tc *) qdisc, p);
|
|
}
|
|
|
|
static void cbq_class_dump_details(struct rtnl_class *class,
|
|
struct nl_dump_params *p)
|
|
{
|
|
cbq_dump_details((struct rtnl_tc *) class, p);
|
|
}
|
|
|
|
static void cbq_dump_stats(struct rtnl_tc *tca, struct nl_dump_params *p)
|
|
{
|
|
struct tc_cbq_xstats *x = tca_xstats(tca);
|
|
|
|
if (!x)
|
|
return;
|
|
|
|
nl_dump_line(p, " borrows overact "
|
|
" avgidle undertime\n");
|
|
nl_dump_line(p, " %10u %10u %10u %10u\n",
|
|
x->borrows, x->overactions, x->avgidle, x->undertime);
|
|
}
|
|
|
|
static void cbq_qdisc_dump_stats(struct rtnl_qdisc *qdisc,
|
|
struct nl_dump_params *p)
|
|
{
|
|
cbq_dump_stats((struct rtnl_tc *) qdisc, p);
|
|
}
|
|
|
|
static void cbq_class_dump_stats(struct rtnl_class *class,
|
|
struct nl_dump_params *p)
|
|
{
|
|
cbq_dump_stats((struct rtnl_tc *) class, p);
|
|
}
|
|
|
|
static struct rtnl_qdisc_ops cbq_qdisc_ops = {
|
|
.qo_kind = "cbq",
|
|
.qo_msg_parser = cbq_qdisc_msg_parser,
|
|
.qo_free_data = cbq_qdisc_free_data,
|
|
.qo_clone = cbq_qdisc_clone,
|
|
.qo_dump = {
|
|
[NL_DUMP_LINE] = cbq_qdisc_dump_line,
|
|
[NL_DUMP_DETAILS] = cbq_qdisc_dump_details,
|
|
[NL_DUMP_STATS] = cbq_qdisc_dump_stats,
|
|
},
|
|
};
|
|
|
|
static struct rtnl_class_ops cbq_class_ops = {
|
|
.co_kind = "cbq",
|
|
.co_msg_parser = cbq_class_msg_parser,
|
|
.co_free_data = cbq_class_free_data,
|
|
.co_clone = cbq_class_clone,
|
|
.co_dump = {
|
|
[NL_DUMP_LINE] = cbq_class_dump_line,
|
|
[NL_DUMP_DETAILS] = cbq_class_dump_details,
|
|
[NL_DUMP_STATS] = cbq_class_dump_stats,
|
|
},
|
|
};
|
|
|
|
static void __init cbq_init(void)
|
|
{
|
|
rtnl_qdisc_register(&cbq_qdisc_ops);
|
|
rtnl_class_register(&cbq_class_ops);
|
|
}
|
|
|
|
static void __exit cbq_exit(void)
|
|
{
|
|
rtnl_qdisc_unregister(&cbq_qdisc_ops);
|
|
rtnl_class_unregister(&cbq_class_ops);
|
|
}
|
|
|
|
/** @} */
|