]> rtime.felk.cvut.cz Git - frescor/fwp.git/blob - fwp/tests/timing/fwp-timing.c
Implemented synchronous and asynchronous sending
[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 bool opt_async = false;
28
29 struct in_addr src, dst;
30
31 void set_rt_prio(int priority)
32 {
33         int maxpri, minpri;
34         static struct sched_param param;
35
36         if ((maxpri = sched_get_priority_max(SCHED_FIFO)) == -1)        {
37                 fprintf(stderr, "warning: sched_get_priority_max failed");
38         }
39
40         if ((minpri = sched_get_priority_min(SCHED_FIFO)) == -1)        {
41                 fprintf(stderr, "warning: sched_get_priority_min failed");
42         }
43
44         if (priority > maxpri)  {
45                 fprintf(stderr, "warning: maximum priority allowed is %d.\n", maxpri);
46         }
47         if (priority < minpri)  {
48                 fprintf(stderr, "warning: minimum priority allowed is %d.\n", minpri);
49         }
50
51         param.sched_priority = priority;
52
53         if (sched_setscheduler(0, SCHED_FIFO, &param) == -1)    {
54                 fprintf(stderr, "warning: sched_setscheduler failed");
55         }
56
57         mlockall(MCL_CURRENT | MCL_FUTURE);
58 }
59
60
61 int negotiate_contract(frsh_vres_id_t *vres)
62 {
63         frsh_contract_t contract;
64         int ret;
65         frsh_rel_time_t period;
66         frsh_rel_time_t budget;
67         frsh_rel_time_t deadline;
68         fres_block_fwp *fwp;    
69         
70         ret = frsh_contract_init(&contract);
71         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_init");
72
73         ret = frsh_contract_set_resource_and_label(
74                 &contract,
75                 FRSH_RT_NETWORK, FRSH_NETPF_FWP,
76                 NULL);
77         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_resource_and_label");
78
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,
82                                              &budget,
83                                              &period,
84                                              FRSH_WT_BOUNDED,
85                                              FRSH_CT_REGULAR);
86         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_basic_params");
87
88
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);
92         else
93                 deadline = period;
94         ret = frsh_contract_set_timing_reqs(&contract, false, &deadline);
95         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_timing_reqs");
96
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");
102
103         ret = frsh_contract_negotiate(&contract, vres);
104         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_negotiate");
105
106         frsh_contract_destroy(&contract);
107
108         return 0;
109 }
110
111 void create_endpoints(frsh_vres_id_t vres,
112                       frsh_send_endpoint_t *epsrc,
113                       frsh_receive_endpoint_t *epdst)
114 {
115         int ret;
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 };
120
121         ret = frsh_receive_endpoint_create(FRSH_NETPF_FWP, 0, qi, rpi,
122                                            epdst);
123         if (ret != 0) error(1, errno, "fwp_receive_endpoint_create");
124                 
125         unsigned int port;
126         frsh_receive_endpoint_get_params(*epdst, NULL, &port, NULL, NULL);
127
128         ret = frsh_send_endpoint_create(FRSH_NETPF_FWP,
129                                         dst.s_addr, port, spi,
130                                         epsrc);
131         if (ret < 0) error(1, errno, "frsh_send_endpoint_create()");
132                 
133         ret = frsh_send_endpoint_bind(vres, *epsrc);
134         if (ret != 0) error(1, errno, "frsh_send_endpoint_bind");
135 }
136
137
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' },
144     { 0, 0, 0, 0}
145 };
146
147 static void
148 usage(void)
149 {
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");
156 }
157
158 void parse_opts(int argc, char *argv[])
159 {
160         char opt;
161         int ret;
162
163         while ((opt = getopt_long(argc, argv, "ab:p:s:d:", long_opts, NULL)) != -1) {
164                 switch (opt) {
165                 case 'a':
166                         opt_async = true;
167                         break;
168                 case 'b':
169                         opt_budget = atoi(optarg);
170                         break;
171                 case 'p':
172                         opt_period = atoi(optarg);
173                         break;
174                 case 's':
175                         ret = inet_aton(optarg, &src);
176                         if (!ret) {
177                                 fprintf(stderr, "Source IP address not recognized: %s\n",
178                                         optarg);
179                                 usage();
180                                 exit(1);
181                         }
182                         break;
183                 case 'd':
184                         ret = inet_aton(optarg, &dst);
185                         if (!ret) {
186                                 fprintf(stderr, "Destination IP address not recognized: %s\n",
187                                         optarg);
188                                 usage();
189                                 exit(1);
190                         }
191                         break;
192                 default:
193                         usage();
194                         exit(1);
195                 }
196         }
197 }
198
199 volatile bool exit_flag = false;
200
201 void stopper()
202 {
203         exit_flag = true;
204 }
205
206 int
207 timespec_subtract (result, x, y)
208      struct timespec *result, *x, *y;
209 {
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;
214     y->tv_sec += nsec;
215   }
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;
219     y->tv_sec -= nsec;
220   }
221
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;
226
227   /* Return 1 if result is negative. */
228   return x->tv_sec < y->tv_sec;
229 }
230
231 static inline double ts2d(struct timespec *ts)
232 {
233         return ts->tv_sec + 1e-9*ts->tv_nsec;
234 }
235
236 static inline double tsdiff2d(struct timespec *x,
237                               struct timespec *y)
238 {
239         struct timespec r;
240         timespec_subtract(&r, x, y);
241         return ts2d(&r);
242 }
243
244 struct msg {
245         int cnt;
246         struct timespec ts;
247 };
248
249 void *receiver(void *arg)
250 {
251         frsh_receive_endpoint_t epdst = (frsh_receive_endpoint_t)arg;
252         size_t mlen;
253         int ret;
254         int last_cnt = -1;
255         struct timespec tss, tsr;
256         struct msg *msg;
257         msg = malloc(opt_budget);
258         if (!msg) error(1, errno, "malloc msg");
259
260         while (!exit_flag) {
261                 ret = frsh_receive_sync(epdst, msg, opt_budget, &mlen, NULL);
262                 clock_gettime(CLOCK_MONOTONIC, &tsr);
263                 tss = msg->ts;
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);
268                 last_cnt = msg->cnt;
269         }
270         return NULL;
271 }
272
273 void run()
274 {
275         frsh_vres_id_t vres;
276         frsh_send_endpoint_t epsrc;
277         frsh_receive_endpoint_t epdst;
278         int ret;
279         struct msg *msg;
280         long int cnt=0;
281         pthread_t receiver_id;
282
283         msg = malloc(opt_budget);
284         if (!msg) error(1, errno, "malloc msg");
285
286         ret = frsh_init();
287         if (ret) PERROR_AND_EXIT(ret, "frsh_init");
288         
289         negotiate_contract(&vres);
290         create_endpoints(vres, &epsrc, &epdst);
291
292         set_rt_prio(50);
293
294         ret = pthread_create(&receiver_id, NULL, receiver, epdst);
295         
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");
300
301         struct timespec next_period;
302         struct timespec tss;
303         clock_gettime(CLOCK_MONOTONIC, &next_period);
304         while (!exit_flag) {
305                 clock_gettime(CLOCK_MONOTONIC, &tss);
306                 msg->cnt = cnt++;
307                 msg->ts = tss;
308                 if (opt_async)
309                         ret = frsh_send_async(epsrc, msg, opt_budget);
310                 else {
311                         ret = frsh_send_sync(epsrc, msg, opt_budget);
312                         clock_gettime(CLOCK_MONOTONIC, &next_period);
313                 }
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++;
319                 }
320                 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
321                                 &next_period, NULL);
322         }
323         
324         frsh_contract_cancel(vres);
325 }
326
327 int main(int argc, char *argv[])
328 {
329         src.s_addr = htonl(INADDR_LOOPBACK);
330         dst.s_addr = htonl(INADDR_LOOPBACK);
331         
332         parse_opts(argc, argv);
333         run();
334         return 0;
335 }