bird/proto/perf/perf.c
Maria Matějka 82b742533b Perf: Protocol to measure BIRD performance internally
This protocol is highly experimental and nobody should use it in
production. Anyway it may help you getting some insight into what eats
so much time in filter processing.
2018-12-18 15:08:31 +01:00

320 lines
6.9 KiB
C

/*
* BIRD -- Table-to-Table Routing Protocol a.k.a Pipe
*
* (c) 1999--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
/**
* DOC: Perf
*
* Run this protocol to measure route import and export times.
* Generates a load of dummy routes and measures time to import.
*/
#undef LOCAL_DEBUG
#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "nest/cli.h"
#include "conf/conf.h"
#include "filter/filter.h"
#include "lib/string.h"
#include "perf.h"
#include <stdlib.h>
#include <time.h>
#define PLOG(msg, ...) log(L_INFO "Perf %s " msg, p->p.name, ##__VA_ARGS__)
static inline void
random_data(void *p, uint len)
{
uint ints = (len + sizeof(int) - 1) / sizeof(int);
int *d = alloca(sizeof(uint) * ints);
for (uint i=0; i<ints; i++)
d[i] = random();
memcpy(p, d, len);
}
static ip_addr
random_gw(net_addr *prefix)
{
ASSERT(net_is_ip(prefix));
ip_addr px = net_prefix(prefix);
ip_addr mask = net_pxmask(prefix);
ip_addr out;
random_data(&out, sizeof(ip_addr));
if (ipa_is_ip4(px))
out = ipa_and(out, ipa_from_ip4(ip4_mkmask(32)));
return ipa_or(ipa_and(px, mask), ipa_and(out, ipa_not(mask)));
}
static net_addr_ip4
random_net_ip4(void)
{
u32 x; random_data(&x, sizeof(u32));
x &= ((1 << 20) - 1);
uint pxlen = u32_log2(x) + 5;
ip4_addr px; random_data(&px, sizeof(ip4_addr));
net_addr_ip4 out = {
.type = NET_IP4,
.pxlen = pxlen,
.length = sizeof(net_addr_ip4),
.prefix = ip4_and(ip4_mkmask(pxlen), px),
};
if (!net_validate((net_addr *) &out))
return random_net_ip4();
int c = net_classify((net_addr *) &out);
if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
return random_net_ip4();
return out;
}
struct perf_random_routes {
net_addr net;
rte *ep;
struct rta a;
};
static inline s64 timediff(struct timespec *begin, struct timespec *end)
{ return (end->tv_sec - begin->tv_sec) * (s64) 1000000000 + end->tv_nsec - begin->tv_nsec; }
static void
perf_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
{
struct perf_proto *p = (struct perf_proto *) P;
if (ad->flags & IA_SECONDARY)
return;
if (p->ifa && p->ifa == ad && (flags & IF_CHANGE_DOWN)) {
p->ifa = NULL;
if (ev_active(p->loop))
ev_postpone(p->loop);
return;
}
if (!p->ifa && (flags & IF_CHANGE_UP)) {
p->ifa = ad;
ev_schedule(p->loop);
PLOG("starting");
return;
}
}
static void
perf_loop(void *data)
{
struct proto *P = data;
struct perf_proto *p = data;
const uint N = 1U << p->exp;
const uint offset = sizeof(net_addr) + RTA_MAX_SIZE;
if (!p->run) {
ASSERT(p->data == NULL);
p->data = xmalloc(offset * N);
bzero(p->data, offset * N);
p->stop = 1;
}
ip_addr gw = random_gw(&p->ifa->prefix);
struct timespec ts_begin, ts_generated, ts_rte, ts_update, ts_withdraw;
clock_gettime(CLOCK_MONOTONIC, &ts_begin);
for (uint i=0; i<N; i++) {
struct perf_random_routes *prr = p->data + offset * i;
*((net_addr_ip4 *) &prr->net) = random_net_ip4();
rta *a = &prr->a;
bzero(a, RTA_MAX_SIZE);
a->src = p->p.main_source;
a->source = RTS_PERF;
a->scope = SCOPE_UNIVERSE;
a->dest = RTD_UNICAST;
a->nh.iface = p->ifa->iface;
a->nh.gw = gw;
a->nh.weight = 1;
}
clock_gettime(CLOCK_MONOTONIC, &ts_generated);
for (uint i=0; i<N; i++) {
struct perf_random_routes *prr = p->data + offset * i;
prr->ep = rte_get_temp(&prr->a);
prr->ep->pflags = 0;
}
clock_gettime(CLOCK_MONOTONIC, &ts_rte);
for (uint i=0; i<N; i++) {
struct perf_random_routes *prr = p->data + offset * i;
rte_update(P, &prr->net, prr->ep);
}
clock_gettime(CLOCK_MONOTONIC, &ts_update);
if (!p->keep)
for (uint i=0; i<N; i++) {
struct perf_random_routes *prr = p->data + offset * i;
rte_update(P, &prr->net, NULL);
}
clock_gettime(CLOCK_MONOTONIC, &ts_withdraw);
s64 gentime = timediff(&ts_begin, &ts_generated);
s64 temptime = timediff(&ts_generated, &ts_rte);
s64 updatetime = timediff(&ts_rte, &ts_update);
s64 withdrawtime = timediff(&ts_update, &ts_withdraw);
if (updatetime NS >= p->threshold_min)
PLOG("exp=%u times: gen=%lu temp=%lu update=%lu withdraw=%lu",
p->exp, gentime, temptime, updatetime, withdrawtime);
if (updatetime NS < p->threshold_max)
p->stop = 0;
if ((updatetime NS < p->threshold_min) || (++p->run == p->repeat)) {
xfree(p->data);
p->data = NULL;
if (p->stop || (p->exp == p->to)) {
PLOG("done with exp=%u", p->exp);
return;
}
p->run = 0;
p->exp++;
}
ev_schedule(p->loop);
}
static void
perf_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net UNUSED, struct rte *new UNUSED, struct rte *old UNUSED)
{
struct perf_proto *p = (struct perf_proto *) P;
p->exp++;
return;
}
static void
perf_feed_begin(struct channel *c, int initial UNUSED)
{
struct perf_proto *p = (struct perf_proto *) c->proto;
p->run++;
p->data = xmalloc(sizeof(struct timespec));
p->exp = 0;
clock_gettime(CLOCK_MONOTONIC, p->data);
}
static void
perf_feed_end(struct channel *c)
{
struct perf_proto *p = (struct perf_proto *) c->proto;
struct timespec ts_end;
clock_gettime(CLOCK_MONOTONIC, &ts_end);
s64 feedtime = timediff(p->data, &ts_end);
PLOG("feed n=%lu time=%lu", p->exp, feedtime);
if (p->run < p->repeat)
channel_request_feeding(c);
else
PLOG("feed done");
}
static struct proto *
perf_init(struct proto_config *CF)
{
struct proto *P = proto_new(CF);
P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
struct perf_proto *p = (struct perf_proto *) P;
p->loop = ev_new_init(P->pool, perf_loop, p);
struct perf_config *cf = (struct perf_config *) CF;
p->threshold_min = cf->threshold_min;
p->threshold_max = cf->threshold_max;
p->from = cf->from;
p->to = cf->to;
p->repeat = cf->repeat;
p->keep = cf->keep;
p->mode = cf->mode;
switch (p->mode) {
case PERF_MODE_IMPORT:
P->ifa_notify = perf_ifa_notify;
break;
case PERF_MODE_EXPORT:
P->rt_notify = perf_rt_notify;
P->feed_begin = perf_feed_begin;
P->feed_end = perf_feed_end;
break;
}
return P;
}
static int
perf_start(struct proto *P)
{
struct perf_proto *p = (struct perf_proto *) P;
p->ifa = NULL;
p->run = 0;
p->exp = p->from;
ASSERT(p->data == NULL);
return PS_UP;
}
static int
perf_reconfigure(struct proto *P UNUSED, struct proto_config *CF UNUSED)
{
return 0;
}
static void
perf_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUSED)
{
}
struct protocol proto_perf = {
.name = "Perf",
.template = "perf%d",
.class = PROTOCOL_PERF,
.channel_mask = NB_IP,
.proto_size = sizeof(struct perf_proto),
.config_size = sizeof(struct perf_config),
.init = perf_init,
.start = perf_start,
.reconfigure = perf_reconfigure,
.copy_config = perf_copy_config,
};