2 * A tool for measuring FWP latency. This program aims to be simpler
3 * than wme_test and is intended to be run on a single machine with
4 * multiple wifi interfaces and send-to-self kernel patch applied.
6 * This program creates both sides of communication and measures the
7 * communication delays.
10 #include <netinet/in.h>
11 #include <arpa/inet.h>
15 #include <netinet/in.h>
17 #include <sys/socket.h>
18 #include <sys/types.h>
20 #include <sys/param.h>
24 #include <semaphore.h>
25 #include <ul_logreg.h>
27 bool opt_verbose = false;
28 bool opt_quiet = false;
30 #define HIST_MAX_US 10000000
31 #define HIST_RES_US 10
34 unsigned cnt[(HIST_MAX_US+1)/HIST_RES_US];
38 /* static void hist_init(struct histogram *h) */
40 /* memset(h, 0, sizeof(*h)); */
43 static void hist_add(struct histogram *h, int us)
48 __sync_fetch_and_add(&h->cnt[us/HIST_RES_US], 1);
53 } while (us > max && ! __sync_bool_compare_and_swap(&h->max, max, us));
57 static unsigned hist_get_percentile(struct histogram *h, unsigned p)
59 uint64_t sum = 0, psum;
61 for (i=0; i<(HIST_MAX_US+1)/HIST_RES_US; i++)
66 for (i=0; i<(HIST_MAX_US+1)/HIST_RES_US; i++) {
80 struct histogram hist;
82 void set_rt_prio(int priority)
85 static struct sched_param param;
87 if ((maxpri = sched_get_priority_max(SCHED_FIFO)) == -1) {
88 fprintf(stderr, "warning: sched_get_priority_max failed\n");
91 if ((minpri = sched_get_priority_min(SCHED_FIFO)) == -1) {
92 fprintf(stderr, "warning: sched_get_priority_min failed\n");
95 if (priority > maxpri) {
96 fprintf(stderr, "warning: maximum priority allowed is %d.\n", maxpri);
98 if (priority < minpri) {
99 fprintf(stderr, "warning: minimum priority allowed is %d.\n", minpri);
102 param.sched_priority = priority;
104 if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
105 fprintf(stderr, "warning: sched_setscheduler failed\n");
108 mlockall(MCL_CURRENT | MCL_FUTURE);
111 struct stream_params {
115 struct in_addr src, dst;
125 int negotiate_contract(struct stream_params *p)
127 frsh_contract_t contract;
129 frsh_rel_time_t period;
130 frsh_rel_time_t budget;
131 frsh_rel_time_t deadline;
134 ret = frsh_contract_init(&contract);
135 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_init");
137 ret = frsh_contract_set_resource_and_label(
139 FRSH_RT_NETWORK, FRSH_NETPF_FWP,
141 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_resource_and_label");
143 frsh_network_bytes_to_budget(FRSH_NETPF_FWP, p->budget, &budget);
144 period = fosa_msec_to_rel_time(p->period_ms);
145 ret = frsh_contract_set_basic_params(&contract,
150 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_basic_params");
153 /* FWP doesn't accept smaller deadlines than 30 ms. */
154 if (frsh_rel_time_smaller(period, frsh_msec_to_rel_time(30)))
155 deadline = frsh_msec_to_rel_time(30);
158 ret = frsh_contract_set_timing_reqs(&contract, false, &deadline);
159 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_timing_reqs");
161 fwp = malloc(sizeof(*fwp));
162 if (!fwp) PERROR_AND_EXIT(errno, "malloc");
163 fwp->src = p->src.s_addr;
164 ret = fres_contract_add_fwp(contract, fwp);
165 if (ret) PERROR_AND_EXIT(ret, "fres_contract_add_fwp");
167 ret = frsh_contract_negotiate(&contract, &p->vres);
169 frsh_contract_destroy(&contract);
174 void create_endpoints(struct stream_params *p,
175 frsh_send_endpoint_t *epsrc,
176 frsh_receive_endpoint_t *epdst)
179 frsh_send_endpoint_protocol_info_t spi = { NULL, 0 };
180 frsh_receive_endpoint_protocol_info_t rpi = { NULL, 0 };
181 frsh_endpoint_queueing_info_t qi = { .queue_size=0,
182 .queue_policy=FRSH_QRP_OLDEST };
184 ret = frsh_receive_endpoint_create(FRSH_NETPF_FWP, 0, qi, rpi,
186 if (ret != 0) error(1, errno, "fwp_receive_endpoint_create");
189 frsh_receive_endpoint_get_params(*epdst, NULL, &port, NULL, NULL);
191 ret = frsh_send_endpoint_create(FRSH_NETPF_FWP,
192 p->dst.s_addr, port, spi,
194 if (ret < 0) error(1, errno, "frsh_send_endpoint_create()");
196 ret = frsh_send_endpoint_bind(p->vres, *epsrc);
197 if (ret != 0) error(1, errno, "frsh_send_endpoint_bind");
201 static struct option long_opts[] = {
202 { "loglevel",required_argument, 0, 'l' },
203 { "period", required_argument, 0, 'p' },
204 { "budget", required_argument, 0, 'b' },
205 { "source", required_argument, 0, 's' },
206 { "dest", required_argument, 0, 'd' },
207 { "async", no_argument, 0, 'a' },
208 { "number", required_argument, 0, 'n' },
209 { "count", required_argument, 0, 'c' },
210 { "verbose",no_argument, 0, 'v' },
211 { "quiet", no_argument, 0, 'q' },
212 { "jitter", required_argument, 0, 'j' },
219 printf("usage: fwp-timing [ options ]\n");
220 printf(" -l, --loglevel <number>|<domain>=<number>,...\n");
221 printf(" -p, --period <ms> period in miliseconds\n");
222 printf(" -b, --budget <bytes> how many bytes is sent in each period\n");
223 printf(" -s, --source <ip> source IP address\n");
224 printf(" -d, --dest <ip:port> destination IP address and port\n");
225 printf(" -a, --async Send packets asynchronously\n");
226 printf(" -n, --number Number of streams with the same parameters\n");
227 printf(" -c, --count Number of messages to send [infinity]\n");
228 printf(" -q, --quiet Print only final statistics\n");
229 printf(" -/, --stream New stream separator\n");
230 printf(" -v, --verbose Be more verbose\n");
231 printf(" -j, --jitter <percent> Sent jitter given as percentage of period\n");
234 int parse_opts(int *argc, char **argv[], struct stream_params *p)
238 bool options_found = false;
240 while ((opt = getopt_long(*argc, *argv, "/ab:c:d:j:l:n:p:qs:v", long_opts, NULL)) != -1) {
241 options_found = true;
247 p->budget = atoi(optarg);
250 p->count = atoi(optarg);
253 ret = inet_aton(optarg, &p->dst);
255 fprintf(stderr, "Destination IP address not recognized: %s\n",
262 p->jitter = atoi(optarg);
265 ul_log_domain_arg2levels(optarg);
268 p->number = atoi(optarg);
271 p->period_ms = atoi(optarg);
274 ret = inet_aton(optarg, &p->src);
276 fprintf(stderr, "Source IP address not recognized: %s\n",
297 return (options_found) ? 0 : -1;
300 volatile bool exit_flag = false;
308 timespec_subtract (result, x, y)
309 struct timespec *result, *x, *y;
311 /* Perform the carry for the later subtraction by updating Y. */
312 if (x->tv_nsec < y->tv_nsec) {
313 int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
314 y->tv_nsec -= 1000000000 * nsec;
317 if (x->tv_nsec - y->tv_nsec > 1000000000) {
318 int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
319 y->tv_nsec += 1000000000 * nsec;
323 /* Compute the time remaining to wait.
324 `tv_nsec' is certainly positive. */
325 result->tv_sec = x->tv_sec - y->tv_sec;
326 result->tv_nsec = x->tv_nsec - y->tv_nsec;
328 /* Return 1 if result is negative. */
329 return x->tv_sec < y->tv_sec;
332 static inline double ts2d(struct timespec *ts)
334 return ts->tv_sec + 1e-9*ts->tv_nsec;
337 static inline double tsdiff2d(struct timespec *x,
341 timespec_subtract(&r, x, y);
345 static inline int tsdiff2us(struct timespec *x,
349 timespec_subtract(&r, x, y);
350 return r.tv_sec*1000000 + r.tv_nsec/1000;
358 struct receiver_params {
360 frsh_receive_endpoint_t epdst;
364 void *receiver(void *arg)
366 struct receiver_params *rp = arg;
367 frsh_receive_endpoint_t epdst = rp->epdst;
371 struct timespec tss, tsr;
373 msg = malloc(rp->budget);
374 if (!msg) error(1, errno, "malloc msg");
377 ret = frsh_receive_sync(epdst, msg, rp->budget, &mlen, NULL);
378 clock_gettime(CLOCK_MONOTONIC, &tsr);
380 if (msg->cnt != last_cnt+1) {
382 printf("%3d: packet(s) lost!\n", rp->id);
383 __sync_fetch_and_add(&stats.lost, msg->cnt - last_cnt+1);
385 hist_add(&hist, tsdiff2us(&tsr, &tss));
386 __sync_fetch_and_add(&stats.received, 1);
389 printf("%3d: %10d: %10.3lf ms\n",
390 rp->id, msg->cnt, tsdiff2d(&tsr, &tss)*1000);
399 void *sender(void *arg)
401 struct stream_params *p = arg;
402 frsh_send_endpoint_t epsrc;
403 struct receiver_params *rp;
407 pthread_t receiver_id;
409 msg = malloc(p->budget);
410 if (!msg) error(1, errno, "malloc msg");
412 rp = malloc(sizeof(*rp));
413 rp->budget = p->budget;
416 create_endpoints(p, &epsrc, &rp->epdst);
420 ret = pthread_create(&receiver_id, NULL, receiver, rp);
422 struct timespec next_period;
424 clock_gettime(CLOCK_MONOTONIC, &next_period);
425 while (!exit_flag && (p->count == -1 || p->count--)) {
426 clock_gettime(CLOCK_MONOTONIC, &tss);
430 ret = frsh_send_async(epsrc, msg, p->budget);
432 ret = frsh_send_sync(epsrc, msg, p->budget);
433 clock_gettime(CLOCK_MONOTONIC, &next_period);
435 __sync_fetch_and_add(&stats.sent, 1);
439 delay_ms = p->period_ms*(100-p->jitter)/100
440 + rand() % (2*p->period_ms*p->jitter/100);
442 delay_ms = p->period_ms;
444 next_period.tv_sec += (delay_ms/1000);
445 next_period.tv_nsec += (delay_ms%1000) * 1000000;
446 if (next_period.tv_nsec >= 1000000000) {
447 next_period.tv_nsec -= 1000000000;
448 next_period.tv_sec++;
450 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
459 void print_stat(bool final)
461 printf("Sent: %5d Received: %5d Lost: %5d Max: %8.3f ms",
462 stats.sent, stats.received, stats.lost, hist.max/1000.0);
464 printf(" Packetloss: %7.3f %% 95%%: %8.3f ms 99%%: %8.3f ms\n",
465 100.0*stats.lost/stats.sent,
466 hist_get_percentile(&hist, 95)/1000.0,
467 hist_get_percentile(&hist, 99)/1000.0);
474 int main(int argc, char *argv[])
479 bool negotiation_failure = false;
481 struct stream_params sp = {
485 .src.s_addr = htonl(INADDR_LOOPBACK),
486 .dst.s_addr = htonl(INADDR_LOOPBACK),
490 struct stream_params *p[100];
492 memset(p, 0, sizeof(p));
494 if (signal(SIGTERM, stopper) == SIG_ERR)
495 error(1, errno, "Error in signal registration");
496 if (signal(SIGINT, stopper) == SIG_ERR)
497 error(1, errno, "Signal handler registration error");
499 sem_init(&finished, 0, 0);
502 if (ret) PERROR_AND_EXIT(ret, "frsh_init");
505 ret = parse_opts(&argc, &argv, &sp);
506 if (num == 0 || ret == 0) {
507 for (i=0; i<sp.number; i++) {
508 p[num] = malloc(sizeof(*p[0]));
509 if (!p[num]) error(1, errno, "malloc");
512 ret = negotiate_contract(p[num]);
516 PERROR_FRESCOR(ret, "frsh_contract_negotiate");
518 negotiation_failure = true;
525 if (negotiation_failure) {
529 for (i=0; i<num; i++)
530 pthread_create(&p[i]->thread, NULL, sender, p[i]);
532 while (!exit_flag && !opt_quiet) {
535 sem_getvalue(&finished, &v);
541 for (i=0; i<num; i++)
542 pthread_join(p[i]->thread, NULL);
544 for (i=0; i<num; i++) {
545 frsh_contract_cancel(p[i]->vres);
549 stats.lost = stats.sent - stats.received;
552 return negotiation_failure ? 1 : 0;