The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
load.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: ed962a7e3768f414686f5b0d854eb8380b5485cf $
19 *
20 * @brief Load generation algorithms
21 * @file io/load.c
22 *
23 * @copyright 2019 Network RADIUS SAS (legal@networkradius.com)
24 */
25RCSID("$Id: ed962a7e3768f414686f5b0d854eb8380b5485cf $")
26
27#include <freeradius-devel/io/load.h>
28
29/*
30 * We use *inverse* numbers to avoid numerical calculation issues.
31 *
32 * i.e. The bad way is to take two small numbers divide them by
33 * alpha / beta and then add them. That process can drop the
34 * lower digits. Instead, we take two small numbers, add them,
35 * and then divide the result by alpha / beta.
36 */
37#define IBETA (4)
38#define IALPHA (8)
39
40#define DIFF(_rtt, _t) \
41 (\
42 fr_time_delta_lt(_rtt, _t) ? \
43 fr_time_delta_sub(_t, _rtt) : \
44 fr_time_delta_sub(_rtt, _t)\
45 )
46
47#define RTTVAR(_rtt, _rttvar, _t) \
48 fr_time_delta_div(\
49 fr_time_delta_add(\
50 fr_time_delta_mul(_rttvar, IBETA - 1), \
51 DIFF(_rtt, _t)\
52 ), \
53 fr_time_delta_wrap(IBETA)\
54 )
55
56#define RTT(_old, _new) fr_time_delta_wrap((fr_time_delta_unwrap(_new) + (fr_time_delta_unwrap(_old) * (IALPHA - 1))) / IALPHA)
57
64
65struct fr_load_s {
71 void *uctx;
72
73 fr_load_stats_t stats; //!< sending statistics
74 fr_time_t step_start; //!< when the current step started
75 fr_time_t step_end; //!< when the current step will end
77 int sent_base; //!< stats.sent when this run started, so that
78 ///< max_requests counts per-run, not cumulatively
79
81 fr_time_delta_t delta; //!< between packets
82
84 bool header; //!< for printing statistics
85
86 fr_time_t next; //!< The next time we're supposed to send a packet
88};
89
92 void *uctx)
93{
94 fr_load_t *l;
95
96 l = talloc_zero(ctx, fr_load_t);
97 if (!l) return NULL;
98
99 if (!config->start_pps) config->start_pps = 1;
100 if (!config->milliseconds) config->milliseconds = 1000;
101 if (!config->parallel) config->parallel = 1;
102
103 l->el = el;
104 l->config = config;
105 l->callback = callback;
106 l->done = done;
107 l->uctx = uctx;
108
109 return l;
110}
111
112/** Send one or more packets.
113 *
114 */
116{
117 int i;
118
119 /*
120 * Send as many packets as necessary.
121 */
122 l->stats.sent += count;
123 l->stats.last_send = now;
124
125 /*
126 * Run the callback AFTER we set the timer. Which makes
127 * it more likely that the next timer fires on time.
128 */
129 for (i = 0; i < count; i++) {
131 }
132}
133
134/** Stop sending new packets, and wait for outstanding replies.
135 *
136 * If every reply has already arrived then the test is complete right
137 * now, so run the "done" callback. Otherwise completion is signalled
138 * by fr_load_generator_have_reply() when the last reply comes in.
139 */
140static void load_drain(fr_load_t *l, fr_time_t now)
141{
143
144 if (l->stats.received >= l->stats.sent) {
145 l->stats.end = now;
146 if (l->done) l->done(l->uctx);
147 }
148}
149
150static void load_timer(fr_timer_list_t *tl, fr_time_t now, void *uctx)
151{
152 fr_load_t *l = uctx;
153 fr_time_delta_t delta;
155
156 /*
157 * Keep track of the overall maximum backlog for the
158 * duration of the entire test run.
159 */
160 l->stats.backlog = l->stats.sent - l->stats.received;
162
163 /*
164 * If we're done this step, go to the next one.
165 */
166 if (fr_time_gteq(l->next, l->step_end)) {
167 l->step_start = l->next;
170 l->pps += l->config->step;
171 if (l->pps > (UINT32_MAX / l->config->milliseconds)) l->pps = UINT32_MAX / l->config->milliseconds;
172
173 /*
174 * The ramp-up has passed max_pps. If there's no
175 * reason to keep sending, stop and drain.
176 * Otherwise hold the rate at max_pps, until
177 * max_requests packets have been sent (checked
178 * below), or forever for "unlimited".
179 */
180 if (l->config->max_pps && (l->pps > l->config->max_pps)) {
181 if (!l->config->max_requests && !l->config->unlimited) {
182 load_drain(l, now);
183 return;
184 }
185
186 l->pps = l->config->max_pps;
187 }
188
189 l->stats.pps = l->pps;
190 l->stats.skipped = 0;
192 }
193
194 /*
195 * We don't have "pps" packets in the backlog, go send
196 * some more. We scale the backlog by 1000 milliseconds
197 * per second. Then, multiple the PPS by the number of
198 * milliseconds of backlog we want to keep.
199 *
200 * If the backlog is smaller than packets/s *
201 * milliseconds of backlog, then keep sending.
202 * Otherwise, switch to a gated mode where we only send
203 * new packets once a reply comes in.
204 */
205 if (((size_t) l->stats.backlog * 1000) < ((size_t) l->pps * l->config->milliseconds)) {
206 uint32_t capacity;
207
209 l->stats.blocked = false;
210 count = l->config->parallel;
211 l->stats.skipped = 0;
212
213 capacity = ((l->pps * l->config->milliseconds) / 1000) - l->stats.backlog;
214
215 /*
216 * Limit "count" so that it doesn't overflow.
217 */
218 if (count > capacity) count = capacity;
219
220 } else {
221
222 /*
223 * We have too many packets in the backlog, we're
224 * gated. Don't send more packets until we have
225 * a reply.
226 *
227 * Note that we will send *these* packets.
228 */
230 l->stats.blocked = true;
231 count = 0;
232 l->stats.skipped += l->count;
233 }
234
235 /*
236 * Stop after max_requests packets, if it's set. Packets
237 * sent by a previous run of the generator don't count.
238 */
239 if (l->config->max_requests) {
240 uint32_t sent = (uint32_t) (l->stats.sent - l->sent_base);
241
242 if (sent >= l->config->max_requests) {
243 load_drain(l, now);
244 return;
245 }
246
247 if (count > (l->config->max_requests - sent)) count = l->config->max_requests - sent;
248 }
249
250 /*
251 * Skip timers if we're too busy.
252 */
253 l->next = fr_time_add(l->next, l->delta);
254 if (fr_time_lt(l->next, now)) {
255 while (fr_time_lt(fr_time_add(l->next, l->delta), now)) {
256// l->stats.skipped += l->count;
257 l->next = fr_time_add(l->next, l->delta);
258 }
259 }
260 delta = fr_time_sub(l->next, now);
261
262 /*
263 * Set the timer for the next packet.
264 */
265 if (fr_timer_in(l, tl, &l->ev, delta, false, load_timer, l) < 0) {
266 load_drain(l, now);
267 return;
268 }
269
270 if (count) fr_load_generator_send(l, now, count);
271}
272
273
274/** Start the load generator.
275 *
276 */
278{
279 uint32_t max;
280
281 l->stats.start = fr_time();
282 l->step_start = l->stats.start;
284
286 l->sent_base = l->stats.sent;
287
288 l->pps = l->config->start_pps;
289
290 /*
291 * Check for numerical overflow. We later multiply pps*milliseconds, and we don't want overflow.
292 */
293 max = UINT32_MAX / l->config->milliseconds;
294
295 if (l->pps > max) l->pps = max;
296
297 l->stats.pps = l->pps;
298 l->count = l->config->parallel;
299
301 l->next = fr_time_add(l->step_start, l->delta);
302
303 load_timer(l->el->tl, l->step_start, l);
304 return 0;
305}
306
307
308/** Stop the load generation through the simple expedient of deleting
309 * the timer associated with it.
310 *
311 */
313{
314 if (!fr_timer_armed(l->ev)) return 0;
315
317 return 0;
318}
319
320
321/** Tell the load generator that we have a reply to a packet we sent.
322 *
323 */
325{
326 fr_time_t now;
328
329 /*
330 * Note that the replies may come out of order with
331 * respect to the request. So we can't use this reply
332 * for any kind of timing.
333 */
334 now = fr_time();
335 t = fr_time_sub(now, request_time);
336
337 l->stats.rttvar = RTTVAR(l->stats.rtt, l->stats.rttvar, t);
338 l->stats.rtt = RTT(l->stats.rtt, t);
339
340 l->stats.received++;
341
342 /*
343 * t is in nanoseconds.
344 */
345 if (fr_time_delta_lt(t, fr_time_delta_wrap(1000))) {
346 l->stats.times[0]++; /* < microseconds */
347 } else if (fr_time_delta_lt(t, fr_time_delta_wrap(10000))) {
348 l->stats.times[1]++; /* microseconds */
349 } else if (fr_time_delta_lt(t, fr_time_delta_wrap(100000))) {
350 l->stats.times[2]++; /* 10s of microseconds */
351 } else if (fr_time_delta_lt(t, fr_time_delta_wrap(1000000))) {
352 l->stats.times[3]++; /* 100s of microseconds */
353 } else if (fr_time_delta_lt(t, fr_time_delta_wrap(10000000))) {
354 l->stats.times[4]++; /* milliseconds */
355 } else if (fr_time_delta_lt(t, fr_time_delta_wrap(100000000))) {
356 l->stats.times[5]++; /* 10s of milliseconds */
357 } else if (fr_time_delta_lt(t, fr_time_delta_wrap(NSEC))) {
358 l->stats.times[6]++; /* 100s of milliseconds */
359 } else {
360 l->stats.times[7]++; /* seconds */
361 }
362
363 /*
364 * Still sending packets. Rely on the timer to send more
365 * packets.
366 */
368
369 /*
370 * The send code has decided that the backlog is too
371 * high. New requests are blocked until replies come in.
372 * Since we have a reply, send another request. Unless
373 * we've already sent max_requests packets, in which case
374 * the timer will notice and start draining.
375 */
376 if (l->state == FR_LOAD_STATE_GATED) {
377 if ((l->stats.skipped > 0) &&
378 (!l->config->max_requests ||
379 ((uint32_t) (l->stats.sent - l->sent_base) < l->config->max_requests))) {
380 l->stats.skipped--;
381 fr_load_generator_send(l, now, 1);
382 }
383 return FR_LOAD_CONTINUE;
384 }
385
386 /*
387 * We're still sending or gated, tell the caller to
388 * continue.
389 */
390 if (l->state != FR_LOAD_STATE_DRAINING) {
391 return FR_LOAD_CONTINUE;
392 }
393 /*
394 * Not yet received all replies. Wait until we have all
395 * replies.
396 */
397 if (l->stats.received < l->stats.sent) return FR_LOAD_CONTINUE;
398
399 l->stats.end = now;
400 return FR_LOAD_DONE;
401}
402
403/** Print load generator statistics in CVS format.
404 *
405 */
406size_t fr_load_generator_stats_sprint(fr_load_t *l, fr_time_t now, char *buffer, size_t buflen)
407{
408 double now_f, last_send_f;
409
410 if (!l->header) {
411 l->header = true;
412 return snprintf(buffer, buflen, "\"time\",\"last_packet\",\"rtt\",\"rttvar\",\"pps\",\"pps_accepted\",\"sent\",\"received\",\"backlog\",\"max_backlog\",\"<usec\",\"us\",\"10us\",\"100us\",\"ms\",\"10ms\",\"100ms\",\"s\",\"blocked\"\n");
413 }
414
415
416 now_f = fr_time_delta_unwrap(fr_time_sub(now, l->stats.start)) / (double)NSEC;
417
418 last_send_f = fr_time_delta_unwrap(fr_time_sub(l->stats.last_send, l->stats.start)) / (double)NSEC;
419
420 /*
421 * Track packets/s. Since times are in nanoseconds, we
422 * have to scale the counters up by NSEC. And since NSEC
423 * is 1B, the calculations have to be done via 64-bit
424 * numbers, and then converted to a final 32-bit counter.
425 */
426 if (fr_time_gt(now, l->step_start)) {
430 fr_time_sub(now, l->step_start))
431 );
432 }
433
434 return snprintf(buffer, buflen,
435 "%f,%f,"
436 "%" PRIu64 ",%" PRIu64 ","
437 "%d,%d,"
438 "%d,%d,"
439 "%d,%d,"
440 "%d,%d,%d,%d,%d,%d,%d,%d,"
441 "%d\n",
442 now_f, last_send_f,
445 l->stats.sent, l->stats.received,
447 l->stats.times[0], l->stats.times[1], l->stats.times[2], l->stats.times[3],
448 l->stats.times[4], l->stats.times[5], l->stats.times[6], l->stats.times[7],
449 l->stats.blocked);
450}
451
453{
454 return &l->stats;
455}
static int const char char buffer[256]
Definition acutest.h:576
#define RCSID(id)
Definition build.h:512
#define fr_time()
Definition event.c:60
Stores all information relating to an event list.
Definition event.c:377
int step_received
Definition load.c:76
static void fr_load_generator_send(fr_load_t *l, fr_time_t now, int count)
Send one or more packets.
Definition load.c:115
void * uctx
Definition load.c:71
static void load_timer(fr_timer_list_t *tl, fr_time_t now, void *uctx)
Definition load.c:150
fr_load_stats_t const * fr_load_generator_stats(fr_load_t const *l)
Definition load.c:452
#define RTTVAR(_rtt, _rttvar, _t)
Definition load.c:47
#define RTT(_old, _new)
Definition load.c:56
fr_time_t step_start
when the current step started
Definition load.c:74
fr_time_t step_end
when the current step will end
Definition load.c:75
static void load_drain(fr_load_t *l, fr_time_t now)
Stop sending new packets, and wait for outstanding replies.
Definition load.c:140
fr_load_callback_t callback
Definition load.c:69
fr_time_delta_t delta
between packets
Definition load.c:81
fr_load_state_t state
Definition load.c:66
fr_timer_t * ev
Definition load.c:87
fr_time_t next
The next time we're supposed to send a packet.
Definition load.c:86
int fr_load_generator_start(fr_load_t *l)
Start the load generator.
Definition load.c:277
int sent_base
stats.sent when this run started, so that max_requests counts per-run, not cumulatively
Definition load.c:77
int fr_load_generator_stop(fr_load_t *l)
Stop the load generation through the simple expedient of deleting the timer associated with it.
Definition load.c:312
fr_event_list_t * el
Definition load.c:67
fr_load_done_callback_t done
Definition load.c:70
fr_load_state_t
Definition load.c:58
@ FR_LOAD_STATE_SENDING
Definition load.c:60
@ FR_LOAD_STATE_GATED
Definition load.c:61
@ FR_LOAD_STATE_INIT
Definition load.c:59
@ FR_LOAD_STATE_DRAINING
Definition load.c:62
fr_load_stats_t stats
sending statistics
Definition load.c:73
fr_load_config_t const * config
Definition load.c:68
fr_load_t * fr_load_generator_create(TALLOC_CTX *ctx, fr_event_list_t *el, fr_load_config_t *config, fr_load_callback_t callback, fr_load_done_callback_t done, void *uctx)
Definition load.c:90
bool header
for printing statistics
Definition load.c:84
uint32_t pps
Definition load.c:80
uint32_t count
Definition load.c:83
size_t fr_load_generator_stats_sprint(fr_load_t *l, fr_time_t now, char *buffer, size_t buflen)
Print load generator statistics in CVS format.
Definition load.c:406
fr_load_reply_t fr_load_generator_have_reply(fr_load_t *l, fr_time_t request_time)
Tell the load generator that we have a reply to a packet we sent.
Definition load.c:324
int sent
total packets sent
Definition load.h:104
fr_time_t last_send
last packet we sent
Definition load.h:99
fr_time_delta_t rtt
smoothed round trip time
Definition load.h:100
int times[8]
response time in microseconds to tens of seconds
Definition load.h:110
int received
total packets received (should be == sent)
Definition load.h:105
int(* fr_load_callback_t)(fr_time_t now, void *uctx)
Definition load.h:124
uint32_t max_requests
hold at max_pps until this many packets are sent, 0 for "no limit"
Definition load.h:92
int skipped
we skipped sending this number of packets
Definition load.h:106
bool unlimited
hold at max_pps forever, the generator is never done
Definition load.h:93
bool blocked
whether or not we're blocked
Definition load.h:109
fr_time_t end
when the test started
Definition load.h:98
int pps_accepted
Accepted PPS for the last second.
Definition load.h:103
uint32_t start_pps
start PPS
Definition load.h:86
int pps
current offered packets/s
Definition load.h:102
void(* fr_load_done_callback_t)(void *uctx)
Called when the generator has stopped sending and every reply has arrived.
Definition load.h:134
fr_load_reply_t
Whether or not the application should continue.
Definition load.h:118
@ FR_LOAD_DONE
the load generator is done
Definition load.h:120
@ FR_LOAD_CONTINUE
continue sending packets.
Definition load.h:119
fr_time_t start
Definition load.h:97
uint32_t parallel
how many packets in parallel to send
Definition load.h:90
fr_time_delta_t duration
duration of each step
Definition load.h:88
int backlog
current backlog
Definition load.h:107
uint32_t step
how much to increase each load test by
Definition load.h:89
int max_backlog
maximum backlog we saw during the test
Definition load.h:108
fr_time_delta_t rttvar
RTT variation.
Definition load.h:101
uint32_t max_pps
max PPS, 0 for "no limit".
Definition load.h:87
uint32_t milliseconds
how many milliseconds of backlog to top out at
Definition load.h:91
Load generation configuration.
Definition load.h:85
unsigned int uint32_t
static const conf_parser_t config[]
Definition base.c:163
static bool done
Definition radclient.c:80
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
return count
Definition module.c:155
#define fr_time_gteq(_a, _b)
Definition time.h:238
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition time.h:154
#define fr_time_delta_lt(_a, _b)
Definition time.h:285
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_delta_wrap(_time)
Definition time.h:152
#define NSEC
Definition time.h:379
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition time.h:196
#define fr_time_gt(_a, _b)
Definition time.h:237
static fr_time_delta_t fr_time_delta_from_nsec(int64_t nsec)
Definition time.h:563
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
static fr_time_delta_t fr_time_delta_div(fr_time_delta_t a, fr_time_delta_t b)
Definition time.h:267
#define fr_time_lt(_a, _b)
Definition time.h:239
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
A timer event.
Definition timer.c:83
#define FR_TIMER_DELETE_RETURN(_ev_p)
Definition timer.h:110
#define fr_timer_in(...)
Definition timer.h:87
static bool fr_timer_armed(fr_timer_t *ev)
Definition timer.h:120
static fr_event_list_t * el