]> rtime.felk.cvut.cz Git - can-eth-gw.git/blob - utils/cegwbench/cegwbench.c
netdevice_notifier implemented; test script; cegwbench rxtimeo
[can-eth-gw.git] / utils / cegwbench / cegwbench.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <getopt.h>
6 #include <time.h>
7 #include <math.h>
8 #include <sys/socket.h>
9 #include <netinet/in.h>
10 #include <net/if.h>
11 #include <arpa/inet.h>
12 #include <linux/can.h>
13 #include <pthread.h>
14 #include "cegwerr.h"
15 #include "readif.h"
16
17 //#define BENCH_DEBUG
18 #ifdef BENCH_DEBUG
19 #define printdbg(...) printf( __VA_ARGS__ )
20 #else
21 #define printdbg(...)
22 #endif
23
24 #define BENCH_FLAG_N 1
25 #define BENCH_FLAG_SRC 2
26 #define BENCH_FLAG_DST 4
27 #define BENCH_FLAG_TIMEO 8
28
29 enum {
30         BENCH_MODE_UNDEF,
31         BENCH_MODE_ASAP,
32         BENCH_MODE_ONEATTIME,
33         BENCH_MODE_PERIOD
34 };
35
36 int can_sock_create( int ifindex );
37 int udp_sock_create( struct in_addr ip, unsigned int port );
38
39 struct optdata
40 {
41         int optflag;
42         int mode;
43         int n;
44         struct timeval timeo;
45         struct cegw_if ceif[2]; /* 0 -> src, 1 -> dst */
46 };
47
48 struct optdata d;
49 pthread_barrier_t barrier;
50 struct timespec* tx_time, * rx_time;
51
52 void* thr_recv( void* arg )
53 {
54         int sock;
55         struct can_frame cf;
56         int i;
57         int seq;
58         int ret;
59
60         if( d.ceif[1].type == IF_CAN )
61                 sock = can_sock_create( d.ceif[1].can.ifindex );
62         else
63                 sock = udp_sock_create( d.ceif[1].eth.ip, d.ceif[1].eth.port );
64         if( sock == -1 )
65         {
66                 /* ToDo: handle */
67                 return (void*)1;
68         }
69
70         ret = setsockopt( sock, SOL_SOCKET, SO_RCVTIMEO, &d.timeo, sizeof(d.timeo) );
71         if( ret != 0 )
72                 return (void*)1;
73
74         pthread_barrier_wait( &barrier );
75
76         /* recv */
77         for( i=0; i<d.n; i++ )
78         {
79                 ret = recvfrom( sock, &cf, sizeof(cf), 0, NULL, 0 );
80                 /* ToDo: rework */
81                 if( ret == -1 )
82                 {
83                         puts( "cegwbench: recv timed out\n" );
84                         return (void*)1;
85                 }
86                 seq = *((int*)cf.data);
87                 clock_gettime( CLOCK_REALTIME, &rx_time[ seq ] );
88                 printdbg( "recv: (id=%d)%d\n", cf.can_id, seq );
89                 if( d.mode == BENCH_MODE_ONEATTIME )
90                 {
91                         pthread_barrier_wait( &barrier );
92                 }
93         }
94
95         close( sock );
96         return (void*)0;
97 }
98
99 int timespec_subtract( struct timespec *result, struct timespec *x, struct timespec *yy )
100 {
101         struct timespec ylocal = *yy, *y = &ylocal;
102 /* Perform the carry for the later subtraction by updating Y. */
103         if( x->tv_nsec < y->tv_nsec )
104         {
105                 int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
106                 y->tv_nsec -= 1000000000 * nsec;
107                 y->tv_sec += nsec;
108         }
109         if (x->tv_nsec - y->tv_nsec > 1000000000)
110         {
111                 int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
112                 y->tv_nsec += 1000000000 * nsec;
113                 y->tv_sec -= nsec;
114         }
115
116         /* Compute the time remaining to wait.
117          *         `tv_nsec' is certainly positive. */
118         result->tv_sec = x->tv_sec - y->tv_sec;
119         result->tv_nsec = x->tv_nsec - y->tv_nsec;
120         /* Return 1 if result is negative. */
121         return x->tv_sec < y->tv_sec;
122 }
123
124 int udp_sock_create( struct in_addr ip, unsigned int port  )
125 {
126         struct sockaddr_in addr_udp;
127         int udp_sock;
128
129         addr_udp.sin_family = AF_INET;
130         addr_udp.sin_port = htons( port );
131         addr_udp.sin_addr.s_addr = INADDR_ANY;
132
133         udp_sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
134         if( udp_sock < 0 )
135         {
136                 fprintf( stderr, "error: udp socket creation failed\n" );
137                 return -1;
138         }
139
140         if( bind(udp_sock, (struct sockaddr*)&addr_udp, sizeof(addr_udp)) != 0 )
141         {
142                 fprintf( stderr, "error: udp socket binding failed\n" );
143                 return -1;
144         }
145
146         return udp_sock;
147 }
148
149 /**
150  * can_sock_create
151  * @return can socket fd, or -1 on failure
152  */
153 int can_sock_create( int ifindex )
154 {
155         int can_sock;
156         struct sockaddr_can addr_can;
157
158         addr_can.can_family = AF_CAN;
159         addr_can.can_ifindex = ifindex;
160         
161         can_sock = socket( PF_CAN, SOCK_RAW, CAN_RAW );
162         if( can_sock < 0 )
163         {
164                 fprintf( stderr, "error: can socket creation failed\n" );
165                 return -1;
166         }
167
168         if( bind(can_sock, (struct sockaddr*)&addr_can, sizeof(addr_can)) != 0 )
169         {
170                 fprintf( stderr, "error: can socket binding failed\n" );
171                 return -1;
172         }
173
174         return can_sock;
175 }
176
177 void dump_arg( int argc, char* argv[] )
178 {
179         int i;
180
181         putchar( '#' );
182         for( i=0; i<argc; i++ )
183         {
184                 printf( "%s ", argv[i] );
185         }
186         putchar( '\n' );        
187 }
188
189 int read_mode( char* in )
190 {
191         if( strcmp( in, "asap" ) == 0 )
192                 return BENCH_MODE_ASAP;
193         if( strcmp( in, "oneattime") == 0 )
194                 return BENCH_MODE_ONEATTIME;
195         if( strcmp( in, "period" ) == 0 )
196                 return BENCH_MODE_PERIOD;
197
198         return BENCH_MODE_UNDEF;
199 }
200
201 int main( int argc, char* argv[] )
202 {
203         char opt;
204         struct can_frame cf;
205         int sock;
206         pthread_t thr;
207         float time;
208         void* thr_ret;
209         int ret;
210         struct timespec res;
211         int i, tmp;
212
213         d.optflag = 0;
214         d.mode = BENCH_MODE_ASAP;
215         d.timeo.tv_sec = 0;
216         d.timeo.tv_usec = 0;
217
218         struct option longopt[] =
219         {
220                 { "mode", 0, NULL, 'm' },
221                 { 0, 0, 0, 0 }
222         };
223
224         while( 1 )
225         {
226                 opt = getopt( argc, argv, "s:d:m:n:t:" );
227                 if( opt == -1 )
228                         break;
229                 switch( opt )
230                 {
231                         case 's':
232                                 d.optflag |= BENCH_FLAG_SRC;
233                                 if( read_if( optarg, &d.ceif[0] ) != 0 )
234                                 {
235                                         perr( "'-s'" );
236                                         return -1;
237                                 }
238                                 break;
239                         case 'd':
240                                 d.optflag |= BENCH_FLAG_DST;
241                                 if( read_if( optarg, &d.ceif[1] ) )
242                                 {
243                                         perr( "-d" );
244                                         return -1;
245                                 }
246                                 break;
247                         case 'm':
248                                 tmp = read_mode( optarg );
249                                 if( tmp == BENCH_MODE_UNDEF )
250                                 {
251                                         perr( "benchmarking mode unknown" );
252                                         return -1;
253                                 }
254                                 d.mode = tmp;
255                                 break;
256                         case 'n':
257                                 d.optflag |= BENCH_FLAG_N;
258                                 d.n = atoi( optarg );
259                                 break;
260                         case 't':
261                                 d.optflag |= BENCH_FLAG_TIMEO;
262                                 if( sscanf( optarg, "%ld", &d.timeo.tv_sec ) != 1 )
263                                 {
264                                         perr( "timeout mismatch" );
265                                         return -1;
266                                 }
267                                 d.timeo.tv_usec = 0;
268                                 break;
269                         case '?':
270                                 return -1;
271                                 break;
272                 }
273         }
274
275         /* check opt */
276         /* '-n' option is mandatory and its value is >0 */
277         if( d.optflag & BENCH_FLAG_N )
278         {
279                 if( d.n < 1 )
280                 {
281                         perr( "'-n' should be at least 1." );
282                 }
283         } else 
284         {
285                 perr( "'-n' is mandatory." );
286                 return -1;
287         }
288
289         /* '-s' and '-d' should be defined and different iftype */
290         if( (d.optflag & BENCH_FLAG_SRC) && (d.optflag & BENCH_FLAG_DST) )
291         {
292                 if( !(d.ceif[0].type == IF_CAN     && d.ceif[1].type == IF_ETH_UDP) &&
293                     !(d.ceif[0].type == IF_ETH_UDP && d.ceif[1].type == IF_CAN    )    )
294                 {
295                         perr( "'-s' and '-d' should be different interface type." );
296                         return -1;
297                 }
298         } else
299         {
300                 perr( "'-s' and '-d' should be defined." );
301                 return -1;
302         }
303
304         tx_time = malloc( d.n*sizeof(struct timespec) );
305         rx_time = malloc( d.n*sizeof(struct timespec) );
306
307         pthread_barrier_init( &barrier, NULL, 2 );
308         pthread_create( &thr, NULL, thr_recv, &d  );
309
310         /**/
311         struct sockaddr* addr;
312         struct sockaddr_can addr_can;
313         struct sockaddr_in addr_udp;
314         int addr_size;
315
316         if( d.ceif[0].type == IF_CAN )
317         {
318                 sock = socket( PF_CAN, SOCK_RAW, CAN_RAW );
319                 addr_can.can_family = AF_CAN;
320                 addr_can.can_ifindex = d.ceif[0].can.ifindex;
321                 addr = (struct sockaddr*)&addr_can;
322                 addr_size = sizeof( addr_can );
323         }
324         else
325         {
326                 sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
327                 addr_udp.sin_family = AF_INET;
328                 addr_udp.sin_addr = d.ceif[0].eth.ip;
329                 addr_udp.sin_port = htons( d.ceif[0].eth.port );
330                 addr = (struct sockaddr*)&addr_udp;
331                 addr_size = sizeof( addr_udp );
332         }
333         if( sock < 0 ) /* ToDo: ? */
334                 return -1;
335
336         pthread_barrier_wait( &barrier );
337
338         /* send  */
339         for( i=0; i<d.n; i++ )
340         {
341                 cf.can_id = i;
342                 cf.can_dlc = 8;
343                 memcpy( cf.data, &i, sizeof(i) );
344
345                 clock_gettime( CLOCK_REALTIME, &tx_time[cf.data[0]] );
346                 ret = sendto( sock, &cf, sizeof(cf), 0, addr, addr_size ); /* ToDo: check ret */
347                 printdbg( "sending(%d)\n", ret );
348                 if( d.mode == BENCH_MODE_ONEATTIME )
349                 {
350                         pthread_barrier_wait( &barrier );
351                 }
352         }
353
354         close( sock ); /* ToDo: shutdown? */
355         pthread_join( thr, &thr_ret );
356
357         if( thr_ret != NULL )
358         {
359                 return 1;
360         }
361
362         /* results */
363         dump_arg( argc, argv );
364         for( i=0; i<d.n; i++ )
365         {
366                 timespec_subtract( &res, &rx_time[i], &tx_time[i] );
367
368                 time = res.tv_sec*1000000000.0f + (float)res.tv_nsec;
369                 printf( "%f\t", time );
370         }
371         putchar( '\n' );
372
373         pthread_barrier_destroy( &barrier );
374         free( tx_time );
375         free( rx_time );
376
377         return 0;
378 }
379