Added overload test scenario + fixed 1 send queue bug.
[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; /* delta times between messages in nanosec */
55         unsigned int    sleep_nsec; /* nanosleep in the main loop to emulate overloading */
56
57         int     cnt;
58
59         int     nmsg; /* number of messages to send (client) */
60
61         int     kill_me;
62         int     error;
63         struct ibw_ctx  *ibwctx;
64
65         struct timeval  start_time, end_time;
66 };
67
68 struct ibwtest_conn {
69         char    *id;
70 };
71
72 enum testopcode {
73         TESTOP_SEND_ID = 1,
74         TESTOP_SEND_DATA = 2
75 };
76
77 int ibwtest_nanosleep(unsigned int sleep_nsec)
78 {
79         int nanosleep(const struct timespec *req, struct timespec *rem);
80         struct timespec req, rem;
81
82         memset(&req, 0, sizeof(struct timespec));
83         memset(&rem, 0, sizeof(struct timespec));
84         req.tv_sec = 0;
85         req.tv_nsec = sleep_nsec;
86
87         while (nanosleep(&req, &rem) < 0) {
88                 if (errno != EINTR) {
89                         DEBUG(0, ("nanosleep ERROR: %d\n", errno));
90                         return -1;
91                 }
92                 memcpy(&req, &rem, sizeof(struct timespec));
93         }
94         return 0;
95 }
96
97 int ibwtest_connect_everybody(struct ibwtest_ctx *tcx)
98 {
99         struct ibwtest_conn     *pconn = talloc_zero(tcx, struct ibwtest_conn);
100         int     i;
101
102         for(i=0; i<tcx->naddrs; i++) {
103                 if (ibw_connect(tcx->ibwctx, &tcx->addrs[i], pconn)) {
104                         fprintf(stderr, "ibw_connect error at %d\n", i);
105                         return -1;
106                 }
107         }
108         DEBUG(10, ("sent %d connect request...\n", tcx->naddrs));
109
110         return 0;
111 }
112
113 int ibwtest_send_id(struct ibw_conn *conn)
114 {
115         struct ibwtest_ctx *tcx = talloc_get_type(conn->ctx->ctx_userdata, struct ibwtest_ctx);
116         char *buf;
117         void *key;
118         uint32_t        len;
119
120         DEBUG(10, ("test IBWC_CONNECTED\n"));
121         len = sizeof(uint32_t)+strlen(tcx->id)+2;
122         if (ibw_alloc_send_buf(conn, (void **)&buf, &key, len)) {
123                 DEBUG(0, ("send_id: ibw_alloc_send_buf failed\n"));
124                 return -1;
125         }
126
127         /* first sizeof(uint32_t) size bytes are for length */
128         buf[sizeof(uint32_t)] = (char)TESTOP_SEND_ID;
129         strcpy(buf+sizeof(uint32_t)+1, tcx->id);
130
131         if (ibw_send(conn, buf, key, len)) {
132                 DEBUG(0, ("send_id: ibw_send error\n"));
133                 return -1;
134         }
135         return 0;
136 }
137
138 int ibwtest_send_test_msg(struct ibwtest_ctx *tcx, struct ibw_conn *conn, const char *msg)
139 {
140         char *buf, *p;
141         void *key;
142         uint32_t len;
143
144         if (conn->state!=IBWC_CONNECTED)
145                 return 0; /* not yet up */
146
147         len = strlen(msg)+2 + sizeof(uint32_t);
148         if (ibw_alloc_send_buf(conn, (void **)&buf, &key, len)) {
149                 fprintf(stderr, "send_test_msg: ibw_alloc_send_buf failed\n");
150                 return -1;
151         }
152
153         p = buf;
154         p += sizeof(uint32_t);
155         p[0] = (char)TESTOP_SEND_DATA;
156         p++;
157         strcpy(p, msg);
158
159         if (ibw_send(conn, buf, key, len)) {
160                 DEBUG(0, ("send_test_msg: ibw_send error\n"));
161                 return -1;
162         }
163         return 0;
164 }
165
166 int ibwtest_connstate_handler(struct ibw_ctx *ctx, struct ibw_conn *conn)
167 {
168         struct ibwtest_ctx      *tcx = NULL; /* userdata */
169         struct ibwtest_conn     *pconn = NULL; /* userdata */
170
171         if (ctx) {
172                 tcx = talloc_get_type(ctx->ctx_userdata, struct ibwtest_ctx);
173
174                 switch(ctx->state) {
175                 case IBWS_INIT:
176                         DEBUG(10, ("test IBWS_INIT\n"));
177                         break;
178                 case IBWS_READY:
179                         DEBUG(10, ("test IBWS_READY\n"));
180                         break;
181                 case IBWS_CONNECT_REQUEST:
182                         DEBUG(10, ("test IBWS_CONNECT_REQUEST\n"));
183                         pconn = talloc_zero(conn, struct ibwtest_conn);
184                         if (ibw_accept(ctx, conn, pconn)) {
185                                 DEBUG(0, ("error accepting the connect request\n"));
186                         }
187                         break;
188                 case IBWS_STOPPED:
189                         DEBUG(10, ("test IBWS_STOPPED\n"));
190                         talloc_free(tcx->ibwctx);
191                         DEBUG(10, ("talloc_free(tcx->ibwctx) DONE\n"));
192                         tcx->kill_me = 1; /* main loop can exit */
193                         break;
194                 case IBWS_ERROR:
195                         DEBUG(10, ("test IBWS_ERROR\n"));
196                         ibw_stop(tcx->ibwctx);
197                         break;
198                 default:
199                         assert(0);
200                         break;
201                 }
202         }
203
204         if (conn) {
205                 pconn = talloc_get_type(conn->conn_userdata, struct ibwtest_conn);
206                 switch(conn->state) {
207                 case IBWC_INIT:
208                         DEBUG(10, ("test IBWC_INIT\n"));
209                         break;
210                 case IBWC_CONNECTED:
211                         if (gettimeofday(&tcx->start_time, NULL)) {
212                                 DEBUG(0, ("gettimeofday error %d", errno));
213                                 return -1;
214                         }
215                         ibwtest_send_id(conn);
216                         break;
217                 case IBWC_DISCONNECTED:
218                         DEBUG(10, ("test IBWC_DISCONNECTED\n"));
219                         break;
220                 case IBWC_ERROR:
221                         DEBUG(10, ("test IBWC_ERROR\n"));
222                         break;
223                 default:
224                         assert(0);
225                         break;
226                 }
227         }
228         return 0;
229 }
230
231 int ibwtest_receive_handler(struct ibw_conn *conn, void *buf, int n)
232 {
233         struct ibwtest_conn *pconn;
234         enum testopcode op;
235         struct ibwtest_ctx *tcx = talloc_get_type(conn->ctx->ctx_userdata, struct ibwtest_ctx);
236
237         assert(conn!=NULL);
238         assert(n>=sizeof(uint32_t)+2);
239         pconn = talloc_get_type(conn->conn_userdata, struct ibwtest_conn);
240
241         op = (enum testopcode)((char *)buf)[sizeof(uint32_t)];
242         if (op==TESTOP_SEND_ID) {
243                 pconn->id = talloc_strdup(pconn, ((char *)buf)+sizeof(uint32_t)+1);
244         }
245         DEBUG(11, ("[%d]msg from %s: \"%s\"(%d)\n", op,
246                 pconn->id ? pconn->id : "NULL", ((char *)buf)+sizeof(uint32_t)+1, n));
247
248         if (tcx->is_server) {
249                 char *buf2;
250                 void *key2;
251                 /* bounce message */
252                 if (ibw_alloc_send_buf(conn, (void **)&buf2, &key2, n)) {
253                         fprintf(stderr, "ibw_alloc_send_buf error #2\n");
254                         return -1;
255                 }
256                 memcpy(buf2, buf, n);
257                 if (ibw_send(conn, buf2, key2, n)) {
258                         fprintf(stderr, "ibw_send error #2\n");
259                         return -2;
260                 }
261         } else {
262                 if (tcx->nmsg) {
263                         char    msg[26];
264                         sprintf(msg, "hello world %d", tcx->nmsg--);
265                         tcx->error = ibwtest_send_test_msg(tcx, conn, msg);
266                         if (tcx->nmsg==0)
267                                 tcx->kill_me = 1;
268                 }
269         }
270
271         return 0;
272 }
273
274 void ibwtest_timeout_handler(struct event_context *ev, struct timed_event *te, 
275         struct timeval t, void *private)
276 {
277         struct ibwtest_ctx *tcx = talloc_get_type(private, struct ibwtest_ctx);
278
279         if (!tcx->is_server) {
280                 struct ibw_conn *p;
281                 char    msg[50];
282
283                 /* fill it with something variable... */
284                 sprintf(msg, "hello world %d", tcx->cnt++);
285
286                 /* send something to everybody... */
287                 for(p=tcx->ibwctx->conn_list; p!=NULL; p=p->next) {
288                         if (p->state==IBWC_CONNECTED) {
289                                 tcx->error = ibwtest_send_test_msg(tcx, p, msg);
290                         }
291                 }
292         } /* else allow main loop run */
293 }
294
295 static struct ibwtest_ctx *testctx = NULL;
296
297 void ibwtest_sigquit_handler(int sig)
298 {
299         DEBUG(0, ("got SIGQUIT\n"));
300         if (testctx)
301                 ibw_stop(testctx->ibwctx);
302 }
303
304 int ibwtest_parse_attrs(struct ibwtest_ctx *tcx, char *optext,
305         struct ibw_initattr **pattrs, int *nattrs, char op)
306 {
307         int     i = 0, n = 1;
308         int     porcess_next = 1;
309         char    *p, *q;
310         struct ibw_initattr *attrs = NULL;
311
312         *pattrs = NULL;
313         for(p = optext; *p!='\0'; p++) {
314                 if (*p==',')
315                         n++;
316         }
317
318         attrs = (struct ibw_initattr *)talloc_size(tcx,
319                 n * sizeof(struct ibw_initattr));
320         for(p = optext; *p!='\0'; p++) {
321                 if (porcess_next) {
322                         attrs[i].name = p;
323                         q = strchr(p, ':');
324                         if (q==NULL) {
325                                 fprintf(stderr, "-%c format error\n", op);
326                                 return -1;
327                         }
328                         *q = '\0';
329                         attrs[i].value = q + 1;
330
331                         porcess_next = 0;
332                         i++;
333                         p = q; /* ++ at end */
334                 }
335                 if (*p==',') {
336                         *p = '\0'; /* ++ at end */
337                         porcess_next = 1;
338                 }
339         }
340         *pattrs = attrs;
341         *nattrs = n;
342
343         return 0;
344 }
345
346 int ibwtest_getdests(struct ibwtest_ctx *tcx, char op)
347 {
348         int     i;
349         struct ibw_initattr     *attrs = NULL;
350         struct sockaddr_in      *p;
351         char    *tmp;
352
353         tmp = talloc_strdup(tcx, optarg);
354         /* hack to reuse the above ibw_initattr parser */
355         if (ibwtest_parse_attrs(tcx, tmp, &attrs, &tcx->naddrs, op))
356                 return -1;
357
358         tcx->addrs = talloc_size(tcx,
359                 tcx->naddrs * sizeof(struct sockaddr_in));
360         for(i=0; i<tcx->naddrs; i++) {
361                 p = tcx->addrs + i;
362                 p->sin_family = AF_INET;
363                 p->sin_addr.s_addr = inet_addr(attrs[i].name);
364                 p->sin_port = atoi(attrs[i].value);
365         }
366
367         return 0;
368 }
369
370 int ibwtest_init_server(struct ibwtest_ctx *tcx)
371 {
372         if (tcx->naddrs!=1) {
373                 fprintf(stderr, "incorrect number of addrs(%d!=1)\n", tcx->naddrs);
374                 return -1;
375         }
376
377         if (ibw_bind(tcx->ibwctx, &tcx->addrs[0])) {
378                 DEBUG(0, ("ERROR: ibw_bind failed\n"));
379                 return -1;
380         }
381         
382         if (ibw_listen(tcx->ibwctx, 1)) {
383                 DEBUG(0, ("ERROR: ibw_listen failed\n"));
384                 return -1;
385         }
386
387         /* continued at IBWS_READY */
388         return 0;
389 }
390
391 void ibwtest_usage(struct ibwtest_ctx *tcx, char *name)
392 {
393         printf("Usage:\n");
394         printf("\t%s -i <id> -o {name:value} -d {addr:port} -t nsec -s\n", name);
395         printf("\t-i <id> is a free text, acting as a server id, max 23 chars [mandatory]\n");
396         printf("\t-o name1:value1,name2:value2,... is a list of (name, value) pairs\n");
397         printf("\t-d addr1:port1,addr2:port2,... is a list of destination ip addresses\n");
398         printf("\t-t nsec delta time between sends in nanosec [default %d]\n", tcx->nsec);
399         printf("\t\t send message periodically and endless when nsec is non-zero\n");
400         printf("\t-s server mode (you have to give exactly one -d address:port in this case)\n");
401         printf("\t-n number of messages to send [default %d]\n", tcx->nmsg);
402         printf("\t-l nsec time to sleep in the main loop [default %d]\n", tcx->sleep_nsec);
403         printf("Press ctrl+C to stop the program.\n");
404 }
405
406 int main(int argc, char *argv[])
407 {
408         int     rc, op;
409         int     result = 1, nmsg;
410         struct event_context *ev = NULL;
411         struct ibwtest_ctx *tcx = NULL;
412         float   usec;
413
414         tcx = talloc_zero(NULL, struct ibwtest_ctx);
415         memset(tcx, 0, sizeof(struct ibwtest_ctx));
416         tcx->nsec = 0;
417         tcx->nmsg = 1000;
418
419         /* here is the only case we can't avoid using global... */
420         testctx = tcx;
421         signal(SIGQUIT, ibwtest_sigquit_handler);
422
423         while ((op=getopt(argc, argv, "i:o:d:m:st:n:l:")) != -1) {
424                 switch (op) {
425                 case 'i':
426                         tcx->id = talloc_strdup(tcx, optarg);
427                         break;
428                 case 'o':
429                         tcx->opts = talloc_strdup(tcx, optarg);
430                         if (ibwtest_parse_attrs(tcx, tcx->opts, &tcx->attrs,
431                                 &tcx->nattrs, op))
432                                 goto cleanup;
433                         break;
434                 case 'd':
435                         if (ibwtest_getdests(tcx, op))
436                                 goto cleanup;
437                         break;
438                 case 's':
439                         tcx->is_server = 1;
440                         break;
441                 case 't':
442                         tcx->nsec = (unsigned int)atoi(optarg);
443                         break;
444                 case 'n':
445                         tcx->nmsg = atoi(optarg);
446                         break;
447                 case 'l':
448                         tcx->sleep_nsec = (unsigned int)atoi(optarg);
449                         break;
450                 default:
451                         fprintf(stderr, "ERROR: unknown option -%c\n", (char)op);
452                         ibwtest_usage(tcx, argv[0]);
453                         goto cleanup;
454                 }
455         }
456         if (tcx->id==NULL) {
457                 ibwtest_usage(tcx, argv[0]);
458                 goto cleanup;
459         }
460         nmsg = tcx->nmsg;
461
462         ev = event_context_init(NULL);
463         assert(ev);
464
465         tcx->ibwctx = ibw_init(tcx->attrs, tcx->nattrs,
466                 tcx,
467                 ibwtest_connstate_handler,
468                 ibwtest_receive_handler,
469                 ev
470         );
471         if (!tcx->ibwctx)
472                 goto cleanup;
473
474         if (tcx->is_server)
475                 rc = ibwtest_init_server(tcx);
476         else
477                 rc = ibwtest_connect_everybody(tcx);
478         if (rc)
479                 goto cleanup;
480
481         while(!tcx->kill_me && !tcx->error) {
482                 if (tcx->nsec) {
483                         event_add_timed(ev, tcx, timeval_current_ofs(0, tcx->nsec),
484                                 ibwtest_timeout_handler, tcx);
485                 }
486
487                 event_loop_once(ev);
488
489                 if (tcx->sleep_nsec) {
490                         if (ibwtest_nanosleep(tcx->sleep_nsec))
491                                 goto cleanup;
492                 }
493         }
494
495         if (!tcx->is_server && nmsg!=0 && !tcx->error) {
496                 if (gettimeofday(&tcx->end_time, NULL)) {
497                         DEBUG(0, ("gettimeofday error %d\n", errno));
498                         goto cleanup;
499                 }
500                 usec = (tcx->end_time.tv_sec - tcx->start_time.tv_sec) * 1000000 +
501                                 (tcx->end_time.tv_usec - tcx->start_time.tv_usec);
502                 printf("usec: %f, nmsg: %d, usec/nmsg: %f\n",
503                         usec, nmsg, usec/(float)nmsg);
504         }
505
506         if (!tcx->error)
507                 result = 0; /* everything OK */
508
509 cleanup:
510         if (tcx)
511                 talloc_free(tcx);
512         if (ev)
513                 talloc_free(ev);
514         DEBUG(0, ("exited with code %d\n", result));
515         return result;
516 }