]>
Commit | Line | Data |
---|---|---|
82b74253 MM |
1 | /* |
2 | * BIRD -- Table-to-Table Routing Protocol a.k.a Pipe | |
3 | * | |
4 | * (c) 1999--2000 Martin Mares <mj@ucw.cz> | |
5 | * | |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
9 | /** | |
10 | * DOC: Perf | |
11 | * | |
12 | * Run this protocol to measure route import and export times. | |
13 | * Generates a load of dummy routes and measures time to import. | |
14 | */ | |
15 | ||
16 | #undef LOCAL_DEBUG | |
17 | ||
18 | #include "nest/bird.h" | |
19 | #include "nest/iface.h" | |
20 | #include "nest/protocol.h" | |
21 | #include "nest/route.h" | |
22 | #include "nest/cli.h" | |
23 | #include "conf/conf.h" | |
24 | #include "filter/filter.h" | |
25 | #include "lib/string.h" | |
26 | ||
27 | #include "perf.h" | |
28 | ||
29 | #include <stdlib.h> | |
30 | #include <time.h> | |
31 | ||
7411b694 | 32 | #define PLOG(msg, ...) log(L_INFO "Perf %s %s " msg, BIRD_VERSION, p->p.name, ##__VA_ARGS__) |
82b74253 MM |
33 | |
34 | static inline void | |
35 | random_data(void *p, uint len) | |
36 | { | |
37 | uint ints = (len + sizeof(int) - 1) / sizeof(int); | |
38 | int *d = alloca(sizeof(uint) * ints); | |
39 | for (uint i=0; i<ints; i++) | |
40 | d[i] = random(); | |
41 | ||
42 | memcpy(p, d, len); | |
43 | } | |
44 | ||
45 | static ip_addr | |
46 | random_gw(net_addr *prefix) | |
47 | { | |
48 | ASSERT(net_is_ip(prefix)); | |
49 | ip_addr px = net_prefix(prefix); | |
50 | ip_addr mask = net_pxmask(prefix); | |
51 | ||
52 | ip_addr out; | |
53 | random_data(&out, sizeof(ip_addr)); | |
54 | ||
55 | if (ipa_is_ip4(px)) | |
56 | out = ipa_and(out, ipa_from_ip4(ip4_mkmask(32))); | |
57 | ||
58 | return ipa_or(ipa_and(px, mask), ipa_and(out, ipa_not(mask))); | |
59 | } | |
60 | ||
61 | static net_addr_ip4 | |
62 | random_net_ip4(void) | |
63 | { | |
64 | u32 x; random_data(&x, sizeof(u32)); | |
65 | x &= ((1 << 20) - 1); | |
66 | uint pxlen = u32_log2(x) + 5; | |
67 | ||
68 | ip4_addr px; random_data(&px, sizeof(ip4_addr)); | |
69 | ||
70 | net_addr_ip4 out = { | |
71 | .type = NET_IP4, | |
72 | .pxlen = pxlen, | |
73 | .length = sizeof(net_addr_ip4), | |
74 | .prefix = ip4_and(ip4_mkmask(pxlen), px), | |
75 | }; | |
76 | ||
77 | if (!net_validate((net_addr *) &out)) | |
78 | return random_net_ip4(); | |
79 | ||
80 | int c = net_classify((net_addr *) &out); | |
81 | if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) | |
82 | return random_net_ip4(); | |
83 | ||
84 | return out; | |
85 | } | |
86 | ||
87 | struct perf_random_routes { | |
dc042d87 | 88 | struct rta *a; |
82b74253 | 89 | net_addr net; |
82b74253 MM |
90 | }; |
91 | ||
dc042d87 | 92 | //static const uint perf_random_routes_size = sizeof(struct perf_random_routes) + (RTA_MAX_SIZE - sizeof(struct rta)); |
2f02c25e | 93 | |
82b74253 MM |
94 | static inline s64 timediff(struct timespec *begin, struct timespec *end) |
95 | { return (end->tv_sec - begin->tv_sec) * (s64) 1000000000 + end->tv_nsec - begin->tv_nsec; } | |
96 | ||
97 | static void | |
98 | perf_ifa_notify(struct proto *P, uint flags, struct ifa *ad) | |
99 | { | |
100 | struct perf_proto *p = (struct perf_proto *) P; | |
101 | ||
102 | if (ad->flags & IA_SECONDARY) | |
103 | return; | |
104 | ||
105 | if (p->ifa && p->ifa == ad && (flags & IF_CHANGE_DOWN)) { | |
106 | p->ifa = NULL; | |
107 | if (ev_active(p->loop)) | |
108 | ev_postpone(p->loop); | |
109 | ||
110 | return; | |
111 | } | |
112 | ||
113 | if (!p->ifa && (flags & IF_CHANGE_UP)) { | |
114 | p->ifa = ad; | |
115 | ev_schedule(p->loop); | |
116 | PLOG("starting"); | |
117 | return; | |
118 | } | |
119 | } | |
120 | ||
121 | static void | |
122 | perf_loop(void *data) | |
123 | { | |
124 | struct proto *P = data; | |
125 | struct perf_proto *p = data; | |
126 | ||
127 | const uint N = 1U << p->exp; | |
82b74253 MM |
128 | |
129 | if (!p->run) { | |
130 | ASSERT(p->data == NULL); | |
dc042d87 | 131 | p->data = xmalloc(sizeof(struct perf_random_routes) * N); |
82b74253 MM |
132 | p->stop = 1; |
133 | } | |
134 | ||
135 | ip_addr gw = random_gw(&p->ifa->prefix); | |
136 | ||
6dda6931 | 137 | struct timespec ts_begin, ts_generated, ts_update, ts_withdraw; |
82b74253 MM |
138 | |
139 | clock_gettime(CLOCK_MONOTONIC, &ts_begin); | |
140 | ||
141 | for (uint i=0; i<N; i++) { | |
dc042d87 | 142 | *((net_addr_ip4 *) &(p->data[i].net)) = random_net_ip4(); |
82b74253 | 143 | |
6dda6931 | 144 | if (!p->attrs_per_rte || !(i % p->attrs_per_rte)) { |
dc042d87 | 145 | struct rta a0 = { |
dc042d87 MM |
146 | .source = RTS_PERF, |
147 | .scope = SCOPE_UNIVERSE, | |
148 | .dest = RTD_UNICAST, | |
eb937358 | 149 | .pref = p->p.main_channel->preference, |
dc042d87 MM |
150 | .nh.iface = p->ifa->iface, |
151 | .nh.gw = gw, | |
152 | .nh.weight = 1, | |
153 | }; | |
154 | ||
155 | p->data[i].a = rta_lookup(&a0); | |
6dda6931 | 156 | } |
dc042d87 MM |
157 | else |
158 | p->data[i].a = rta_clone(p->data[i-1].a); | |
82b74253 MM |
159 | } |
160 | ||
6dda6931 | 161 | clock_gettime(CLOCK_MONOTONIC, &ts_generated); |
82b74253 MM |
162 | |
163 | for (uint i=0; i<N; i++) { | |
5cff1d5f | 164 | rte *e = rte_get_temp(p->data[i].a, p->p.main_source); |
dc042d87 MM |
165 | |
166 | rte_update(P, &(p->data[i].net), e); | |
82b74253 MM |
167 | } |
168 | ||
169 | clock_gettime(CLOCK_MONOTONIC, &ts_update); | |
170 | ||
171 | if (!p->keep) | |
dc042d87 MM |
172 | for (uint i=0; i<N; i++) |
173 | rte_update(P, &(p->data[i].net), NULL); | |
82b74253 MM |
174 | |
175 | clock_gettime(CLOCK_MONOTONIC, &ts_withdraw); | |
176 | ||
177 | s64 gentime = timediff(&ts_begin, &ts_generated); | |
6dda6931 | 178 | s64 updatetime = timediff(&ts_generated, &ts_update); |
82b74253 MM |
179 | s64 withdrawtime = timediff(&ts_update, &ts_withdraw); |
180 | ||
181 | if (updatetime NS >= p->threshold_min) | |
759b204b | 182 | PLOG("exp=%u times: gen=%ld update=%ld withdraw=%ld", |
6dda6931 | 183 | p->exp, gentime, updatetime, withdrawtime); |
82b74253 MM |
184 | |
185 | if (updatetime NS < p->threshold_max) | |
186 | p->stop = 0; | |
187 | ||
188 | if ((updatetime NS < p->threshold_min) || (++p->run == p->repeat)) { | |
189 | xfree(p->data); | |
190 | p->data = NULL; | |
191 | ||
192 | if (p->stop || (p->exp == p->to)) { | |
193 | PLOG("done with exp=%u", p->exp); | |
194 | return; | |
195 | } | |
196 | ||
197 | p->run = 0; | |
198 | p->exp++; | |
199 | } | |
200 | ||
e85e37d9 | 201 | rt_schedule_prune(P->main_channel->table); |
82b74253 MM |
202 | ev_schedule(p->loop); |
203 | } | |
204 | ||
205 | static void | |
206 | perf_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net UNUSED, struct rte *new UNUSED, struct rte *old UNUSED) | |
207 | { | |
208 | struct perf_proto *p = (struct perf_proto *) P; | |
209 | p->exp++; | |
210 | return; | |
211 | } | |
212 | ||
213 | static void | |
214 | perf_feed_begin(struct channel *c, int initial UNUSED) | |
215 | { | |
216 | struct perf_proto *p = (struct perf_proto *) c->proto; | |
217 | ||
218 | p->run++; | |
dc042d87 | 219 | p->feed_begin = xmalloc(sizeof(struct timespec)); |
82b74253 MM |
220 | p->exp = 0; |
221 | ||
dc042d87 | 222 | clock_gettime(CLOCK_MONOTONIC, p->feed_begin); |
82b74253 MM |
223 | } |
224 | ||
225 | static void | |
226 | perf_feed_end(struct channel *c) | |
227 | { | |
228 | struct perf_proto *p = (struct perf_proto *) c->proto; | |
229 | struct timespec ts_end; | |
230 | clock_gettime(CLOCK_MONOTONIC, &ts_end); | |
231 | ||
dc042d87 | 232 | s64 feedtime = timediff(p->feed_begin, &ts_end); |
82b74253 MM |
233 | |
234 | PLOG("feed n=%lu time=%lu", p->exp, feedtime); | |
235 | ||
dc042d87 MM |
236 | xfree(p->feed_begin); |
237 | p->feed_begin = NULL; | |
238 | ||
82b74253 MM |
239 | if (p->run < p->repeat) |
240 | channel_request_feeding(c); | |
241 | else | |
242 | PLOG("feed done"); | |
243 | } | |
244 | ||
245 | static struct proto * | |
246 | perf_init(struct proto_config *CF) | |
247 | { | |
248 | struct proto *P = proto_new(CF); | |
249 | ||
250 | P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF)); | |
251 | ||
252 | struct perf_proto *p = (struct perf_proto *) P; | |
253 | ||
254 | p->loop = ev_new_init(P->pool, perf_loop, p); | |
255 | ||
256 | struct perf_config *cf = (struct perf_config *) CF; | |
257 | ||
258 | p->threshold_min = cf->threshold_min; | |
259 | p->threshold_max = cf->threshold_max; | |
260 | p->from = cf->from; | |
261 | p->to = cf->to; | |
262 | p->repeat = cf->repeat; | |
263 | p->keep = cf->keep; | |
264 | p->mode = cf->mode; | |
6dda6931 | 265 | p->attrs_per_rte = cf->attrs_per_rte; |
82b74253 MM |
266 | |
267 | switch (p->mode) { | |
268 | case PERF_MODE_IMPORT: | |
269 | P->ifa_notify = perf_ifa_notify; | |
270 | break; | |
271 | case PERF_MODE_EXPORT: | |
272 | P->rt_notify = perf_rt_notify; | |
273 | P->feed_begin = perf_feed_begin; | |
274 | P->feed_end = perf_feed_end; | |
275 | break; | |
276 | } | |
277 | ||
278 | return P; | |
279 | } | |
280 | ||
281 | static int | |
282 | perf_start(struct proto *P) | |
283 | { | |
284 | struct perf_proto *p = (struct perf_proto *) P; | |
285 | ||
286 | p->ifa = NULL; | |
287 | p->run = 0; | |
288 | p->exp = p->from; | |
289 | ASSERT(p->data == NULL); | |
290 | ||
291 | return PS_UP; | |
292 | } | |
293 | ||
294 | static int | |
295 | perf_reconfigure(struct proto *P UNUSED, struct proto_config *CF UNUSED) | |
296 | { | |
297 | return 0; | |
298 | } | |
299 | ||
300 | static void | |
301 | perf_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUSED) | |
302 | { | |
303 | } | |
304 | ||
305 | struct protocol proto_perf = { | |
306 | .name = "Perf", | |
307 | .template = "perf%d", | |
308 | .class = PROTOCOL_PERF, | |
309 | .channel_mask = NB_IP, | |
310 | .proto_size = sizeof(struct perf_proto), | |
311 | .config_size = sizeof(struct perf_config), | |
312 | .init = perf_init, | |
313 | .start = perf_start, | |
314 | .reconfigure = perf_reconfigure, | |
315 | .copy_config = perf_copy_config, | |
316 | }; | |
4a23ede2 MM |
317 | |
318 | void | |
319 | perf_build(void) | |
320 | { | |
321 | proto_build(&proto_perf); | |
322 | } |