]> rtime.felk.cvut.cz Git - frescor/fwp.git/blob - fwp/tests/timing/fwp-timing.c
Added application to measure FWP timing properites
[frescor/fwp.git] / fwp / tests / timing / fwp-timing.c
1 /*
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.
5  *
6  * This program creates both sides of communication and measures the
7  * communication delays.
8  */
9
10 #include <netinet/in.h>
11 #include <arpa/inet.h>
12 #include <frsh.h>
13 #include <getopt.h>
14 #include <netdb.h>
15 #include <netinet/in.h>
16 #include <stdlib.h>
17 #include <sys/socket.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 #include <sys/param.h>
21 #include <fwp_res.h>
22 #include <error.h>
23 #include <sys/mman.h>
24
25 int opt_budget = 1024;
26 int opt_period = 20;
27
28 struct in_addr src, dst;
29
30 void set_rt_prio(int priority)
31 {
32         int maxpri, minpri;
33         static struct sched_param param;
34
35         if ((maxpri = sched_get_priority_max(SCHED_FIFO)) == -1)        {
36                 fprintf(stderr, "warning: sched_get_priority_max failed");
37         }
38
39         if ((minpri = sched_get_priority_min(SCHED_FIFO)) == -1)        {
40                 fprintf(stderr, "warning: sched_get_priority_min failed");
41         }
42
43         if (priority > maxpri)  {
44                 fprintf(stderr, "warning: maximum priority allowed is %d.\n", maxpri);
45         }
46         if (priority < minpri)  {
47                 fprintf(stderr, "warning: minimum priority allowed is %d.\n", minpri);
48         }
49
50         param.sched_priority = priority;
51
52         if (sched_setscheduler(0, SCHED_FIFO, &param) == -1)    {
53                 fprintf(stderr, "warning: sched_setscheduler failed");
54         }
55
56         mlockall(MCL_CURRENT | MCL_FUTURE);
57 }
58
59
60 int negotiate_contract(frsh_vres_id_t *vres)
61 {
62         frsh_contract_t contract;
63         int ret;
64         frsh_rel_time_t period;
65         frsh_rel_time_t budget;
66         frsh_rel_time_t deadline;
67         fres_block_fwp *fwp;    
68         
69         ret = frsh_contract_init(&contract);
70         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_init");
71
72         ret = frsh_contract_set_resource_and_label(
73                 &contract,
74                 FRSH_RT_NETWORK, FRSH_NETPF_FWP,
75                 NULL);
76         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_resource_and_label");
77
78         frsh_network_bytes_to_budget(FRSH_NETPF_FWP, opt_budget, &budget);
79         period = fosa_msec_to_rel_time(opt_period);
80         ret = frsh_contract_set_basic_params(&contract,
81                                              &budget,
82                                              &period,
83                                              FRSH_WT_BOUNDED,
84                                              FRSH_CT_REGULAR);
85         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_basic_params");
86
87
88         /* FWP doesn't accept smaller deadlines than 30 ms. */
89         if (frsh_rel_time_smaller(period, frsh_msec_to_rel_time(30)))
90                 deadline = frsh_msec_to_rel_time(30);
91         else
92                 deadline = period;
93         ret = frsh_contract_set_timing_reqs(&contract, false, &deadline);
94         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_timing_reqs");
95
96         fwp = malloc(sizeof(*fwp));
97         if (!fwp) PERROR_AND_EXIT(errno, "malloc");
98         fwp->src = src.s_addr;
99         ret = fres_contract_add_fwp(contract, fwp);
100         if (ret) PERROR_AND_EXIT(ret, "fres_contract_add_fwp");
101
102         ret = frsh_contract_negotiate(&contract, vres);
103         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_negotiate");
104
105         frsh_contract_destroy(&contract);
106
107         return 0;
108 }
109
110 void create_endpoints(frsh_vres_id_t vres,
111                       frsh_send_endpoint_t *epsrc,
112                       frsh_receive_endpoint_t *epdst)
113 {
114         int ret;
115         frsh_send_endpoint_protocol_info_t    spi = { NULL, 0 };
116         frsh_receive_endpoint_protocol_info_t rpi = { NULL, 0 };
117         frsh_endpoint_queueing_info_t qi = { .queue_size=0,
118                                              .queue_policy=FRSH_QRP_OLDEST };
119
120         ret = frsh_receive_endpoint_create(FRSH_NETPF_FWP, 0, qi, rpi,
121                                            epdst);
122         if (ret != 0) error(1, errno, "fwp_receive_endpoint_create");
123                 
124         unsigned int port;
125         frsh_receive_endpoint_get_params(*epdst, NULL, &port, NULL, NULL);
126
127         ret = frsh_send_endpoint_create(FRSH_NETPF_FWP,
128                                         dst.s_addr, port, spi,
129                                         epsrc);
130         if (ret < 0) error(1, errno, "frsh_send_endpoint_create()");
131                 
132         ret = frsh_send_endpoint_bind(vres, *epsrc);
133         if (ret != 0) error(1, errno, "frsh_send_endpoint_bind");
134 }
135
136
137 static struct option long_opts[] = {
138     { "period", 1, 0, 'p' },
139     { "budget", 1, 0, 'b' },
140     { "source", 1, 0, 's' },
141     { "dest",   1, 0, 'd' },
142     { 0, 0, 0, 0}
143 };
144
145 static void
146 usage(void)
147 {
148         printf("usage: fwp-timing [ options ]\n");
149         printf("  -p, --period <ms>  period in miliseconds\n");
150         printf("  -b, --budget <bytes>  how many bytes is sent in each period\n");
151         printf("  -s, --source <ip>  source IP address\n");
152         printf("  -d, --dest <ip:port> destination IP address and port\n");
153 }
154
155 void parse_opts(int argc, char *argv[])
156 {
157         char opt;
158         int ret;
159
160         while ((opt = getopt_long(argc, argv, "b:p:s:d:", long_opts, NULL)) != -1) {
161                 switch (opt) {
162                 case 'b':
163                         opt_budget = atoi(optarg);
164                         break;
165                 case 'p':
166                         opt_period = atoi(optarg);
167                         break;
168                 case 's':
169                         ret = inet_aton(optarg, &src);
170                         if (!ret) {
171                                 fprintf(stderr, "Source IP address not recognized: %s\n",
172                                         optarg);
173                                 usage();
174                                 exit(1);
175                         }
176                         break;
177                 case 'd':
178                         ret = inet_aton(optarg, &dst);
179                         if (!ret) {
180                                 fprintf(stderr, "Destination IP address not recognized: %s\n",
181                                         optarg);
182                                 usage();
183                                 exit(1);
184                         }
185                         break;
186                 default:
187                         usage();
188                         exit(1);
189                 }
190         }
191 }
192
193 volatile bool exit_flag = false;
194
195 void stopper()
196 {
197         exit_flag = true;
198 }
199
200 int
201 timespec_subtract (result, x, y)
202      struct timespec *result, *x, *y;
203 {
204   /* Perform the carry for the later subtraction by updating Y. */
205   if (x->tv_nsec < y->tv_nsec) {
206     int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
207     y->tv_nsec -= 1000000000 * nsec;
208     y->tv_sec += nsec;
209   }
210   if (x->tv_nsec - y->tv_nsec > 1000000000) {
211     int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
212     y->tv_nsec += 1000000000 * nsec;
213     y->tv_sec -= nsec;
214   }
215
216   /* Compute the time remaining to wait.
217      `tv_nsec' is certainly positive. */
218   result->tv_sec = x->tv_sec - y->tv_sec;
219   result->tv_nsec = x->tv_nsec - y->tv_nsec;
220
221   /* Return 1 if result is negative. */
222   return x->tv_sec < y->tv_sec;
223 }
224
225 static inline double ts2d(struct timespec *ts)
226 {
227         return ts->tv_sec + 1e-9*ts->tv_nsec;
228 }
229
230 static inline double tsdiff2d(struct timespec *x,
231                               struct timespec *y)
232 {
233         struct timespec r;
234         timespec_subtract(&r, x, y);
235         return ts2d(&r);
236 }
237
238 struct msg {
239         int cnt;
240         struct timespec ts;
241 };
242
243 void *receiver(void *arg)
244 {
245         frsh_receive_endpoint_t epdst = (frsh_receive_endpoint_t)arg;
246         size_t mlen;
247         int ret;
248         struct timespec tss, tsr;
249         struct msg *msg;
250         msg = malloc(opt_budget);
251         if (!msg) error(1, errno, "malloc msg");
252
253         while (!exit_flag) {
254                 ret = frsh_receive_sync(epdst, msg, opt_budget, &mlen, NULL);
255                 clock_gettime(CLOCK_MONOTONIC, &tsr);
256                 tss = msg->ts;
257                 printf("%10d: %10.3lf ms\n",
258                        msg->cnt, tsdiff2d(&tsr, &tss)*1000);
259         }
260         return NULL;
261 }
262
263 void run()
264 {
265         frsh_vres_id_t vres;
266         frsh_send_endpoint_t epsrc;
267         frsh_receive_endpoint_t epdst;
268         int ret;
269         struct msg *msg;
270         long int cnt=0;
271         pthread_t receiver_id;
272
273         msg = malloc(opt_budget);
274         if (!msg) error(1, errno, "malloc msg");
275
276         ret = frsh_init();
277         if (ret) PERROR_AND_EXIT(ret, "frsh_init");
278         
279         negotiate_contract(&vres);
280         create_endpoints(vres, &epsrc, &epdst);
281
282         set_rt_prio(50);
283
284         ret = pthread_create(&receiver_id, NULL, receiver, epdst);
285         
286         if (signal(SIGTERM, stopper) == SIG_ERR)
287                 error(1, errno, "Error in signal registration");
288         if (signal(SIGINT, stopper) == SIG_ERR)
289                 error(1, errno, "Signal handler registration error");
290
291         struct timespec next_period, tss;
292         clock_gettime(CLOCK_MONOTONIC, &next_period);
293         while (!exit_flag) {
294                 clock_gettime(CLOCK_MONOTONIC, &tss);
295                 msg->cnt = cnt++;
296                 msg->ts = tss;
297                 ret = frsh_send_async(epsrc, msg, opt_budget);
298
299                 next_period.tv_nsec += opt_period * 1000000;
300                 if (next_period.tv_nsec >= 1000000000) {
301                         next_period.tv_nsec -= 1000000000;
302                         next_period.tv_sec++;
303                 }
304
305                 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
306                                 &next_period, NULL);
307         }
308         
309         frsh_contract_cancel(vres);
310 }
311
312 int main(int argc, char *argv[])
313 {
314         src.s_addr = htonl(INADDR_LOOPBACK);
315         dst.s_addr = htonl(INADDR_LOOPBACK);
316         
317         parse_opts(argc, argv);
318         run();
319         return 0;
320 }