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
48struct cmatest_node {
49	int			id;
50	struct rdma_cm_id	*cma_id;
51	int			connected;
52	struct ibv_pd		*pd;
53	struct ibv_cq		*cq;
54	struct ibv_mr		*mr;
55	struct ibv_ah		*ah;
56	uint32_t		remote_qpn;
57	uint32_t		remote_qkey;
58	void			*mem;
59};
60
61struct cmatest {
62	struct rdma_event_channel *channel;
63	struct cmatest_node	*nodes;
64	int			conn_index;
65	int			connects_left;
66
67	struct sockaddr_in	dst_in;
68	struct sockaddr		*dst_addr;
69	struct sockaddr_in	src_in;
70	struct sockaddr		*src_addr;
71};
72
73static struct cmatest test;
74static int connections = 1;
75static int message_size = 100;
76static int message_count = 10;
77static uint16_t port = 7174;
78static uint8_t set_tos = 0;
79static uint8_t tos;
80static char *dst_addr;
81static char *src_addr;
82static enum rdma_port_space port_space = RDMA_PS_UDP;
83
84static int create_message(struct cmatest_node *node)
85{
86	if (!message_size)
87		message_count = 0;
88
89	if (!message_count)
90		return 0;
91
92	node->mem = malloc(message_size + sizeof(struct ibv_grh));
93	if (!node->mem) {
94		printf("failed message allocation\n");
95		return -1;
96	}
97	node->mr = ibv_reg_mr(node->pd, node->mem,
98			      message_size + sizeof(struct ibv_grh),
99			      IBV_ACCESS_LOCAL_WRITE);
100	if (!node->mr) {
101		printf("failed to reg MR\n");
102		goto err;
103	}
104	return 0;
105err:
106	free(node->mem);
107	return -1;
108}
109
110static int verify_test_params(struct cmatest_node *node)
111{
112	struct ibv_port_attr port_attr;
113	int ret;
114
115	ret = ibv_query_port(node->cma_id->verbs, node->cma_id->port_num,
116			     &port_attr);
117	if (ret)
118		return ret;
119
120	if (message_count && message_size > (1 << (port_attr.active_mtu + 7))) {
121		printf("udaddy: message_size %d is larger than active mtu %d\n",
122		       message_size, 1 << (port_attr.active_mtu + 7));
123		return -EINVAL;
124	}
125
126	return 0;
127}
128
129static int init_node(struct cmatest_node *node)
130{
131	struct ibv_qp_init_attr init_qp_attr;
132	int cqe, ret;
133
134	node->pd = ibv_alloc_pd(node->cma_id->verbs);
135	if (!node->pd) {
136		ret = -ENOMEM;
137		printf("udaddy: unable to allocate PD\n");
138		goto out;
139	}
140
141	cqe = message_count ? message_count * 2 : 2;
142	node->cq = ibv_create_cq(node->cma_id->verbs, cqe, node, 0, 0);
143	if (!node->cq) {
144		ret = -ENOMEM;
145		printf("udaddy: unable to create CQ\n");
146		goto out;
147	}
148
149	memset(&init_qp_attr, 0, sizeof init_qp_attr);
150	init_qp_attr.cap.max_send_wr = message_count ? message_count : 1;
151	init_qp_attr.cap.max_recv_wr = message_count ? message_count : 1;
152	init_qp_attr.cap.max_send_sge = 1;
153	init_qp_attr.cap.max_recv_sge = 1;
154	init_qp_attr.qp_context = node;
155	init_qp_attr.sq_sig_all = 0;
156	init_qp_attr.qp_type = IBV_QPT_UD;
157	init_qp_attr.send_cq = node->cq;
158	init_qp_attr.recv_cq = node->cq;
159	ret = rdma_create_qp(node->cma_id, node->pd, &init_qp_attr);
160	if (ret) {
161		perror("udaddy: unable to create QP");
162		goto out;
163	}
164
165	ret = create_message(node);
166	if (ret) {
167		printf("udaddy: failed to create messages: %d\n", ret);
168		goto out;
169	}
170out:
171	return ret;
172}
173
174static int post_recvs(struct cmatest_node *node)
175{
176	struct ibv_recv_wr recv_wr, *recv_failure;
177	struct ibv_sge sge;
178	int i, ret = 0;
179
180	if (!message_count)
181		return 0;
182
183	recv_wr.next = NULL;
184	recv_wr.sg_list = &sge;
185	recv_wr.num_sge = 1;
186	recv_wr.wr_id = (uintptr_t) node;
187
188	sge.length = message_size + sizeof(struct ibv_grh);
189	sge.lkey = node->mr->lkey;
190	sge.addr = (uintptr_t) node->mem;
191
192	for (i = 0; i < message_count && !ret; i++ ) {
193		ret = ibv_post_recv(node->cma_id->qp, &recv_wr, &recv_failure);
194		if (ret) {
195			printf("failed to post receives: %d\n", ret);
196			break;
197		}
198	}
199	return ret;
200}
201
202static int post_sends(struct cmatest_node *node, int signal_flag)
203{
204	struct ibv_send_wr send_wr, *bad_send_wr;
205	struct ibv_sge sge;
206	int i, ret = 0;
207
208	if (!node->connected || !message_count)
209		return 0;
210
211	send_wr.next = NULL;
212	send_wr.sg_list = &sge;
213	send_wr.num_sge = 1;
214	send_wr.opcode = IBV_WR_SEND_WITH_IMM;
215	send_wr.send_flags = signal_flag;
216	send_wr.wr_id = (unsigned long)node;
217	send_wr.imm_data = htonl(node->cma_id->qp->qp_num);
218
219	send_wr.wr.ud.ah = node->ah;
220	send_wr.wr.ud.remote_qpn = node->remote_qpn;
221	send_wr.wr.ud.remote_qkey = node->remote_qkey;
222
223	sge.length = message_size;
224	sge.lkey = node->mr->lkey;
225	sge.addr = (uintptr_t) node->mem;
226
227	for (i = 0; i < message_count && !ret; i++) {
228		ret = ibv_post_send(node->cma_id->qp, &send_wr, &bad_send_wr);
229		if (ret)
230			printf("failed to post sends: %d\n", ret);
231	}
232	return ret;
233}
234
235static void connect_error(void)
236{
237	test.connects_left--;
238}
239
240static int addr_handler(struct cmatest_node *node)
241{
242	int ret;
243
244	if (set_tos) {
245		ret = rdma_set_option(node->cma_id, RDMA_OPTION_ID,
246				      RDMA_OPTION_ID_TOS, &tos, sizeof tos);
247		if (ret)
248			perror("udaddy: set TOS option failed");
249	}
250
251	ret = rdma_resolve_route(node->cma_id, 2000);
252	if (ret) {
253		perror("udaddy: resolve route failed");
254		connect_error();
255	}
256	return ret;
257}
258
259static int route_handler(struct cmatest_node *node)
260{
261	struct rdma_conn_param conn_param;
262	int ret;
263
264	ret = verify_test_params(node);
265	if (ret)
266		goto err;
267
268	ret = init_node(node);
269	if (ret)
270		goto err;
271
272	ret = post_recvs(node);
273	if (ret)
274		goto err;
275
276	memset(&conn_param, 0, sizeof conn_param);
277	ret = rdma_connect(node->cma_id, &conn_param);
278	if (ret) {
279		perror("udaddy: failure connecting");
280		goto err;
281	}
282	return 0;
283err:
284	connect_error();
285	return ret;
286}
287
288static int connect_handler(struct rdma_cm_id *cma_id)
289{
290	struct cmatest_node *node;
291	struct rdma_conn_param conn_param;
292	int ret;
293
294	if (test.conn_index == connections) {
295		ret = -ENOMEM;
296		goto err1;
297	}
298	node = &test.nodes[test.conn_index++];
299
300	node->cma_id = cma_id;
301	cma_id->context = node;
302
303	ret = verify_test_params(node);
304	if (ret)
305		goto err2;
306
307	ret = init_node(node);
308	if (ret)
309		goto err2;
310
311	ret = post_recvs(node);
312	if (ret)
313		goto err2;
314
315	memset(&conn_param, 0, sizeof conn_param);
316	conn_param.qp_num = node->cma_id->qp->qp_num;
317	ret = rdma_accept(node->cma_id, &conn_param);
318	if (ret) {
319		perror("udaddy: failure accepting");
320		goto err2;
321	}
322	node->connected = 1;
323	test.connects_left--;
324	return 0;
325
326err2:
327	node->cma_id = NULL;
328	connect_error();
329err1:
330	printf("udaddy: failing connection request\n");
331	rdma_reject(cma_id, NULL, 0);
332	return ret;
333}
334
335static int resolved_handler(struct cmatest_node *node,
336			    struct rdma_cm_event *event)
337{
338	node->remote_qpn = event->param.ud.qp_num;
339	node->remote_qkey = event->param.ud.qkey;
340	node->ah = ibv_create_ah(node->pd, &event->param.ud.ah_attr);
341	if (!node->ah) {
342		printf("udaddy: failure creating address handle\n");
343		goto err;
344	}
345
346	node->connected = 1;
347	test.connects_left--;
348	return 0;
349err:
350	connect_error();
351	return -1;
352}
353
354static int cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
355{
356	int ret = 0;
357
358	switch (event->event) {
359	case RDMA_CM_EVENT_ADDR_RESOLVED:
360		ret = addr_handler(cma_id->context);
361		break;
362	case RDMA_CM_EVENT_ROUTE_RESOLVED:
363		ret = route_handler(cma_id->context);
364		break;
365	case RDMA_CM_EVENT_CONNECT_REQUEST:
366		ret = connect_handler(cma_id);
367		break;
368	case RDMA_CM_EVENT_ESTABLISHED:
369		ret = resolved_handler(cma_id->context, event);
370		break;
371	case RDMA_CM_EVENT_ADDR_ERROR:
372	case RDMA_CM_EVENT_ROUTE_ERROR:
373	case RDMA_CM_EVENT_CONNECT_ERROR:
374	case RDMA_CM_EVENT_UNREACHABLE:
375	case RDMA_CM_EVENT_REJECTED:
376		printf("udaddy: event: %s, error: %d\n",
377		       rdma_event_str(event->event), event->status);
378		connect_error();
379		ret = event->status;
380		break;
381	case RDMA_CM_EVENT_DEVICE_REMOVAL:
382		/* Cleanup will occur after test completes. */
383		break;
384	default:
385		break;
386	}
387	return ret;
388}
389
390static void destroy_node(struct cmatest_node *node)
391{
392	if (!node->cma_id)
393		return;
394
395	if (node->ah)
396		ibv_destroy_ah(node->ah);
397
398	if (node->cma_id->qp)
399		rdma_destroy_qp(node->cma_id);
400
401	if (node->cq)
402		ibv_destroy_cq(node->cq);
403
404	if (node->mem) {
405		ibv_dereg_mr(node->mr);
406		free(node->mem);
407	}
408
409	if (node->pd)
410		ibv_dealloc_pd(node->pd);
411
412	/* Destroy the RDMA ID after all device resources */
413	rdma_destroy_id(node->cma_id);
414}
415
416static int alloc_nodes(void)
417{
418	int ret, i;
419
420	test.nodes = malloc(sizeof *test.nodes * connections);
421	if (!test.nodes) {
422		printf("udaddy: unable to allocate memory for test nodes\n");
423		return -ENOMEM;
424	}
425	memset(test.nodes, 0, sizeof *test.nodes * connections);
426
427	for (i = 0; i < connections; i++) {
428		test.nodes[i].id = i;
429		if (dst_addr) {
430			ret = rdma_create_id(test.channel,
431					     &test.nodes[i].cma_id,
432					     &test.nodes[i], port_space);
433			if (ret)
434				goto err;
435		}
436	}
437	return 0;
438err:
439	while (--i >= 0)
440		rdma_destroy_id(test.nodes[i].cma_id);
441	free(test.nodes);
442	return ret;
443}
444
445static void destroy_nodes(void)
446{
447	int i;
448
449	for (i = 0; i < connections; i++)
450		destroy_node(&test.nodes[i]);
451	free(test.nodes);
452}
453
454static void create_reply_ah(struct cmatest_node *node, struct ibv_wc *wc)
455{
456	struct ibv_qp_attr attr;
457	struct ibv_qp_init_attr init_attr;
458
459	node->ah = ibv_create_ah_from_wc(node->pd, wc, node->mem,
460					 node->cma_id->port_num);
461	node->remote_qpn = ntohl(wc->imm_data);
462
463	ibv_query_qp(node->cma_id->qp, &attr, IBV_QP_QKEY, &init_attr);
464	node->remote_qkey = attr.qkey;
465}
466
467static int poll_cqs(void)
468{
469	struct ibv_wc wc[8];
470	int done, i, ret;
471
472	for (i = 0; i < connections; i++) {
473		if (!test.nodes[i].connected)
474			continue;
475
476		for (done = 0; done < message_count; done += ret) {
477			ret = ibv_poll_cq(test.nodes[i].cq, 8, wc);
478			if (ret < 0) {
479				printf("udaddy: failed polling CQ: %d\n", ret);
480				return ret;
481			}
482
483			if (ret && !test.nodes[i].ah)
484				create_reply_ah(&test.nodes[i], wc);
485		}
486	}
487	return 0;
488}
489
490static int connect_events(void)
491{
492	struct rdma_cm_event *event;
493	int ret = 0;
494
495	while (test.connects_left && !ret) {
496		ret = rdma_get_cm_event(test.channel, &event);
497		if (!ret) {
498			ret = cma_handler(event->id, event);
499			rdma_ack_cm_event(event);
500		}
501	}
502	return ret;
503}
504
505static int get_addr(char *dst, struct sockaddr_in *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		ret = -1;
518		goto out;
519	}
520
521	*addr = *(struct sockaddr_in *) res->ai_addr;
522out:
523	freeaddrinfo(res);
524	return ret;
525}
526
527static int run_server(void)
528{
529	struct rdma_cm_id *listen_id;
530	int i, ret;
531
532	printf("udaddy: starting server\n");
533	ret = rdma_create_id(test.channel, &listen_id, &test, port_space);
534	if (ret) {
535		perror("udaddy: listen request failed");
536		return ret;
537	}
538
539	if (src_addr) {
540		ret = get_addr(src_addr, &test.src_in);
541		if (ret)
542			goto out;
543	} else
544		test.src_in.sin_family = PF_INET;
545
546	test.src_in.sin_port = port;
547	ret = rdma_bind_addr(listen_id, test.src_addr);
548	if (ret) {
549		perror("udaddy: bind address failed");
550		return ret;
551	}
552
553	ret = rdma_listen(listen_id, 0);
554	if (ret) {
555		perror("udaddy: failure trying to listen");
556		goto out;
557	}
558
559	connect_events();
560
561	if (message_count) {
562		printf("receiving data transfers\n");
563		ret = poll_cqs();
564		if (ret)
565			goto out;
566
567		printf("sending replies\n");
568		for (i = 0; i < connections; i++) {
569			ret = post_sends(&test.nodes[i], IBV_SEND_SIGNALED);
570			if (ret)
571				goto out;
572		}
573
574		ret = poll_cqs();
575		if (ret)
576			goto out;
577		printf("data transfers complete\n");
578	}
579out:
580	rdma_destroy_id(listen_id);
581	return ret;
582}
583
584static int run_client(void)
585{
586	int i, ret;
587
588	printf("udaddy: starting client\n");
589	if (src_addr) {
590		ret = get_addr(src_addr, &test.src_in);
591		if (ret)
592			return ret;
593	}
594
595	ret = get_addr(dst_addr, &test.dst_in);
596	if (ret)
597		return ret;
598
599	test.dst_in.sin_port = port;
600
601	printf("udaddy: connecting\n");
602	for (i = 0; i < connections; i++) {
603		ret = rdma_resolve_addr(test.nodes[i].cma_id,
604					src_addr ? test.src_addr : NULL,
605					test.dst_addr, 2000);
606		if (ret) {
607			perror("udaddy: failure getting addr");
608			connect_error();
609			return ret;
610		}
611	}
612
613	ret = connect_events();
614	if (ret)
615		goto out;
616
617	if (message_count) {
618		printf("initiating data transfers\n");
619		for (i = 0; i < connections; i++) {
620			ret = post_sends(&test.nodes[i], 0);
621			if (ret)
622				goto out;
623		}
624		printf("receiving data transfers\n");
625		ret = poll_cqs();
626		if (ret)
627			goto out;
628
629		printf("data transfers complete\n");
630	}
631out:
632	return ret;
633}
634
635int main(int argc, char **argv)
636{
637	int op, ret;
638
639	while ((op = getopt(argc, argv, "s:b:c:C:S:t:p:")) != -1) {
640		switch (op) {
641		case 's':
642			dst_addr = optarg;
643			break;
644		case 'b':
645			src_addr = optarg;
646			break;
647		case 'c':
648			connections = atoi(optarg);
649			break;
650		case 'C':
651			message_count = atoi(optarg);
652			break;
653		case 'S':
654			message_size = atoi(optarg);
655			break;
656		case 't':
657			set_tos = 1;
658			tos = (uint8_t) atoi(optarg);
659			break;
660		case 'p':
661			port_space = strtol(optarg, NULL, 0);
662			break;
663		default:
664			printf("usage: %s\n", argv[0]);
665			printf("\t[-s server_address]\n");
666			printf("\t[-b bind_address]\n");
667			printf("\t[-c connections]\n");
668			printf("\t[-C message_count]\n");
669			printf("\t[-S message_size]\n");
670			printf("\t[-t type_of_service]\n");
671			printf("\t[-p port_space - %#x for UDP (default), "
672			       "%#x for IPOIB]\n", RDMA_PS_UDP, RDMA_PS_IPOIB);
673			exit(1);
674		}
675	}
676
677	test.dst_addr = (struct sockaddr *) &test.dst_in;
678	test.src_addr = (struct sockaddr *) &test.src_in;
679	test.connects_left = connections;
680
681	test.channel = rdma_create_event_channel();
682	if (!test.channel) {
683		perror("failed to create event channel");
684		exit(1);
685	}
686
687	if (alloc_nodes())
688		exit(1);
689
690	if (dst_addr)
691		ret = run_client();
692	else
693		ret = run_server();
694
695	printf("test complete\n");
696	destroy_nodes();
697	rdma_destroy_event_channel(test.channel);
698
699	printf("return status %d\n", ret);
700	return ret;
701}
702