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>
26 bool opt_verbose = false;
32 void set_rt_prio(int priority)
35 static struct sched_param param;
37 if ((maxpri = sched_get_priority_max(SCHED_FIFO)) == -1) {
38 fprintf(stderr, "warning: sched_get_priority_max failed");
41 if ((minpri = sched_get_priority_min(SCHED_FIFO)) == -1) {
42 fprintf(stderr, "warning: sched_get_priority_min failed");
45 if (priority > maxpri) {
46 fprintf(stderr, "warning: maximum priority allowed is %d.\n", maxpri);
48 if (priority < minpri) {
49 fprintf(stderr, "warning: minimum priority allowed is %d.\n", minpri);
52 param.sched_priority = priority;
54 if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
55 fprintf(stderr, "warning: sched_setscheduler failed");
58 mlockall(MCL_CURRENT | MCL_FUTURE);
61 struct stream_params {
65 struct in_addr src, dst;
72 int negotiate_contract(struct stream_params *p, frsh_vres_id_t *vres)
74 frsh_contract_t contract;
76 frsh_rel_time_t period;
77 frsh_rel_time_t budget;
78 frsh_rel_time_t deadline;
81 ret = frsh_contract_init(&contract);
82 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_init");
84 ret = frsh_contract_set_resource_and_label(
86 FRSH_RT_NETWORK, FRSH_NETPF_FWP,
88 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_resource_and_label");
90 frsh_network_bytes_to_budget(FRSH_NETPF_FWP, p->budget, &budget);
91 period = fosa_msec_to_rel_time(p->period_ms);
92 ret = frsh_contract_set_basic_params(&contract,
97 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_basic_params");
100 /* FWP doesn't accept smaller deadlines than 30 ms. */
101 if (frsh_rel_time_smaller(period, frsh_msec_to_rel_time(30)))
102 deadline = frsh_msec_to_rel_time(30);
105 ret = frsh_contract_set_timing_reqs(&contract, false, &deadline);
106 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_timing_reqs");
108 fwp = malloc(sizeof(*fwp));
109 if (!fwp) PERROR_AND_EXIT(errno, "malloc");
110 fwp->src = p->src.s_addr;
111 ret = fres_contract_add_fwp(contract, fwp);
112 if (ret) PERROR_AND_EXIT(ret, "fres_contract_add_fwp");
114 ret = frsh_contract_negotiate(&contract, vres);
115 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_negotiate");
117 frsh_contract_destroy(&contract);
122 void create_endpoints(struct stream_params *p,
124 frsh_send_endpoint_t *epsrc,
125 frsh_receive_endpoint_t *epdst)
128 frsh_send_endpoint_protocol_info_t spi = { NULL, 0 };
129 frsh_receive_endpoint_protocol_info_t rpi = { NULL, 0 };
130 frsh_endpoint_queueing_info_t qi = { .queue_size=0,
131 .queue_policy=FRSH_QRP_OLDEST };
133 ret = frsh_receive_endpoint_create(FRSH_NETPF_FWP, 0, qi, rpi,
135 if (ret != 0) error(1, errno, "fwp_receive_endpoint_create");
138 frsh_receive_endpoint_get_params(*epdst, NULL, &port, NULL, NULL);
140 ret = frsh_send_endpoint_create(FRSH_NETPF_FWP,
141 p->dst.s_addr, port, spi,
143 if (ret < 0) error(1, errno, "frsh_send_endpoint_create()");
145 ret = frsh_send_endpoint_bind(vres, *epsrc);
146 if (ret != 0) error(1, errno, "frsh_send_endpoint_bind");
150 static struct option long_opts[] = {
151 { "period", 1, 0, 'p' },
152 { "budget", 1, 0, 'b' },
153 { "source", 1, 0, 's' },
154 { "dest", 1, 0, 'd' },
155 { "async", 0, 0, 'a' },
156 { "number", 0, 0, 'n' },
157 { "count", 0, 0, 'c' },
158 { "verbose",0, 0, 'v' },
165 printf("usage: fwp-timing [ options ]\n");
166 printf(" -p, --period <ms> period in miliseconds\n");
167 printf(" -b, --budget <bytes> how many bytes is sent in each period\n");
168 printf(" -s, --source <ip> source IP address\n");
169 printf(" -d, --dest <ip:port> destination IP address and port\n");
170 printf(" -a, --async Send packets asynchronously\n");
171 printf(" -n, --number Number of streams with the same parameters\n");
172 printf(" -c, --count Number of messages to send [infinity]\n");
173 printf(" -/, --stream New stream separator\n");
174 printf(" -v, --verbose Be more verbose\n");
177 int parse_opts(int *argc, char **argv[], struct stream_params *p)
181 bool options_found = false;
183 while ((opt = getopt_long(*argc, *argv, "/ab:c:p:s:d:n:v", long_opts, NULL)) != -1) {
184 options_found = true;
190 p->budget = atoi(optarg);
193 p->count = atoi(optarg);
196 ret = inet_aton(optarg, &p->dst);
198 fprintf(stderr, "Destination IP address not recognized: %s\n",
205 p->number = atoi(optarg);
208 p->period_ms = atoi(optarg);
211 ret = inet_aton(optarg, &p->src);
213 fprintf(stderr, "Source IP address not recognized: %s\n",
230 return (options_found) ? 0 : -1;
233 volatile bool exit_flag = false;
241 timespec_subtract (result, x, y)
242 struct timespec *result, *x, *y;
244 /* Perform the carry for the later subtraction by updating Y. */
245 if (x->tv_nsec < y->tv_nsec) {
246 int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
247 y->tv_nsec -= 1000000000 * nsec;
250 if (x->tv_nsec - y->tv_nsec > 1000000000) {
251 int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
252 y->tv_nsec += 1000000000 * nsec;
256 /* Compute the time remaining to wait.
257 `tv_nsec' is certainly positive. */
258 result->tv_sec = x->tv_sec - y->tv_sec;
259 result->tv_nsec = x->tv_nsec - y->tv_nsec;
261 /* Return 1 if result is negative. */
262 return x->tv_sec < y->tv_sec;
265 static inline double ts2d(struct timespec *ts)
267 return ts->tv_sec + 1e-9*ts->tv_nsec;
270 static inline double tsdiff2d(struct timespec *x,
274 timespec_subtract(&r, x, y);
283 struct receiver_params {
285 frsh_receive_endpoint_t epdst;
289 void *receiver(void *arg)
291 struct receiver_params *rp = arg;
292 frsh_receive_endpoint_t epdst = rp->epdst;
296 struct timespec tss, tsr;
298 msg = malloc(rp->budget);
299 if (!msg) error(1, errno, "malloc msg");
302 ret = frsh_receive_sync(epdst, msg, rp->budget, &mlen, NULL);
303 clock_gettime(CLOCK_MONOTONIC, &tsr);
305 if (msg->cnt != last_cnt+1)
306 printf("%3d: packet(s) lost!\n", rp->id);
308 printf("%3d: %10d: %10.3lf ms\n",
309 rp->id, msg->cnt, tsdiff2d(&tsr, &tss)*1000);
318 void *sender(void *arg)
320 struct stream_params *p = arg;
322 frsh_send_endpoint_t epsrc;
323 struct receiver_params *rp;
327 pthread_t receiver_id;
329 msg = malloc(p->budget);
330 if (!msg) error(1, errno, "malloc msg");
332 negotiate_contract(p, &vres);
334 rp = malloc(sizeof(*rp));
335 rp->budget = p->budget;
338 create_endpoints(p, vres, &epsrc, &rp->epdst);
342 ret = pthread_create(&receiver_id, NULL, receiver, rp);
344 if (signal(SIGTERM, stopper) == SIG_ERR)
345 error(1, errno, "Error in signal registration");
346 if (signal(SIGINT, stopper) == SIG_ERR)
347 error(1, errno, "Signal handler registration error");
349 struct timespec next_period;
351 clock_gettime(CLOCK_MONOTONIC, &next_period);
352 while (!exit_flag && (p->count == -1 || p->count--)) {
353 clock_gettime(CLOCK_MONOTONIC, &tss);
357 ret = frsh_send_async(epsrc, msg, p->budget);
359 ret = frsh_send_sync(epsrc, msg, p->budget);
360 clock_gettime(CLOCK_MONOTONIC, &next_period);
362 next_period.tv_sec += (p->period_ms/1000);
363 next_period.tv_nsec += (p->period_ms%1000) * 1000000;
364 if (next_period.tv_nsec >= 1000000000) {
365 next_period.tv_nsec -= 1000000000;
366 next_period.tv_sec++;
368 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
372 frsh_contract_cancel(vres);
378 int main(int argc, char *argv[])
382 struct stream_params sp = {
386 .src.s_addr = htonl(INADDR_LOOPBACK),
387 .dst.s_addr = htonl(INADDR_LOOPBACK),
391 struct stream_params *p = NULL;
393 sem_init(&finished, 0, 0);
396 if (ret) PERROR_AND_EXIT(ret, "frsh_init");
399 ret = parse_opts(&argc, &argv, &sp);
400 if (num == 0 || ret == 0) {
402 for (i=0; i<sp.number; i++) {
404 p = malloc(sizeof(*p));
405 if (!p) error(1, errno, "malloc");
408 pthread_create(&thread, NULL, sender, p);