summaryrefslogtreecommitdiff
path: root/proto/perf/perf.c
diff options
context:
space:
mode:
Diffstat (limited to 'proto/perf/perf.c')
-rw-r--r--proto/perf/perf.c319
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,
+};