79324f5c4bd4932a4b2e2d505488bbbc4d95568f
[vlendec/samba-autobuild/.git] / ctdb / tests / ibwrapper_test.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Test the infiniband wrapper.
4  *
5  * Copyright (C) Sven Oehme <oehmes@de.ibm.com> 2006
6  *
7  * Major code contributions by Peter Somogyi <psomogyi@gamax.hu>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <netinet/in.h>
30 #include <sys/socket.h>
31 #include <netdb.h>
32 #include <arpa/inet.h>
33 #include <malloc.h>
34 #include <assert.h>
35 #include <unistd.h>
36 #include <signal.h>
37 #include <sys/time.h>
38
39 #include "includes.h"
40 #include "lib/events/events.h"
41 #include "ib/ibwrapper.h"
42
43 struct ibwtest_ctx {
44         int     is_server;
45         char    *id; /* my id */
46
47         struct ibw_initattr *attrs;
48         int     nattrs;
49         char    *opts; /* option string */
50
51         struct sockaddr_in *addrs; /* dynamic array of dest addrs */
52         int     naddrs;
53
54         unsigned int    nsec; /* nanosleep between messages */
55
56         int     cnt;
57
58         int     nmsg; /* number of messages to send (client) */
59
60         int     kill_me;
61         struct ibw_ctx  *ibwctx;
62
63         struct timeval  start_time, end_time;
64 };
65
66 struct ibwtest_conn {
67         char    *id;
68 };
69
70 enum testopcode {
71         TESTOP_SEND_ID = 1,
72         TESTOP_SEND_DATA = 2
73 };
74
75 int ibwtest_connect_everybody(struct ibwtest_ctx *tcx)
76 {
77         struct ibwtest_conn     *pconn = talloc_zero(tcx, struct ibwtest_conn);
78         int     i;
79
80         for(i=0; i<tcx->naddrs; i++) {
81                 if (ibw_connect(tcx->ibwctx, &tcx->addrs[i], pconn)) {
82                         fprintf(stderr, "ibw_connect error at %d\n", i);
83                         return -1;
84                 }
85         }
86         DEBUG(10, ("sent %d connect request...\n", tcx->naddrs));
87
88         return 0;
89 }
90
91 int ibwtest_send_id(struct ibw_conn *conn)
92 {
93         char *buf;
94         void *key;
95         struct ibwtest_ctx *tcx = talloc_get_type(conn->ctx->ctx_userdata, struct ibwtest_ctx);
96         uint32_t        len;
97
98         DEBUG(10, ("test IBWC_CONNECTED\n"));
99         len = sizeof(uint32_t)+strlen(tcx->id)+2;
100         if (ibw_alloc_send_buf(conn, (void **)&buf, &key, len)) {
101                 DEBUG(0, ("send_id: ibw_alloc_send_buf failed\n"));
102                 return -1;
103         }
104
105         /* first sizeof(uint32_t) size bytes are for length */
106         buf[sizeof(uint32_t)] = (char)TESTOP_SEND_ID;
107         strcpy(buf+sizeof(uint32_t)+1, tcx->id);
108
109         if (ibw_send(conn, buf, key, len)) {
110                 DEBUG(0, ("send_id: ibw_send error\n"));
111                 return -1;
112         }
113         return 0;
114 }
115
116 int ibwtest_send_test_msg(struct ibwtest_ctx *tcx, struct ibw_conn *conn, const char *msg)
117 {
118         char *buf, *p;
119         void *key;
120         uint32_t len;
121
122         if (conn->state!=IBWC_CONNECTED)
123                 return 0; /* not yet up */
124
125         len = strlen(msg)+2 + sizeof(uint32_t);
126         if (ibw_alloc_send_buf(conn, (void **)&buf, &key, len)) {
127                 fprintf(stderr, "send_test_msg: ibw_alloc_send_buf failed\n");
128                 return -1;
129         }
130
131         p = buf;
132         p += sizeof(uint32_t);
133         p[0] = (char)TESTOP_SEND_DATA;
134         p++;
135         strcpy(p, msg);
136
137         if (ibw_send(conn, buf, key, len)) {
138                 DEBUG(0, ("send_test_msg: ibw_send error\n"));
139                 return -1;
140         }
141         return 0;
142 }
143
144 int ibwtest_connstate_handler(struct ibw_ctx *ctx, struct ibw_conn *conn)
145 {
146         struct ibwtest_ctx      *tcx = NULL; /* userdata */
147         struct ibwtest_conn     *pconn = NULL; /* userdata */
148
149         if (ctx) {
150                 tcx = talloc_get_type(ctx->ctx_userdata, struct ibwtest_ctx);
151
152                 switch(ctx->state) {
153                 case IBWS_INIT:
154                         DEBUG(10, ("test IBWS_INIT\n"));
155                         break;
156                 case IBWS_READY:
157                         DEBUG(10, ("test IBWS_READY\n"));
158                         break;
159                 case IBWS_CONNECT_REQUEST:
160                         DEBUG(10, ("test IBWS_CONNECT_REQUEST\n"));
161                         pconn = talloc_zero(conn, struct ibwtest_conn);
162                         if (ibw_accept(ctx, conn, pconn)) {
163                                 DEBUG(0, ("error accepting the connect request\n"));
164                         }
165                         break;
166                 case IBWS_STOPPED:
167                         DEBUG(10, ("test IBWS_STOPPED\n"));
168                         talloc_free(tcx->ibwctx);
169                         DEBUG(10, ("talloc_free(tcx->ibwctx) DONE\n"));
170                         tcx->kill_me = 1; /* main loop can exit */
171                         break;
172                 case IBWS_ERROR:
173                         DEBUG(10, ("test IBWS_ERROR\n"));
174                         ibw_stop(tcx->ibwctx);
175                         break;
176                 default:
177                         assert(0);
178                         break;
179                 }
180         }
181
182         if (conn) {
183                 pconn = talloc_get_type(conn->conn_userdata, struct ibwtest_conn);
184                 switch(conn->state) {
185                 case IBWC_INIT:
186                         DEBUG(10, ("test IBWC_INIT\n"));
187                         break;
188                 case IBWC_CONNECTED:
189                         if (gettimeofday(&tcx->start_time, NULL)) {
190                                 DEBUG(0, ("gettimeofday error %d", errno));
191                                 return -1;
192                         }
193                         ibwtest_send_id(conn);
194                         break;
195                 case IBWC_DISCONNECTED:
196                         DEBUG(10, ("test IBWC_DISCONNECTED\n"));
197                         break;
198                 case IBWC_ERROR:
199                         DEBUG(10, ("test IBWC_ERROR\n"));
200                         break;
201                 default:
202                         assert(0);
203                         break;
204                 }
205         }
206         return 0;
207 }
208
209 int ibwtest_receive_handler(struct ibw_conn *conn, void *buf, int n)
210 {
211         struct ibwtest_conn *pconn;
212         enum testopcode op;
213         struct ibwtest_ctx *tcx = talloc_get_type(conn->ctx->ctx_userdata, struct ibwtest_ctx);
214
215         assert(conn!=NULL);
216         assert(n>=sizeof(uint32_t)+2);
217         pconn = talloc_get_type(conn->conn_userdata, struct ibwtest_conn);
218
219         op = (enum testopcode)((char *)buf)[sizeof(uint32_t)];
220         if (op==TESTOP_SEND_ID) {
221                 pconn->id = talloc_strdup(pconn, ((char *)buf)+sizeof(uint32_t)+1);
222         }
223         DEBUG(11, ("[%d]msg from %s: \"%s\"(%d)\n", op,
224                 pconn->id ? pconn->id : "NULL", ((char *)buf)+sizeof(uint32_t)+1, n));
225
226         if (tcx->is_server) {
227                 char *buf2;
228                 void *key2;
229                 /* bounce message */
230                 if (ibw_alloc_send_buf(conn, (void **)&buf2, &key2, n)) {
231                         fprintf(stderr, "ibw_alloc_send_buf error #2\n");
232                         return -1;
233                 }
234                 memcpy(buf2, buf, n);
235                 if (ibw_send(conn, buf2, key2, n)) {
236                         fprintf(stderr, "ibw_send error #2\n");
237                         return -2;
238                 }
239         } else {
240                 if (tcx->nmsg) {
241                         char    msg[26];
242                         sprintf(msg, "hello world %d", tcx->nmsg--);
243                         ibwtest_send_test_msg(tcx, conn, msg);
244                         if (tcx->nmsg==0)
245                                 tcx->kill_me = 1;
246                 }
247         }
248
249         return 0;
250 }
251
252 void ibwtest_timeout_handler(struct event_context *ev, struct timed_event *te, 
253         struct timeval t, void *private)
254 {
255         struct ibwtest_ctx *tcx = talloc_get_type(private, struct ibwtest_ctx);
256
257         if (!tcx->is_server) {
258                 struct ibw_conn *p;
259                 char    msg[50];
260
261                 /* fill it with something variable... */
262                 sprintf(msg, "hello world %d", tcx->cnt++);
263
264                 /* send something to everybody... */
265                 for(p=tcx->ibwctx->conn_list; p!=NULL; p=p->next) {
266                         ibwtest_send_test_msg(tcx, p, msg);
267                 }
268         } /* else allow main loop run */
269 }
270
271 static struct ibwtest_ctx *testctx = NULL;
272
273 void ibwtest_sigquit_handler(int sig)
274 {
275         DEBUG(0, ("got SIGQUIT\n"));
276         if (testctx)
277                 ibw_stop(testctx->ibwctx);
278 }
279
280 int ibwtest_parse_attrs(struct ibwtest_ctx *tcx, char *optext,
281         struct ibw_initattr **pattrs, int *nattrs, char op)
282 {
283         int     i = 0, n = 1;
284         int     porcess_next = 1;
285         char    *p, *q;
286         struct ibw_initattr *attrs = NULL;
287
288         *pattrs = NULL;
289         for(p = optext; *p!='\0'; p++) {
290                 if (*p==',')
291                         n++;
292         }
293
294         attrs = (struct ibw_initattr *)talloc_size(tcx,
295                 n * sizeof(struct ibw_initattr));
296         for(p = optext; *p!='\0'; p++) {
297                 if (porcess_next) {
298                         attrs[i].name = p;
299                         q = strchr(p, ':');
300                         if (q==NULL) {
301                                 fprintf(stderr, "-%c format error\n", op);
302                                 return -1;
303                         }
304                         *q = '\0';
305                         attrs[i].value = q + 1;
306
307                         porcess_next = 0;
308                         i++;
309                         p = q; /* ++ at end */
310                 }
311                 if (*p==',') {
312                         *p = '\0'; /* ++ at end */
313                         porcess_next = 1;
314                 }
315         }
316         *pattrs = attrs;
317         *nattrs = n;
318
319         return 0;
320 }
321
322 int ibwtest_getdests(struct ibwtest_ctx *tcx, char op)
323 {
324         int     i;
325         struct ibw_initattr     *attrs = NULL;
326         struct sockaddr_in      *p;
327         char    *tmp;
328
329         tmp = talloc_strdup(tcx, optarg);
330         /* hack to reuse the above ibw_initattr parser */
331         if (ibwtest_parse_attrs(tcx, tmp, &attrs, &tcx->naddrs, op))
332                 return -1;
333
334         tcx->addrs = talloc_size(tcx,
335                 tcx->naddrs * sizeof(struct sockaddr_in));
336         for(i=0; i<tcx->naddrs; i++) {
337                 p = tcx->addrs + i;
338                 p->sin_family = AF_INET;
339                 p->sin_addr.s_addr = inet_addr(attrs[i].name);
340                 p->sin_port = atoi(attrs[i].value);
341         }
342
343         return 0;
344 }
345
346 int ibwtest_init_server(struct ibwtest_ctx *tcx)
347 {
348         if (tcx->naddrs!=1) {
349                 fprintf(stderr, "incorrect number of addrs(%d!=1)\n", tcx->naddrs);
350                 return -1;
351         }
352
353         if (ibw_bind(tcx->ibwctx, &tcx->addrs[0])) {
354                 DEBUG(0, ("ERROR: ibw_bind failed\n"));
355                 return -1;
356         }
357         
358         if (ibw_listen(tcx->ibwctx, 1)) {
359                 DEBUG(0, ("ERROR: ibw_listen failed\n"));
360                 return -1;
361         }
362
363         /* continued at IBWS_READY */
364         return 0;
365 }
366
367 void ibwtest_usage(struct ibwtest_ctx *tcx, char *name)
368 {
369         printf("Usage:\n");
370         printf("\t%s -i <id> -o {name:value} -d {addr:port} -t nsec -s\n", name);
371         printf("\t-i <id> is a free text, acting as a server id, max 23 chars [mandatory]\n");
372         printf("\t-o name1:value1,name2:value2,... is a list of (name, value) pairs\n");
373         printf("\t-d addr1:port1,addr2:port2,... is a list of destination ip addresses\n");
374         printf("\t-t nsec delta time between sends in nanosec [default %d]\n", tcx->nsec);
375         printf("\t\t send message periodically and endless when nsec is non-zero\n");
376         printf("\t-s server mode (you have to give exactly one -d address:port in this case)\n");
377         printf("\t-n number of messages to send [default %d]\n", tcx->nmsg);
378         printf("Press ctrl+C to stop the program.\n");
379 }
380
381 int main(int argc, char *argv[])
382 {
383         int     rc, op;
384         int     result = 1, nmsg;
385         struct event_context *ev = NULL;
386         struct ibwtest_ctx *tcx = NULL;
387         float   usec;
388
389         tcx = talloc_zero(NULL, struct ibwtest_ctx);
390         memset(tcx, 0, sizeof(struct ibwtest_ctx));
391         tcx->nsec = 0;
392         tcx->nmsg = 1000;
393
394         /* here is the only case we can't avoid using global... */
395         testctx = tcx;
396         signal(SIGQUIT, ibwtest_sigquit_handler);
397
398         while ((op=getopt(argc, argv, "i:o:d:m:st:n:")) != -1) {
399                 switch (op) {
400                 case 'i':
401                         tcx->id = talloc_strdup(tcx, optarg);
402                         break;
403                 case 'o':
404                         tcx->opts = talloc_strdup(tcx, optarg);
405                         if (ibwtest_parse_attrs(tcx, tcx->opts, &tcx->attrs,
406                                 &tcx->nattrs, op))
407                                 goto cleanup;
408                         break;
409                 case 'd':
410                         if (ibwtest_getdests(tcx, op))
411                                 goto cleanup;
412                         break;
413                 case 's':
414                         tcx->is_server = 1;
415                         break;
416                 case 't':
417                         tcx->nsec = (unsigned int)atoi(optarg);
418                         break;
419                 case 'n':
420                         tcx->nmsg = atoi(optarg);
421                         break;
422                 default:
423                         fprintf(stderr, "ERROR: unknown option -%c\n", (char)op);
424                         ibwtest_usage(tcx, argv[0]);
425                         goto cleanup;
426                 }
427         }
428         if (tcx->id==NULL) {
429                 ibwtest_usage(tcx, argv[0]);
430                 goto cleanup;
431         }
432         nmsg = tcx->nmsg;
433
434         ev = event_context_init(NULL);
435         assert(ev);
436
437         tcx->ibwctx = ibw_init(tcx->attrs, tcx->nattrs,
438                 tcx,
439                 ibwtest_connstate_handler,
440                 ibwtest_receive_handler,
441                 ev
442         );
443         if (!tcx->ibwctx)
444                 goto cleanup;
445
446         if (tcx->is_server)
447                 rc = ibwtest_init_server(tcx);
448         else
449                 rc = ibwtest_connect_everybody(tcx);
450         if (rc)
451                 goto cleanup;
452
453         while(!tcx->kill_me) {
454                 if (tcx->nsec)
455                         event_add_timed(ev, tcx, timeval_current_ofs(0, tcx->nsec),
456                                 ibwtest_timeout_handler, tcx);
457                 event_loop_once(ev);
458         }
459
460         if (!tcx->is_server && nmsg!=0) {
461                 if (gettimeofday(&tcx->end_time, NULL)) {
462                         DEBUG(0, ("gettimeofday error %d", errno));
463                         goto cleanup;
464                 }
465                 usec = (tcx->end_time.tv_sec - tcx->start_time.tv_sec) * 1000000 +
466                                 (tcx->end_time.tv_usec - tcx->start_time.tv_usec);
467                 printf("usec: %f, nmsg: %d, usec/nmsg: %f\n",
468                         usec, nmsg, usec/(float)nmsg);
469         }
470
471         result = 0; /* everything OK */
472
473 cleanup:
474         if (tcx)
475                 talloc_free(tcx);
476         if (ev)
477                 talloc_free(ev);
478         DEBUG(0, ("exited with code %d\n", result));
479         return result;
480 }