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>
25 int opt_budget = 1024;
27 bool opt_async = false;
29 struct in_addr src, dst;
31 void set_rt_prio(int priority)
34 static struct sched_param param;
36 if ((maxpri = sched_get_priority_max(SCHED_FIFO)) == -1) {
37 fprintf(stderr, "warning: sched_get_priority_max failed");
40 if ((minpri = sched_get_priority_min(SCHED_FIFO)) == -1) {
41 fprintf(stderr, "warning: sched_get_priority_min failed");
44 if (priority > maxpri) {
45 fprintf(stderr, "warning: maximum priority allowed is %d.\n", maxpri);
47 if (priority < minpri) {
48 fprintf(stderr, "warning: minimum priority allowed is %d.\n", minpri);
51 param.sched_priority = priority;
53 if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
54 fprintf(stderr, "warning: sched_setscheduler failed");
57 mlockall(MCL_CURRENT | MCL_FUTURE);
61 int negotiate_contract(frsh_vres_id_t *vres)
63 frsh_contract_t contract;
65 frsh_rel_time_t period;
66 frsh_rel_time_t budget;
67 frsh_rel_time_t deadline;
70 ret = frsh_contract_init(&contract);
71 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_init");
73 ret = frsh_contract_set_resource_and_label(
75 FRSH_RT_NETWORK, FRSH_NETPF_FWP,
77 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_resource_and_label");
79 frsh_network_bytes_to_budget(FRSH_NETPF_FWP, opt_budget, &budget);
80 period = fosa_msec_to_rel_time(opt_period);
81 ret = frsh_contract_set_basic_params(&contract,
86 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_basic_params");
89 /* FWP doesn't accept smaller deadlines than 30 ms. */
90 if (frsh_rel_time_smaller(period, frsh_msec_to_rel_time(30)))
91 deadline = frsh_msec_to_rel_time(30);
94 ret = frsh_contract_set_timing_reqs(&contract, false, &deadline);
95 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_timing_reqs");
97 fwp = malloc(sizeof(*fwp));
98 if (!fwp) PERROR_AND_EXIT(errno, "malloc");
99 fwp->src = src.s_addr;
100 ret = fres_contract_add_fwp(contract, fwp);
101 if (ret) PERROR_AND_EXIT(ret, "fres_contract_add_fwp");
103 ret = frsh_contract_negotiate(&contract, vres);
104 if (ret) PERROR_AND_EXIT(ret, "frsh_contract_negotiate");
106 frsh_contract_destroy(&contract);
111 void create_endpoints(frsh_vres_id_t vres,
112 frsh_send_endpoint_t *epsrc,
113 frsh_receive_endpoint_t *epdst)
116 frsh_send_endpoint_protocol_info_t spi = { NULL, 0 };
117 frsh_receive_endpoint_protocol_info_t rpi = { NULL, 0 };
118 frsh_endpoint_queueing_info_t qi = { .queue_size=0,
119 .queue_policy=FRSH_QRP_OLDEST };
121 ret = frsh_receive_endpoint_create(FRSH_NETPF_FWP, 0, qi, rpi,
123 if (ret != 0) error(1, errno, "fwp_receive_endpoint_create");
126 frsh_receive_endpoint_get_params(*epdst, NULL, &port, NULL, NULL);
128 ret = frsh_send_endpoint_create(FRSH_NETPF_FWP,
129 dst.s_addr, port, spi,
131 if (ret < 0) error(1, errno, "frsh_send_endpoint_create()");
133 ret = frsh_send_endpoint_bind(vres, *epsrc);
134 if (ret != 0) error(1, errno, "frsh_send_endpoint_bind");
138 static struct option long_opts[] = {
139 { "period", 1, 0, 'p' },
140 { "budget", 1, 0, 'b' },
141 { "source", 1, 0, 's' },
142 { "dest", 1, 0, 'd' },
143 { "async", 0, 0, 'a' },
150 printf("usage: fwp-timing [ options ]\n");
151 printf(" -p, --period <ms> period in miliseconds\n");
152 printf(" -b, --budget <bytes> how many bytes is sent in each period\n");
153 printf(" -s, --source <ip> source IP address\n");
154 printf(" -d, --dest <ip:port> destination IP address and port\n");
155 printf(" -a, --async Send packets asynchronously\n");
158 void parse_opts(int argc, char *argv[])
163 while ((opt = getopt_long(argc, argv, "ab:p:s:d:", long_opts, NULL)) != -1) {
169 opt_budget = atoi(optarg);
172 opt_period = atoi(optarg);
175 ret = inet_aton(optarg, &src);
177 fprintf(stderr, "Source IP address not recognized: %s\n",
184 ret = inet_aton(optarg, &dst);
186 fprintf(stderr, "Destination IP address not recognized: %s\n",
199 volatile bool exit_flag = false;
207 timespec_subtract (result, x, y)
208 struct timespec *result, *x, *y;
210 /* Perform the carry for the later subtraction by updating Y. */
211 if (x->tv_nsec < y->tv_nsec) {
212 int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
213 y->tv_nsec -= 1000000000 * nsec;
216 if (x->tv_nsec - y->tv_nsec > 1000000000) {
217 int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
218 y->tv_nsec += 1000000000 * nsec;
222 /* Compute the time remaining to wait.
223 `tv_nsec' is certainly positive. */
224 result->tv_sec = x->tv_sec - y->tv_sec;
225 result->tv_nsec = x->tv_nsec - y->tv_nsec;
227 /* Return 1 if result is negative. */
228 return x->tv_sec < y->tv_sec;
231 static inline double ts2d(struct timespec *ts)
233 return ts->tv_sec + 1e-9*ts->tv_nsec;
236 static inline double tsdiff2d(struct timespec *x,
240 timespec_subtract(&r, x, y);
249 void *receiver(void *arg)
251 frsh_receive_endpoint_t epdst = (frsh_receive_endpoint_t)arg;
255 struct timespec tss, tsr;
257 msg = malloc(opt_budget);
258 if (!msg) error(1, errno, "malloc msg");
261 ret = frsh_receive_sync(epdst, msg, opt_budget, &mlen, NULL);
262 clock_gettime(CLOCK_MONOTONIC, &tsr);
264 if (msg->cnt != last_cnt+1)
265 printf("packet(s) lost!\n");
266 printf("%10d: %10.3lf ms\n",
267 msg->cnt, tsdiff2d(&tsr, &tss)*1000);
276 frsh_send_endpoint_t epsrc;
277 frsh_receive_endpoint_t epdst;
281 pthread_t receiver_id;
283 msg = malloc(opt_budget);
284 if (!msg) error(1, errno, "malloc msg");
287 if (ret) PERROR_AND_EXIT(ret, "frsh_init");
289 negotiate_contract(&vres);
290 create_endpoints(vres, &epsrc, &epdst);
294 ret = pthread_create(&receiver_id, NULL, receiver, epdst);
296 if (signal(SIGTERM, stopper) == SIG_ERR)
297 error(1, errno, "Error in signal registration");
298 if (signal(SIGINT, stopper) == SIG_ERR)
299 error(1, errno, "Signal handler registration error");
301 struct timespec next_period;
303 clock_gettime(CLOCK_MONOTONIC, &next_period);
305 clock_gettime(CLOCK_MONOTONIC, &tss);
309 ret = frsh_send_async(epsrc, msg, opt_budget);
311 ret = frsh_send_sync(epsrc, msg, opt_budget);
312 clock_gettime(CLOCK_MONOTONIC, &next_period);
314 next_period.tv_sec += (opt_period/1000);
315 next_period.tv_nsec += (opt_period%1000) * 1000000;
316 if (next_period.tv_nsec >= 1000000000) {
317 next_period.tv_nsec -= 1000000000;
318 next_period.tv_sec++;
320 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
324 frsh_contract_cancel(vres);
327 int main(int argc, char *argv[])
329 src.s_addr = htonl(INADDR_LOOPBACK);
330 dst.s_addr = htonl(INADDR_LOOPBACK);
332 parse_opts(argc, argv);