diff options
Diffstat (limited to 'proto/perf/perf.c')
-rw-r--r-- | proto/perf/perf.c | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/proto/perf/perf.c b/proto/perf/perf.c new file mode 100644 index 00000000..6741f7cb --- /dev/null +++ b/proto/perf/perf.c @@ -0,0 +1,319 @@ +/* + * 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, +}; |