xref: /freebsd/contrib/ofed/librdmacm/examples/cmatose.c (revision 0b3105a37d7adcadcb720112fed4dc4e8040be99)
1 /*
2  * Copyright (c) 2005-2006 Intel Corporation.  All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  *
32  * $Id$
33  */
34 
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <netinet/in.h>
41 #include <sys/socket.h>
42 #include <netdb.h>
43 #include <byteswap.h>
44 #include <getopt.h>
45 
46 #include <rdma/rdma_cma.h>
47 
48 #if __BYTE_ORDER == __BIG_ENDIAN
49 static inline uint64_t cpu_to_be64(uint64_t x) { return x; }
50 static inline uint32_t cpu_to_be32(uint32_t x) { return x; }
51 #else
52 static inline uint64_t cpu_to_be64(uint64_t x) { return bswap_64(x); }
53 static inline uint32_t cpu_to_be32(uint32_t x) { return bswap_32(x); }
54 #endif
55 
56 struct cmatest_node {
57 	int			id;
58 	struct rdma_cm_id	*cma_id;
59 	int			connected;
60 	struct ibv_pd		*pd;
61 	struct ibv_cq		*cq[2];
62 	struct ibv_mr		*mr;
63 	void			*mem;
64 };
65 
66 enum CQ_INDEX {
67 	SEND_CQ_INDEX,
68 	RECV_CQ_INDEX
69 };
70 
71 struct cmatest {
72 	struct rdma_event_channel *channel;
73 	struct cmatest_node	*nodes;
74 	int			conn_index;
75 	int			connects_left;
76 	int			disconnects_left;
77 
78 	struct rdma_addr	addr;
79 };
80 
81 static struct cmatest test;
82 static int connections = 1;
83 static int message_size = 100;
84 static int message_count = 10;
85 static uint16_t port = 7471;
86 static uint8_t set_tos = 0;
87 static uint8_t tos;
88 static uint8_t migrate = 0;
89 static char *dst_addr;
90 static char *src_addr;
91 
92 static int create_message(struct cmatest_node *node)
93 {
94 	if (!message_size)
95 		message_count = 0;
96 
97 	if (!message_count)
98 		return 0;
99 
100 	node->mem = malloc(message_size);
101 	if (!node->mem) {
102 		printf("failed message allocation\n");
103 		return -1;
104 	}
105 	node->mr = ibv_reg_mr(node->pd, node->mem, message_size,
106 			     IBV_ACCESS_LOCAL_WRITE);
107 	if (!node->mr) {
108 		printf("failed to reg MR\n");
109 		goto err;
110 	}
111 	return 0;
112 err:
113 	free(node->mem);
114 	return -1;
115 }
116 
117 static int init_node(struct cmatest_node *node)
118 {
119 	struct ibv_qp_init_attr init_qp_attr;
120 	int cqe, ret;
121 
122 	node->pd = ibv_alloc_pd(node->cma_id->verbs);
123 	if (!node->pd) {
124 		ret = -ENOMEM;
125 		printf("cmatose: unable to allocate PD\n");
126 		goto out;
127 	}
128 
129 	cqe = message_count ? message_count : 1;
130 	node->cq[SEND_CQ_INDEX] = ibv_create_cq(node->cma_id->verbs, cqe, node, 0, 0);
131 	node->cq[RECV_CQ_INDEX] = ibv_create_cq(node->cma_id->verbs, cqe, node, 0, 0);
132 	if (!node->cq[SEND_CQ_INDEX] || !node->cq[RECV_CQ_INDEX]) {
133 		ret = -ENOMEM;
134 		printf("cmatose: unable to create CQ\n");
135 		goto out;
136 	}
137 
138 	memset(&init_qp_attr, 0, sizeof init_qp_attr);
139 	init_qp_attr.cap.max_send_wr = cqe;
140 	init_qp_attr.cap.max_recv_wr = cqe;
141 	init_qp_attr.cap.max_send_sge = 1;
142 	init_qp_attr.cap.max_recv_sge = 1;
143 	init_qp_attr.qp_context = node;
144 	init_qp_attr.sq_sig_all = 1;
145 	init_qp_attr.qp_type = IBV_QPT_RC;
146 	init_qp_attr.send_cq = node->cq[SEND_CQ_INDEX];
147 	init_qp_attr.recv_cq = node->cq[RECV_CQ_INDEX];
148 	ret = rdma_create_qp(node->cma_id, node->pd, &init_qp_attr);
149 	if (ret) {
150 		perror("cmatose: unable to create QP");
151 		goto out;
152 	}
153 
154 	ret = create_message(node);
155 	if (ret) {
156 		printf("cmatose: failed to create messages: %d\n", ret);
157 		goto out;
158 	}
159 out:
160 	return ret;
161 }
162 
163 static int post_recvs(struct cmatest_node *node)
164 {
165 	struct ibv_recv_wr recv_wr, *recv_failure;
166 	struct ibv_sge sge;
167 	int i, ret = 0;
168 
169 	if (!message_count)
170 		return 0;
171 
172 	recv_wr.next = NULL;
173 	recv_wr.sg_list = &sge;
174 	recv_wr.num_sge = 1;
175 	recv_wr.wr_id = (uintptr_t) node;
176 
177 	sge.length = message_size;
178 	sge.lkey = node->mr->lkey;
179 	sge.addr = (uintptr_t) node->mem;
180 
181 	for (i = 0; i < message_count && !ret; i++ ) {
182 		ret = ibv_post_recv(node->cma_id->qp, &recv_wr, &recv_failure);
183 		if (ret) {
184 			printf("failed to post receives: %d\n", ret);
185 			break;
186 		}
187 	}
188 	return ret;
189 }
190 
191 static int post_sends(struct cmatest_node *node)
192 {
193 	struct ibv_send_wr send_wr, *bad_send_wr;
194 	struct ibv_sge sge;
195 	int i, ret = 0;
196 
197 	if (!node->connected || !message_count)
198 		return 0;
199 
200 	send_wr.next = NULL;
201 	send_wr.sg_list = &sge;
202 	send_wr.num_sge = 1;
203 	send_wr.opcode = IBV_WR_SEND;
204 	send_wr.send_flags = 0;
205 	send_wr.wr_id = (unsigned long)node;
206 
207 	sge.length = message_size;
208 	sge.lkey = node->mr->lkey;
209 	sge.addr = (uintptr_t) node->mem;
210 
211 	for (i = 0; i < message_count && !ret; i++) {
212 		ret = ibv_post_send(node->cma_id->qp, &send_wr, &bad_send_wr);
213 		if (ret)
214 			printf("failed to post sends: %d\n", ret);
215 	}
216 	return ret;
217 }
218 
219 static void connect_error(void)
220 {
221 	test.disconnects_left--;
222 	test.connects_left--;
223 }
224 
225 static int addr_handler(struct cmatest_node *node)
226 {
227 	int ret;
228 
229 	if (set_tos) {
230 		ret = rdma_set_option(node->cma_id, RDMA_OPTION_ID,
231 				      RDMA_OPTION_ID_TOS, &tos, sizeof tos);
232 		if (ret)
233 			perror("cmatose: set TOS option failed");
234 	}
235 
236 	ret = rdma_resolve_route(node->cma_id, 2000);
237 	if (ret) {
238 		perror("cmatose: resolve route failed");
239 		connect_error();
240 	}
241 	return ret;
242 }
243 
244 static int route_handler(struct cmatest_node *node)
245 {
246 	struct rdma_conn_param conn_param;
247 	int ret;
248 
249 	ret = init_node(node);
250 	if (ret)
251 		goto err;
252 
253 	ret = post_recvs(node);
254 	if (ret)
255 		goto err;
256 
257 	memset(&conn_param, 0, sizeof conn_param);
258 	conn_param.responder_resources = 1;
259 	conn_param.initiator_depth = 1;
260 	conn_param.retry_count = 5;
261 	ret = rdma_connect(node->cma_id, &conn_param);
262 	if (ret) {
263 		perror("cmatose: failure connecting");
264 		goto err;
265 	}
266 	return 0;
267 err:
268 	connect_error();
269 	return ret;
270 }
271 
272 static int connect_handler(struct rdma_cm_id *cma_id)
273 {
274 	struct cmatest_node *node;
275 	struct rdma_conn_param conn_param;
276 	int ret;
277 
278 	if (test.conn_index == connections) {
279 		ret = -ENOMEM;
280 		goto err1;
281 	}
282 	node = &test.nodes[test.conn_index++];
283 
284 	node->cma_id = cma_id;
285 	cma_id->context = node;
286 
287 	ret = init_node(node);
288 	if (ret)
289 		goto err2;
290 
291 	ret = post_recvs(node);
292 	if (ret)
293 		goto err2;
294 
295 	memset(&conn_param, 0, sizeof conn_param);
296 	conn_param.responder_resources = 1;
297 	conn_param.initiator_depth = 1;
298 	ret = rdma_accept(node->cma_id, &conn_param);
299 	if (ret) {
300 		perror("cmatose: failure accepting");
301 		goto err2;
302 	}
303 	return 0;
304 
305 err2:
306 	node->cma_id = NULL;
307 	connect_error();
308 err1:
309 	printf("cmatose: failing connection request\n");
310 	rdma_reject(cma_id, NULL, 0);
311 	return ret;
312 }
313 
314 static int cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
315 {
316 	int ret = 0;
317 
318 	switch (event->event) {
319 	case RDMA_CM_EVENT_ADDR_RESOLVED:
320 		ret = addr_handler(cma_id->context);
321 		break;
322 	case RDMA_CM_EVENT_ROUTE_RESOLVED:
323 		ret = route_handler(cma_id->context);
324 		break;
325 	case RDMA_CM_EVENT_CONNECT_REQUEST:
326 		ret = connect_handler(cma_id);
327 		break;
328 	case RDMA_CM_EVENT_ESTABLISHED:
329 		((struct cmatest_node *) cma_id->context)->connected = 1;
330 		test.connects_left--;
331 		break;
332 	case RDMA_CM_EVENT_ADDR_ERROR:
333 	case RDMA_CM_EVENT_ROUTE_ERROR:
334 	case RDMA_CM_EVENT_CONNECT_ERROR:
335 	case RDMA_CM_EVENT_UNREACHABLE:
336 	case RDMA_CM_EVENT_REJECTED:
337 		printf("cmatose: event: %s, error: %d\n",
338 		       rdma_event_str(event->event), event->status);
339 		connect_error();
340 		break;
341 	case RDMA_CM_EVENT_DISCONNECTED:
342 		rdma_disconnect(cma_id);
343 		test.disconnects_left--;
344 		break;
345 	case RDMA_CM_EVENT_DEVICE_REMOVAL:
346 		/* Cleanup will occur after test completes. */
347 		break;
348 	default:
349 		break;
350 	}
351 	return ret;
352 }
353 
354 static void destroy_node(struct cmatest_node *node)
355 {
356 	if (!node->cma_id)
357 		return;
358 
359 	if (node->cma_id->qp)
360 		rdma_destroy_qp(node->cma_id);
361 
362 	if (node->cq[SEND_CQ_INDEX])
363 		ibv_destroy_cq(node->cq[SEND_CQ_INDEX]);
364 
365 	if (node->cq[RECV_CQ_INDEX])
366 		ibv_destroy_cq(node->cq[RECV_CQ_INDEX]);
367 
368 	if (node->mem) {
369 		ibv_dereg_mr(node->mr);
370 		free(node->mem);
371 	}
372 
373 	if (node->pd)
374 		ibv_dealloc_pd(node->pd);
375 
376 	/* Destroy the RDMA ID after all device resources */
377 	rdma_destroy_id(node->cma_id);
378 }
379 
380 static int alloc_nodes(void)
381 {
382 	int ret, i;
383 
384 	test.nodes = malloc(sizeof *test.nodes * connections);
385 	if (!test.nodes) {
386 		printf("cmatose: unable to allocate memory for test nodes\n");
387 		return -ENOMEM;
388 	}
389 	memset(test.nodes, 0, sizeof *test.nodes * connections);
390 
391 	for (i = 0; i < connections; i++) {
392 		test.nodes[i].id = i;
393 		if (dst_addr) {
394 			ret = rdma_create_id(test.channel,
395 					     &test.nodes[i].cma_id,
396 					     &test.nodes[i], RDMA_PS_TCP);
397 			if (ret)
398 				goto err;
399 		}
400 	}
401 	return 0;
402 err:
403 	while (--i >= 0)
404 		rdma_destroy_id(test.nodes[i].cma_id);
405 	free(test.nodes);
406 	return ret;
407 }
408 
409 static void destroy_nodes(void)
410 {
411 	int i;
412 
413 	for (i = 0; i < connections; i++)
414 		destroy_node(&test.nodes[i]);
415 	free(test.nodes);
416 }
417 
418 static int poll_cqs(enum CQ_INDEX index)
419 {
420 	struct ibv_wc wc[8];
421 	int done, i, ret;
422 
423 	for (i = 0; i < connections; i++) {
424 		if (!test.nodes[i].connected)
425 			continue;
426 
427 		for (done = 0; done < message_count; done += ret) {
428 			ret = ibv_poll_cq(test.nodes[i].cq[index], 8, wc);
429 			if (ret < 0) {
430 				printf("cmatose: failed polling CQ: %d\n", ret);
431 				return ret;
432 			}
433 		}
434 	}
435 	return 0;
436 }
437 
438 static int connect_events(void)
439 {
440 	struct rdma_cm_event *event;
441 	int err = 0, ret = 0;
442 
443 	while (test.connects_left && !err) {
444 		err = rdma_get_cm_event(test.channel, &event);
445 		if (!err) {
446 			cma_handler(event->id, event);
447 			rdma_ack_cm_event(event);
448 		} else {
449 			perror("cmatose: failure in rdma_get_cm_event in connect events");
450 			ret = errno;
451 		}
452 	}
453 
454 	return ret;
455 }
456 
457 static int disconnect_events(void)
458 {
459 	struct rdma_cm_event *event;
460 	int err = 0, ret = 0;
461 
462 	while (test.disconnects_left && !err) {
463 		err = rdma_get_cm_event(test.channel, &event);
464 		if (!err) {
465 			cma_handler(event->id, event);
466 			rdma_ack_cm_event(event);
467 		} else {
468 			perror("cmatose: failure in rdma_get_cm_event in disconnect events");
469 			ret = errno;
470 		}
471 	}
472 
473 	return ret;
474 }
475 
476 static int migrate_channel(struct rdma_cm_id *listen_id)
477 {
478 	struct rdma_event_channel *channel;
479 	int i, ret;
480 
481 	printf("migrating to new event channel\n");
482 
483 	channel = rdma_create_event_channel();
484 	if (!channel) {
485 		perror("cmatose: failed to create event channel");
486 		return -1;
487 	}
488 
489 	ret = 0;
490 	if (listen_id)
491 		ret = rdma_migrate_id(listen_id, channel);
492 
493 	for (i = 0; i < connections && !ret; i++)
494 		ret = rdma_migrate_id(test.nodes[i].cma_id, channel);
495 
496 	if (!ret) {
497 		rdma_destroy_event_channel(test.channel);
498 		test.channel = channel;
499 	} else
500 		perror("cmatose: failure migrating to channel");
501 
502 	return ret;
503 }
504 
505 static int get_addr(char *dst, struct sockaddr *addr)
506 {
507 	struct addrinfo *res;
508 	int ret;
509 
510 	ret = getaddrinfo(dst, NULL, NULL, &res);
511 	if (ret) {
512 		printf("getaddrinfo failed - invalid hostname or IP address\n");
513 		return ret;
514 	}
515 
516 	if (res->ai_family == PF_INET)
517 		memcpy(addr, res->ai_addr, sizeof(struct sockaddr_in));
518 	else if (res->ai_family == PF_INET6)
519 		memcpy(addr, res->ai_addr, sizeof(struct sockaddr_in6));
520 	else
521 		ret = -1;
522 
523         freeaddrinfo(res);
524         return ret;
525 }
526 
527 static int run_server(void)
528 {
529 	struct rdma_cm_id *listen_id;
530 	int i, ret;
531 
532 	printf("cmatose: starting server\n");
533 	ret = rdma_create_id(test.channel, &listen_id, &test, RDMA_PS_TCP);
534 	if (ret) {
535 		perror("cmatose: listen request failed");
536 		return ret;
537 	}
538 
539 	if (src_addr) {
540 		ret = get_addr(src_addr, &test.addr.src_addr);
541 		if (ret)
542 			goto out;
543 		if (test.addr.src_addr.sa_family == AF_INET)
544 			((struct sockaddr_in *) &test.addr.src_addr)->sin_port = port;
545 		else
546 			((struct sockaddr_in6 *) &test.addr.src_addr)->sin6_port = port;
547 
548 	} else {
549 		test.addr.src_addr.sa_family = PF_INET;
550 		((struct sockaddr_in *) &test.addr.src_addr)->sin_port = port;
551 	}
552 
553 	ret = rdma_bind_addr(listen_id, &test.addr.src_addr);
554 
555 	if (ret) {
556 		perror("cmatose: bind address failed");
557 		goto out;
558 	}
559 
560 	ret = rdma_listen(listen_id, 0);
561 	if (ret) {
562 		perror("cmatose: failure trying to listen");
563 		goto out;
564 	}
565 
566 	ret = connect_events();
567 	if (ret)
568 		goto out;
569 
570 	if (message_count) {
571 		printf("initiating data transfers\n");
572 		for (i = 0; i < connections; i++) {
573 			ret = post_sends(&test.nodes[i]);
574 			if (ret)
575 				goto out;
576 		}
577 
578 		printf("completing sends\n");
579 		ret = poll_cqs(SEND_CQ_INDEX);
580 		if (ret)
581 			goto out;
582 
583 		printf("receiving data transfers\n");
584 		ret = poll_cqs(RECV_CQ_INDEX);
585 		if (ret)
586 			goto out;
587 		printf("data transfers complete\n");
588 
589 	}
590 
591 	if (migrate) {
592 		ret = migrate_channel(listen_id);
593 		if (ret)
594 			goto out;
595 	}
596 
597 	printf("cmatose: disconnecting\n");
598 	for (i = 0; i < connections; i++) {
599 		if (!test.nodes[i].connected)
600 			continue;
601 
602 		test.nodes[i].connected = 0;
603 		rdma_disconnect(test.nodes[i].cma_id);
604 	}
605 
606 	ret = disconnect_events();
607 
608  	printf("disconnected\n");
609 
610 out:
611 	rdma_destroy_id(listen_id);
612 	return ret;
613 }
614 
615 static int run_client(void)
616 {
617 	int i, ret, ret2;
618 
619 	printf("cmatose: starting client\n");
620 	if (src_addr) {
621 		ret = get_addr(src_addr, &test.addr.src_addr);
622 		if (ret)
623 			return ret;
624 	}
625 
626 	ret = get_addr(dst_addr, &test.addr.dst_addr);
627 	if (ret)
628 		return ret;
629 
630 	if (test.addr.dst_addr.sa_family == AF_INET)
631 		((struct sockaddr_in *) &test.addr.dst_addr)->sin_port = port;
632 	else
633 		((struct sockaddr_in6 *) &test.addr.dst_addr)->sin6_port = port;
634 
635 	printf("cmatose: connecting\n");
636 	for (i = 0; i < connections; i++) {
637 		ret = rdma_resolve_addr(test.nodes[i].cma_id,
638 					src_addr ? &test.addr.src_addr : NULL,
639 					&test.addr.dst_addr, 2000);
640 		if (ret) {
641 			perror("cmatose: failure getting addr");
642 			connect_error();
643 			return ret;
644 		}
645 	}
646 
647 	ret = connect_events();
648 	if (ret)
649 		goto disc;
650 
651 	if (message_count) {
652 		printf("receiving data transfers\n");
653 		ret = poll_cqs(RECV_CQ_INDEX);
654 		if (ret)
655 			goto disc;
656 
657 		printf("sending replies\n");
658 		for (i = 0; i < connections; i++) {
659 			ret = post_sends(&test.nodes[i]);
660 			if (ret)
661 				goto disc;
662 		}
663 
664 		printf("data transfers complete\n");
665 	}
666 
667 	ret = 0;
668 
669 	if (migrate) {
670 		ret = migrate_channel(NULL);
671 		if (ret)
672 			goto out;
673 	}
674 disc:
675 	ret2 = disconnect_events();
676 	if (ret2)
677 		ret = ret2;
678 out:
679 	return ret;
680 }
681 
682 int main(int argc, char **argv)
683 {
684 	int op, ret;
685 
686 	while ((op = getopt(argc, argv, "s:b:c:C:S:t:p:m")) != -1) {
687 		switch (op) {
688 		case 's':
689 			dst_addr = optarg;
690 			break;
691 		case 'b':
692 			src_addr = optarg;
693 			break;
694 		case 'c':
695 			connections = atoi(optarg);
696 			break;
697 		case 'C':
698 			message_count = atoi(optarg);
699 			break;
700 		case 'S':
701 			message_size = atoi(optarg);
702 			break;
703 		case 't':
704 			set_tos = 1;
705 			tos = (uint8_t) atoi(optarg);
706 			break;
707 		case 'p':
708 			port = atoi(optarg);
709 			break;
710 		case 'm':
711 			migrate = 1;
712 			break;
713 		default:
714 			printf("usage: %s\n", argv[0]);
715 			printf("\t[-s server_address]\n");
716 			printf("\t[-b bind_address]\n");
717 			printf("\t[-c connections]\n");
718 			printf("\t[-C message_count]\n");
719 			printf("\t[-S message_size]\n");
720 			printf("\t[-t type_of_service]\n");
721 			printf("\t[-p port_number]\n");
722 			printf("\t[-m(igrate)]\n");
723 			exit(1);
724 		}
725 	}
726 
727 	test.connects_left = connections;
728 	test.disconnects_left = connections;
729 
730 	test.channel = rdma_create_event_channel();
731 	if (!test.channel) {
732 		printf("failed to create event channel\n");
733 		exit(1);
734 	}
735 
736 	if (alloc_nodes())
737 		exit(1);
738 
739 	if (dst_addr)
740 		ret = run_client();
741 	else
742 		ret = run_server();
743 
744 	printf("test complete\n");
745 	destroy_nodes();
746 	rdma_destroy_event_channel(test.channel);
747 
748 	printf("return status %d\n", ret);
749 	return ret;
750 }
751