1219820Sjeff/*
2219820Sjeff * Copyright (c) 2005 Topspin Communications.  All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff */
32219820Sjeff
33219820Sjeff#if HAVE_CONFIG_H
34219820Sjeff#  include <config.h>
35219820Sjeff#endif /* HAVE_CONFIG_H */
36219820Sjeff
37219820Sjeff#include <stdio.h>
38219820Sjeff#include <stdlib.h>
39219820Sjeff#include <unistd.h>
40219820Sjeff#include <string.h>
41219820Sjeff#include <sys/types.h>
42219820Sjeff#include <sys/socket.h>
43219820Sjeff#include <sys/time.h>
44219820Sjeff#include <netdb.h>
45219820Sjeff#include <stdlib.h>
46219820Sjeff#include <getopt.h>
47219820Sjeff#include <arpa/inet.h>
48219820Sjeff#include <time.h>
49219820Sjeff
50219820Sjeff#include "pingpong.h"
51219820Sjeff
52219820Sjeffenum {
53219820Sjeff	PINGPONG_RECV_WRID = 1,
54219820Sjeff	PINGPONG_SEND_WRID = 2,
55219820Sjeff};
56219820Sjeff
57219820Sjeffstatic int page_size;
58219820Sjeff
59219820Sjeffstruct pingpong_context {
60219820Sjeff	struct ibv_context	*context;
61219820Sjeff	struct ibv_comp_channel *channel;
62219820Sjeff	struct ibv_pd		*pd;
63219820Sjeff	struct ibv_mr		*mr;
64219820Sjeff	struct ibv_cq		*cq;
65219820Sjeff	struct ibv_qp		*qp;
66219820Sjeff	void			*buf;
67219820Sjeff	int			 size;
68219820Sjeff	int			 rx_depth;
69219820Sjeff	int			 pending;
70219820Sjeff	struct ibv_port_attr     portinfo;
71219820Sjeff};
72219820Sjeff
73219820Sjeffstruct pingpong_dest {
74219820Sjeff	int lid;
75219820Sjeff	int qpn;
76219820Sjeff	int psn;
77219820Sjeff	union ibv_gid gid;
78219820Sjeff};
79219820Sjeff
80219820Sjeffstatic int pp_connect_ctx(struct pingpong_context *ctx, int port, int my_psn,
81219820Sjeff			  enum ibv_mtu mtu, int sl,
82219820Sjeff			  struct pingpong_dest *dest, int sgid_idx)
83219820Sjeff{
84219820Sjeff	struct ibv_qp_attr attr = {
85219820Sjeff		.qp_state		= IBV_QPS_RTR,
86219820Sjeff		.path_mtu		= mtu,
87219820Sjeff		.dest_qp_num		= dest->qpn,
88219820Sjeff		.rq_psn			= dest->psn,
89219820Sjeff		.max_dest_rd_atomic	= 1,
90219820Sjeff		.min_rnr_timer		= 12,
91219820Sjeff		.ah_attr		= {
92219820Sjeff			.is_global	= 0,
93219820Sjeff			.dlid		= dest->lid,
94219820Sjeff			.sl		= sl,
95219820Sjeff			.src_path_bits	= 0,
96219820Sjeff			.port_num	= port
97219820Sjeff		}
98219820Sjeff	};
99219820Sjeff
100219820Sjeff	if (dest->gid.global.interface_id) {
101219820Sjeff		attr.ah_attr.is_global = 1;
102219820Sjeff		attr.ah_attr.grh.hop_limit = 1;
103219820Sjeff		attr.ah_attr.grh.dgid = dest->gid;
104219820Sjeff		attr.ah_attr.grh.sgid_index = sgid_idx;
105219820Sjeff	}
106219820Sjeff	if (ibv_modify_qp(ctx->qp, &attr,
107219820Sjeff			  IBV_QP_STATE              |
108219820Sjeff			  IBV_QP_AV                 |
109219820Sjeff			  IBV_QP_PATH_MTU           |
110219820Sjeff			  IBV_QP_DEST_QPN           |
111219820Sjeff			  IBV_QP_RQ_PSN             |
112219820Sjeff			  IBV_QP_MAX_DEST_RD_ATOMIC |
113219820Sjeff			  IBV_QP_MIN_RNR_TIMER)) {
114219820Sjeff		fprintf(stderr, "Failed to modify QP to RTR\n");
115219820Sjeff		return 1;
116219820Sjeff	}
117219820Sjeff
118219820Sjeff	attr.qp_state	    = IBV_QPS_RTS;
119219820Sjeff	attr.timeout	    = 14;
120219820Sjeff	attr.retry_cnt	    = 7;
121219820Sjeff	attr.rnr_retry	    = 7;
122219820Sjeff	attr.sq_psn	    = my_psn;
123219820Sjeff	attr.max_rd_atomic  = 1;
124219820Sjeff	if (ibv_modify_qp(ctx->qp, &attr,
125219820Sjeff			  IBV_QP_STATE              |
126219820Sjeff			  IBV_QP_TIMEOUT            |
127219820Sjeff			  IBV_QP_RETRY_CNT          |
128219820Sjeff			  IBV_QP_RNR_RETRY          |
129219820Sjeff			  IBV_QP_SQ_PSN             |
130219820Sjeff			  IBV_QP_MAX_QP_RD_ATOMIC)) {
131219820Sjeff		fprintf(stderr, "Failed to modify QP to RTS\n");
132219820Sjeff		return 1;
133219820Sjeff	}
134219820Sjeff
135219820Sjeff	return 0;
136219820Sjeff}
137219820Sjeff
138219820Sjeffstatic struct pingpong_dest *pp_client_exch_dest(const char *servername, int port,
139219820Sjeff						 const struct pingpong_dest *my_dest)
140219820Sjeff{
141219820Sjeff	struct addrinfo *res, *t;
142219820Sjeff	struct addrinfo hints = {
143219820Sjeff		.ai_family   = AF_INET,
144219820Sjeff		.ai_socktype = SOCK_STREAM
145219820Sjeff	};
146219820Sjeff	char *service;
147219820Sjeff	char msg[sizeof "0000:000000:000000:00000000000000000000000000000000"];
148219820Sjeff	int n;
149219820Sjeff	int sockfd = -1;
150219820Sjeff	struct pingpong_dest *rem_dest = NULL;
151219820Sjeff	char gid[33];
152219820Sjeff
153219820Sjeff	if (asprintf(&service, "%d", port) < 0)
154219820Sjeff		return NULL;
155219820Sjeff
156219820Sjeff	n = getaddrinfo(servername, service, &hints, &res);
157219820Sjeff
158219820Sjeff	if (n < 0) {
159219820Sjeff		fprintf(stderr, "%s for %s:%d\n", gai_strerror(n), servername, port);
160219820Sjeff		free(service);
161219820Sjeff		return NULL;
162219820Sjeff	}
163219820Sjeff
164219820Sjeff	for (t = res; t; t = t->ai_next) {
165219820Sjeff		sockfd = socket(t->ai_family, t->ai_socktype, t->ai_protocol);
166219820Sjeff		if (sockfd >= 0) {
167219820Sjeff			if (!connect(sockfd, t->ai_addr, t->ai_addrlen))
168219820Sjeff				break;
169219820Sjeff			close(sockfd);
170219820Sjeff			sockfd = -1;
171219820Sjeff		}
172219820Sjeff	}
173219820Sjeff
174219820Sjeff	freeaddrinfo(res);
175219820Sjeff	free(service);
176219820Sjeff
177219820Sjeff	if (sockfd < 0) {
178219820Sjeff		fprintf(stderr, "Couldn't connect to %s:%d\n", servername, port);
179219820Sjeff		return NULL;
180219820Sjeff	}
181219820Sjeff
182219820Sjeff	gid_to_wire_gid(&my_dest->gid, gid);
183219820Sjeff	sprintf(msg, "%04x:%06x:%06x:%s", my_dest->lid, my_dest->qpn, my_dest->psn, gid);
184219820Sjeff	if (write(sockfd, msg, sizeof msg) != sizeof msg) {
185219820Sjeff		fprintf(stderr, "Couldn't send local address\n");
186219820Sjeff		goto out;
187219820Sjeff	}
188219820Sjeff
189219820Sjeff	if (read(sockfd, msg, sizeof msg) != sizeof msg) {
190219820Sjeff		perror("client read");
191219820Sjeff		fprintf(stderr, "Couldn't read remote address\n");
192219820Sjeff		goto out;
193219820Sjeff	}
194219820Sjeff
195219820Sjeff	write(sockfd, "done", sizeof "done");
196219820Sjeff
197219820Sjeff	rem_dest = malloc(sizeof *rem_dest);
198219820Sjeff	if (!rem_dest)
199219820Sjeff		goto out;
200219820Sjeff
201219820Sjeff	sscanf(msg, "%x:%x:%x:%s", &rem_dest->lid, &rem_dest->qpn, &rem_dest->psn, gid);
202219820Sjeff	wire_gid_to_gid(gid, &rem_dest->gid);
203219820Sjeff
204219820Sjeffout:
205219820Sjeff	close(sockfd);
206219820Sjeff	return rem_dest;
207219820Sjeff}
208219820Sjeff
209219820Sjeffstatic struct pingpong_dest *pp_server_exch_dest(struct pingpong_context *ctx,
210219820Sjeff						 int ib_port, enum ibv_mtu mtu,
211219820Sjeff						 int port, int sl,
212219820Sjeff						 const struct pingpong_dest *my_dest,
213219820Sjeff						 int sgid_idx)
214219820Sjeff{
215219820Sjeff	struct addrinfo *res, *t;
216219820Sjeff	struct addrinfo hints = {
217219820Sjeff		.ai_flags    = AI_PASSIVE,
218219820Sjeff		.ai_family   = AF_INET,
219219820Sjeff		.ai_socktype = SOCK_STREAM
220219820Sjeff	};
221219820Sjeff	char *service;
222219820Sjeff	char msg[sizeof "0000:000000:000000:00000000000000000000000000000000"];
223219820Sjeff	int n;
224219820Sjeff	int sockfd = -1, connfd;
225219820Sjeff	struct pingpong_dest *rem_dest = NULL;
226219820Sjeff	char gid[33];
227219820Sjeff
228219820Sjeff	if (asprintf(&service, "%d", port) < 0)
229219820Sjeff		return NULL;
230219820Sjeff
231219820Sjeff	n = getaddrinfo(NULL, service, &hints, &res);
232219820Sjeff
233219820Sjeff	if (n < 0) {
234219820Sjeff		fprintf(stderr, "%s for port %d\n", gai_strerror(n), port);
235219820Sjeff		free(service);
236219820Sjeff		return NULL;
237219820Sjeff	}
238219820Sjeff
239219820Sjeff	for (t = res; t; t = t->ai_next) {
240219820Sjeff		sockfd = socket(t->ai_family, t->ai_socktype, t->ai_protocol);
241219820Sjeff		if (sockfd >= 0) {
242219820Sjeff			n = 1;
243219820Sjeff
244219820Sjeff			setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof n);
245219820Sjeff
246219820Sjeff			if (!bind(sockfd, t->ai_addr, t->ai_addrlen))
247219820Sjeff				break;
248219820Sjeff			close(sockfd);
249219820Sjeff			sockfd = -1;
250219820Sjeff		}
251219820Sjeff	}
252219820Sjeff
253219820Sjeff	freeaddrinfo(res);
254219820Sjeff	free(service);
255219820Sjeff
256219820Sjeff	if (sockfd < 0) {
257219820Sjeff		fprintf(stderr, "Couldn't listen to port %d\n", port);
258219820Sjeff		return NULL;
259219820Sjeff	}
260219820Sjeff
261219820Sjeff	listen(sockfd, 1);
262219820Sjeff	connfd = accept(sockfd, NULL, 0);
263219820Sjeff	close(sockfd);
264219820Sjeff	if (connfd < 0) {
265219820Sjeff		fprintf(stderr, "accept() failed\n");
266219820Sjeff		return NULL;
267219820Sjeff	}
268219820Sjeff
269219820Sjeff	n = read(connfd, msg, sizeof msg);
270219820Sjeff	if (n != sizeof msg) {
271219820Sjeff		perror("server read");
272219820Sjeff		fprintf(stderr, "%d/%d: Couldn't read remote address\n", n, (int) sizeof msg);
273219820Sjeff		goto out;
274219820Sjeff	}
275219820Sjeff
276219820Sjeff	rem_dest = malloc(sizeof *rem_dest);
277219820Sjeff	if (!rem_dest)
278219820Sjeff		goto out;
279219820Sjeff
280219820Sjeff	sscanf(msg, "%x:%x:%x:%s", &rem_dest->lid, &rem_dest->qpn, &rem_dest->psn, gid);
281219820Sjeff	wire_gid_to_gid(gid, &rem_dest->gid);
282219820Sjeff
283219820Sjeff	if (pp_connect_ctx(ctx, ib_port, my_dest->psn, mtu, sl, rem_dest, sgid_idx)) {
284219820Sjeff		fprintf(stderr, "Couldn't connect to remote QP\n");
285219820Sjeff		free(rem_dest);
286219820Sjeff		rem_dest = NULL;
287219820Sjeff		goto out;
288219820Sjeff	}
289219820Sjeff
290219820Sjeff
291219820Sjeff	gid_to_wire_gid(&my_dest->gid, gid);
292219820Sjeff	sprintf(msg, "%04x:%06x:%06x:%s", my_dest->lid, my_dest->qpn, my_dest->psn, gid);
293219820Sjeff	if (write(connfd, msg, sizeof msg) != sizeof msg) {
294219820Sjeff		fprintf(stderr, "Couldn't send local address\n");
295219820Sjeff		free(rem_dest);
296219820Sjeff		rem_dest = NULL;
297219820Sjeff		goto out;
298219820Sjeff	}
299219820Sjeff
300219820Sjeff	read(connfd, msg, sizeof msg);
301219820Sjeff
302219820Sjeffout:
303219820Sjeff	close(connfd);
304219820Sjeff	return rem_dest;
305219820Sjeff}
306219820Sjeff
307219820Sjeff#include <sys/param.h>
308219820Sjeff
309219820Sjeffstatic struct pingpong_context *pp_init_ctx(struct ibv_device *ib_dev, int size,
310219820Sjeff					    int rx_depth, int port,
311219820Sjeff					    int use_event, int is_server)
312219820Sjeff{
313219820Sjeff	struct pingpong_context *ctx;
314219820Sjeff
315219820Sjeff	ctx = calloc(1, sizeof *ctx);
316219820Sjeff	if (!ctx)
317219820Sjeff		return NULL;
318219820Sjeff
319219820Sjeff	ctx->size     = size;
320219820Sjeff	ctx->rx_depth = rx_depth;
321219820Sjeff
322219820Sjeff	ctx->buf = malloc(roundup(size, page_size));
323219820Sjeff	if (!ctx->buf) {
324219820Sjeff		fprintf(stderr, "Couldn't allocate work buf.\n");
325219820Sjeff		return NULL;
326219820Sjeff	}
327219820Sjeff
328219820Sjeff	memset(ctx->buf, 0x7b + is_server, size);
329219820Sjeff
330219820Sjeff	ctx->context = ibv_open_device(ib_dev);
331219820Sjeff	if (!ctx->context) {
332219820Sjeff		fprintf(stderr, "Couldn't get context for %s\n",
333219820Sjeff			ibv_get_device_name(ib_dev));
334219820Sjeff		return NULL;
335219820Sjeff	}
336219820Sjeff
337219820Sjeff	if (use_event) {
338219820Sjeff		ctx->channel = ibv_create_comp_channel(ctx->context);
339219820Sjeff		if (!ctx->channel) {
340219820Sjeff			fprintf(stderr, "Couldn't create completion channel\n");
341219820Sjeff			return NULL;
342219820Sjeff		}
343219820Sjeff	} else
344219820Sjeff		ctx->channel = NULL;
345219820Sjeff
346219820Sjeff	ctx->pd = ibv_alloc_pd(ctx->context);
347219820Sjeff	if (!ctx->pd) {
348219820Sjeff		fprintf(stderr, "Couldn't allocate PD\n");
349219820Sjeff		return NULL;
350219820Sjeff	}
351219820Sjeff
352219820Sjeff	ctx->mr = ibv_reg_mr(ctx->pd, ctx->buf, size, IBV_ACCESS_LOCAL_WRITE);
353219820Sjeff	if (!ctx->mr) {
354219820Sjeff		fprintf(stderr, "Couldn't register MR\n");
355219820Sjeff		return NULL;
356219820Sjeff	}
357219820Sjeff
358219820Sjeff	ctx->cq = ibv_create_cq(ctx->context, rx_depth + 1, NULL,
359219820Sjeff				ctx->channel, 0);
360219820Sjeff	if (!ctx->cq) {
361219820Sjeff		fprintf(stderr, "Couldn't create CQ\n");
362219820Sjeff		return NULL;
363219820Sjeff	}
364219820Sjeff
365219820Sjeff	{
366219820Sjeff		struct ibv_qp_init_attr attr = {
367219820Sjeff			.send_cq = ctx->cq,
368219820Sjeff			.recv_cq = ctx->cq,
369219820Sjeff			.cap     = {
370219820Sjeff				.max_send_wr  = 1,
371219820Sjeff				.max_recv_wr  = rx_depth,
372219820Sjeff				.max_send_sge = 1,
373219820Sjeff				.max_recv_sge = 1
374219820Sjeff			},
375219820Sjeff			.qp_type = IBV_QPT_RC
376219820Sjeff		};
377219820Sjeff
378219820Sjeff		ctx->qp = ibv_create_qp(ctx->pd, &attr);
379219820Sjeff		if (!ctx->qp)  {
380219820Sjeff			fprintf(stderr, "Couldn't create QP\n");
381219820Sjeff			return NULL;
382219820Sjeff		}
383219820Sjeff	}
384219820Sjeff
385219820Sjeff	{
386219820Sjeff		struct ibv_qp_attr attr = {
387219820Sjeff			.qp_state        = IBV_QPS_INIT,
388219820Sjeff			.pkey_index      = 0,
389219820Sjeff			.port_num        = port,
390219820Sjeff			.qp_access_flags = 0
391219820Sjeff		};
392219820Sjeff
393219820Sjeff		if (ibv_modify_qp(ctx->qp, &attr,
394219820Sjeff				  IBV_QP_STATE              |
395219820Sjeff				  IBV_QP_PKEY_INDEX         |
396219820Sjeff				  IBV_QP_PORT               |
397219820Sjeff				  IBV_QP_ACCESS_FLAGS)) {
398219820Sjeff			fprintf(stderr, "Failed to modify QP to INIT\n");
399219820Sjeff			return NULL;
400219820Sjeff		}
401219820Sjeff	}
402219820Sjeff
403219820Sjeff	return ctx;
404219820Sjeff}
405219820Sjeff
406219820Sjeffint pp_close_ctx(struct pingpong_context *ctx)
407219820Sjeff{
408219820Sjeff	if (ibv_destroy_qp(ctx->qp)) {
409219820Sjeff		fprintf(stderr, "Couldn't destroy QP\n");
410219820Sjeff		return 1;
411219820Sjeff	}
412219820Sjeff
413219820Sjeff	if (ibv_destroy_cq(ctx->cq)) {
414219820Sjeff		fprintf(stderr, "Couldn't destroy CQ\n");
415219820Sjeff		return 1;
416219820Sjeff	}
417219820Sjeff
418219820Sjeff	if (ibv_dereg_mr(ctx->mr)) {
419219820Sjeff		fprintf(stderr, "Couldn't deregister MR\n");
420219820Sjeff		return 1;
421219820Sjeff	}
422219820Sjeff
423219820Sjeff	if (ibv_dealloc_pd(ctx->pd)) {
424219820Sjeff		fprintf(stderr, "Couldn't deallocate PD\n");
425219820Sjeff		return 1;
426219820Sjeff	}
427219820Sjeff
428219820Sjeff	if (ctx->channel) {
429219820Sjeff		if (ibv_destroy_comp_channel(ctx->channel)) {
430219820Sjeff			fprintf(stderr, "Couldn't destroy completion channel\n");
431219820Sjeff			return 1;
432219820Sjeff		}
433219820Sjeff	}
434219820Sjeff
435219820Sjeff	if (ibv_close_device(ctx->context)) {
436219820Sjeff		fprintf(stderr, "Couldn't release context\n");
437219820Sjeff		return 1;
438219820Sjeff	}
439219820Sjeff
440219820Sjeff	free(ctx->buf);
441219820Sjeff	free(ctx);
442219820Sjeff
443219820Sjeff	return 0;
444219820Sjeff}
445219820Sjeff
446219820Sjeffstatic int pp_post_recv(struct pingpong_context *ctx, int n)
447219820Sjeff{
448219820Sjeff	struct ibv_sge list = {
449219820Sjeff		.addr	= (uintptr_t) ctx->buf,
450219820Sjeff		.length = ctx->size,
451219820Sjeff		.lkey	= ctx->mr->lkey
452219820Sjeff	};
453219820Sjeff	struct ibv_recv_wr wr = {
454219820Sjeff		.wr_id	    = PINGPONG_RECV_WRID,
455219820Sjeff		.sg_list    = &list,
456219820Sjeff		.num_sge    = 1,
457219820Sjeff	};
458219820Sjeff	struct ibv_recv_wr *bad_wr;
459219820Sjeff	int i;
460219820Sjeff
461219820Sjeff	for (i = 0; i < n; ++i)
462219820Sjeff		if (ibv_post_recv(ctx->qp, &wr, &bad_wr))
463219820Sjeff			break;
464219820Sjeff
465219820Sjeff	return i;
466219820Sjeff}
467219820Sjeff
468219820Sjeffstatic int pp_post_send(struct pingpong_context *ctx)
469219820Sjeff{
470219820Sjeff	struct ibv_sge list = {
471219820Sjeff		.addr	= (uintptr_t) ctx->buf,
472219820Sjeff		.length = ctx->size,
473219820Sjeff		.lkey	= ctx->mr->lkey
474219820Sjeff	};
475219820Sjeff	struct ibv_send_wr wr = {
476219820Sjeff		.wr_id	    = PINGPONG_SEND_WRID,
477219820Sjeff		.sg_list    = &list,
478219820Sjeff		.num_sge    = 1,
479219820Sjeff		.opcode     = IBV_WR_SEND,
480219820Sjeff		.send_flags = IBV_SEND_SIGNALED,
481219820Sjeff	};
482219820Sjeff	struct ibv_send_wr *bad_wr;
483219820Sjeff
484219820Sjeff	return ibv_post_send(ctx->qp, &wr, &bad_wr);
485219820Sjeff}
486219820Sjeff
487219820Sjeffstatic void usage(const char *argv0)
488219820Sjeff{
489219820Sjeff	printf("Usage:\n");
490219820Sjeff	printf("  %s            start a server and wait for connection\n", argv0);
491219820Sjeff	printf("  %s <host>     connect to server at <host>\n", argv0);
492219820Sjeff	printf("\n");
493219820Sjeff	printf("Options:\n");
494219820Sjeff	printf("  -p, --port=<port>      listen on/connect to port <port> (default 18515)\n");
495219820Sjeff	printf("  -d, --ib-dev=<dev>     use IB device <dev> (default first device found)\n");
496219820Sjeff	printf("  -i, --ib-port=<port>   use port <port> of IB device (default 1)\n");
497219820Sjeff	printf("  -s, --size=<size>      size of message to exchange (default 4096)\n");
498219820Sjeff	printf("  -m, --mtu=<size>       path MTU (default 1024)\n");
499219820Sjeff	printf("  -r, --rx-depth=<dep>   number of receives to post at a time (default 500)\n");
500219820Sjeff	printf("  -n, --iters=<iters>    number of exchanges (default 1000)\n");
501219820Sjeff	printf("  -l, --sl=<sl>          service level value\n");
502219820Sjeff	printf("  -e, --events           sleep on CQ events (default poll)\n");
503219820Sjeff	printf("  -g, --gid-idx=<gid index> local port gid index\n");
504219820Sjeff}
505219820Sjeff
506219820Sjeffint main(int argc, char *argv[])
507219820Sjeff{
508219820Sjeff	struct ibv_device      **dev_list;
509219820Sjeff	struct ibv_device	*ib_dev;
510219820Sjeff	struct pingpong_context *ctx;
511219820Sjeff	struct pingpong_dest     my_dest;
512219820Sjeff	struct pingpong_dest    *rem_dest;
513219820Sjeff	struct timeval           start, end;
514219820Sjeff	char                    *ib_devname = NULL;
515219820Sjeff	char                    *servername = NULL;
516219820Sjeff	int                      port = 18515;
517219820Sjeff	int                      ib_port = 1;
518219820Sjeff	int                      size = 4096;
519219820Sjeff	enum ibv_mtu		 mtu = IBV_MTU_1024;
520219820Sjeff	int                      rx_depth = 500;
521219820Sjeff	int                      iters = 1000;
522219820Sjeff	int                      use_event = 0;
523219820Sjeff	int                      routs;
524219820Sjeff	int                      rcnt, scnt;
525219820Sjeff	int                      num_cq_events = 0;
526219820Sjeff	int                      sl = 0;
527219820Sjeff	int			 gidx = -1;
528219820Sjeff	char			 gid[33];
529219820Sjeff
530219820Sjeff	srand48(getpid() * time(NULL));
531219820Sjeff
532219820Sjeff	while (1) {
533219820Sjeff		int c;
534219820Sjeff
535219820Sjeff		static struct option long_options[] = {
536219820Sjeff			{ .name = "port",     .has_arg = 1, .val = 'p' },
537219820Sjeff			{ .name = "ib-dev",   .has_arg = 1, .val = 'd' },
538219820Sjeff			{ .name = "ib-port",  .has_arg = 1, .val = 'i' },
539219820Sjeff			{ .name = "size",     .has_arg = 1, .val = 's' },
540219820Sjeff			{ .name = "mtu",      .has_arg = 1, .val = 'm' },
541219820Sjeff			{ .name = "rx-depth", .has_arg = 1, .val = 'r' },
542219820Sjeff			{ .name = "iters",    .has_arg = 1, .val = 'n' },
543219820Sjeff			{ .name = "sl",       .has_arg = 1, .val = 'l' },
544219820Sjeff			{ .name = "events",   .has_arg = 0, .val = 'e' },
545219820Sjeff			{ .name = "gid-idx",  .has_arg = 1, .val = 'g' },
546219820Sjeff			{ 0 }
547219820Sjeff		};
548219820Sjeff
549219820Sjeff		c = getopt_long(argc, argv, "p:d:i:s:m:r:n:l:eg:", long_options, NULL);
550219820Sjeff		if (c == -1)
551219820Sjeff			break;
552219820Sjeff
553219820Sjeff		switch (c) {
554219820Sjeff		case 'p':
555219820Sjeff			port = strtol(optarg, NULL, 0);
556219820Sjeff			if (port < 0 || port > 65535) {
557219820Sjeff				usage(argv[0]);
558219820Sjeff				return 1;
559219820Sjeff			}
560219820Sjeff			break;
561219820Sjeff
562219820Sjeff		case 'd':
563219820Sjeff			ib_devname = strdup(optarg);
564219820Sjeff			break;
565219820Sjeff
566219820Sjeff		case 'i':
567219820Sjeff			ib_port = strtol(optarg, NULL, 0);
568219820Sjeff			if (ib_port < 0) {
569219820Sjeff				usage(argv[0]);
570219820Sjeff				return 1;
571219820Sjeff			}
572219820Sjeff			break;
573219820Sjeff
574219820Sjeff		case 's':
575219820Sjeff			size = strtol(optarg, NULL, 0);
576219820Sjeff			break;
577219820Sjeff
578219820Sjeff		case 'm':
579219820Sjeff			mtu = pp_mtu_to_enum(strtol(optarg, NULL, 0));
580219820Sjeff			if (mtu < 0) {
581219820Sjeff				usage(argv[0]);
582219820Sjeff				return 1;
583219820Sjeff			}
584219820Sjeff			break;
585219820Sjeff
586219820Sjeff		case 'r':
587219820Sjeff			rx_depth = strtol(optarg, NULL, 0);
588219820Sjeff			break;
589219820Sjeff
590219820Sjeff		case 'n':
591219820Sjeff			iters = strtol(optarg, NULL, 0);
592219820Sjeff			break;
593219820Sjeff
594219820Sjeff		case 'l':
595219820Sjeff			sl = strtol(optarg, NULL, 0);
596219820Sjeff			break;
597219820Sjeff
598219820Sjeff		case 'e':
599219820Sjeff			++use_event;
600219820Sjeff			break;
601219820Sjeff
602219820Sjeff		case 'g':
603219820Sjeff			gidx = strtol(optarg, NULL, 0);
604219820Sjeff			break;
605219820Sjeff
606219820Sjeff		default:
607219820Sjeff			usage(argv[0]);
608219820Sjeff			return 1;
609219820Sjeff		}
610219820Sjeff	}
611219820Sjeff
612219820Sjeff	if (optind == argc - 1)
613219820Sjeff		servername = strdup(argv[optind]);
614219820Sjeff	else if (optind < argc) {
615219820Sjeff		usage(argv[0]);
616219820Sjeff		return 1;
617219820Sjeff	}
618219820Sjeff
619219820Sjeff	page_size = sysconf(_SC_PAGESIZE);
620219820Sjeff
621219820Sjeff	dev_list = ibv_get_device_list(NULL);
622219820Sjeff	if (!dev_list) {
623219820Sjeff		perror("Failed to get IB devices list");
624219820Sjeff		return 1;
625219820Sjeff	}
626219820Sjeff
627219820Sjeff	if (!ib_devname) {
628219820Sjeff		ib_dev = *dev_list;
629219820Sjeff		if (!ib_dev) {
630219820Sjeff			fprintf(stderr, "No IB devices found\n");
631219820Sjeff			return 1;
632219820Sjeff		}
633219820Sjeff	} else {
634219820Sjeff		int i;
635219820Sjeff		for (i = 0; dev_list[i]; ++i)
636219820Sjeff			if (!strcmp(ibv_get_device_name(dev_list[i]), ib_devname))
637219820Sjeff				break;
638219820Sjeff		ib_dev = dev_list[i];
639219820Sjeff		if (!ib_dev) {
640219820Sjeff			fprintf(stderr, "IB device %s not found\n", ib_devname);
641219820Sjeff			return 1;
642219820Sjeff		}
643219820Sjeff	}
644219820Sjeff
645219820Sjeff	ctx = pp_init_ctx(ib_dev, size, rx_depth, ib_port, use_event, !servername);
646219820Sjeff	if (!ctx)
647219820Sjeff		return 1;
648219820Sjeff
649219820Sjeff	routs = pp_post_recv(ctx, ctx->rx_depth);
650219820Sjeff	if (routs < ctx->rx_depth) {
651219820Sjeff		fprintf(stderr, "Couldn't post receive (%d)\n", routs);
652219820Sjeff		return 1;
653219820Sjeff	}
654219820Sjeff
655219820Sjeff	if (use_event)
656219820Sjeff		if (ibv_req_notify_cq(ctx->cq, 0)) {
657219820Sjeff			fprintf(stderr, "Couldn't request CQ notification\n");
658219820Sjeff			return 1;
659219820Sjeff		}
660219820Sjeff
661219820Sjeff
662219820Sjeff	if (pp_get_port_info(ctx->context, ib_port, &ctx->portinfo)) {
663219820Sjeff		fprintf(stderr, "Couldn't get port info\n");
664219820Sjeff		return 1;
665219820Sjeff	}
666219820Sjeff
667219820Sjeff	my_dest.lid = ctx->portinfo.lid;
668219820Sjeff	if (ctx->portinfo.link_layer == IBV_LINK_LAYER_INFINIBAND && !my_dest.lid) {
669219820Sjeff		fprintf(stderr, "Couldn't get local LID\n");
670219820Sjeff		return 1;
671219820Sjeff	}
672219820Sjeff
673219820Sjeff	if (gidx >= 0) {
674219820Sjeff		if (ibv_query_gid(ctx->context, ib_port, gidx, &my_dest.gid)) {
675219820Sjeff			fprintf(stderr, "Could not get local gid for gid index %d\n", gidx);
676219820Sjeff			return 1;
677219820Sjeff		}
678219820Sjeff	} else
679219820Sjeff		memset(&my_dest.gid, 0, sizeof my_dest.gid);
680219820Sjeff
681219820Sjeff	my_dest.qpn = ctx->qp->qp_num;
682219820Sjeff	my_dest.psn = lrand48() & 0xffffff;
683219820Sjeff	inet_ntop(AF_INET6, &my_dest.gid, gid, sizeof gid);
684219820Sjeff	printf("  local address:  LID 0x%04x, QPN 0x%06x, PSN 0x%06x, GID %s\n",
685219820Sjeff	       my_dest.lid, my_dest.qpn, my_dest.psn, gid);
686219820Sjeff
687219820Sjeff
688219820Sjeff	if (servername)
689219820Sjeff		rem_dest = pp_client_exch_dest(servername, port, &my_dest);
690219820Sjeff	else
691219820Sjeff		rem_dest = pp_server_exch_dest(ctx, ib_port, mtu, port, sl, &my_dest, gidx);
692219820Sjeff
693219820Sjeff	if (!rem_dest)
694219820Sjeff		return 1;
695219820Sjeff
696219820Sjeff	inet_ntop(AF_INET6, &rem_dest->gid, gid, sizeof gid);
697219820Sjeff	printf("  remote address: LID 0x%04x, QPN 0x%06x, PSN 0x%06x, GID %s\n",
698219820Sjeff	       rem_dest->lid, rem_dest->qpn, rem_dest->psn, gid);
699219820Sjeff
700219820Sjeff	if (servername)
701219820Sjeff		if (pp_connect_ctx(ctx, ib_port, my_dest.psn, mtu, sl, rem_dest, gidx))
702219820Sjeff			return 1;
703219820Sjeff
704219820Sjeff	ctx->pending = PINGPONG_RECV_WRID;
705219820Sjeff
706219820Sjeff	if (servername) {
707219820Sjeff		if (pp_post_send(ctx)) {
708219820Sjeff			fprintf(stderr, "Couldn't post send\n");
709219820Sjeff			return 1;
710219820Sjeff		}
711219820Sjeff		ctx->pending |= PINGPONG_SEND_WRID;
712219820Sjeff	}
713219820Sjeff
714219820Sjeff	if (gettimeofday(&start, NULL)) {
715219820Sjeff		perror("gettimeofday");
716219820Sjeff		return 1;
717219820Sjeff	}
718219820Sjeff
719219820Sjeff	rcnt = scnt = 0;
720219820Sjeff	while (rcnt < iters || scnt < iters) {
721219820Sjeff		if (use_event) {
722219820Sjeff			struct ibv_cq *ev_cq;
723219820Sjeff			void          *ev_ctx;
724219820Sjeff
725219820Sjeff			if (ibv_get_cq_event(ctx->channel, &ev_cq, &ev_ctx)) {
726219820Sjeff				fprintf(stderr, "Failed to get cq_event\n");
727219820Sjeff				return 1;
728219820Sjeff			}
729219820Sjeff
730219820Sjeff			++num_cq_events;
731219820Sjeff
732219820Sjeff			if (ev_cq != ctx->cq) {
733219820Sjeff				fprintf(stderr, "CQ event for unknown CQ %p\n", ev_cq);
734219820Sjeff				return 1;
735219820Sjeff			}
736219820Sjeff
737219820Sjeff			if (ibv_req_notify_cq(ctx->cq, 0)) {
738219820Sjeff				fprintf(stderr, "Couldn't request CQ notification\n");
739219820Sjeff				return 1;
740219820Sjeff			}
741219820Sjeff		}
742219820Sjeff
743219820Sjeff		{
744219820Sjeff			struct ibv_wc wc[2];
745219820Sjeff			int ne, i;
746219820Sjeff
747219820Sjeff			do {
748219820Sjeff				ne = ibv_poll_cq(ctx->cq, 2, wc);
749219820Sjeff				if (ne < 0) {
750219820Sjeff					fprintf(stderr, "poll CQ failed %d\n", ne);
751219820Sjeff					return 1;
752219820Sjeff				}
753219820Sjeff
754219820Sjeff			} while (!use_event && ne < 1);
755219820Sjeff
756219820Sjeff			for (i = 0; i < ne; ++i) {
757219820Sjeff				if (wc[i].status != IBV_WC_SUCCESS) {
758219820Sjeff					fprintf(stderr, "Failed status %s (%d) for wr_id %d\n",
759219820Sjeff						ibv_wc_status_str(wc[i].status),
760219820Sjeff						wc[i].status, (int) wc[i].wr_id);
761219820Sjeff					return 1;
762219820Sjeff				}
763219820Sjeff
764219820Sjeff				switch ((int) wc[i].wr_id) {
765219820Sjeff				case PINGPONG_SEND_WRID:
766219820Sjeff					++scnt;
767219820Sjeff					break;
768219820Sjeff
769219820Sjeff				case PINGPONG_RECV_WRID:
770219820Sjeff					if (--routs <= 1) {
771219820Sjeff						routs += pp_post_recv(ctx, ctx->rx_depth - routs);
772219820Sjeff						if (routs < ctx->rx_depth) {
773219820Sjeff							fprintf(stderr,
774219820Sjeff								"Couldn't post receive (%d)\n",
775219820Sjeff								routs);
776219820Sjeff							return 1;
777219820Sjeff						}
778219820Sjeff					}
779219820Sjeff
780219820Sjeff					++rcnt;
781219820Sjeff					break;
782219820Sjeff
783219820Sjeff				default:
784219820Sjeff					fprintf(stderr, "Completion for unknown wr_id %d\n",
785219820Sjeff						(int) wc[i].wr_id);
786219820Sjeff					return 1;
787219820Sjeff				}
788219820Sjeff
789219820Sjeff				ctx->pending &= ~(int) wc[i].wr_id;
790219820Sjeff				if (scnt < iters && !ctx->pending) {
791219820Sjeff					if (pp_post_send(ctx)) {
792219820Sjeff						fprintf(stderr, "Couldn't post send\n");
793219820Sjeff						return 1;
794219820Sjeff					}
795219820Sjeff					ctx->pending = PINGPONG_RECV_WRID |
796219820Sjeff						       PINGPONG_SEND_WRID;
797219820Sjeff				}
798219820Sjeff			}
799219820Sjeff		}
800219820Sjeff	}
801219820Sjeff
802219820Sjeff	if (gettimeofday(&end, NULL)) {
803219820Sjeff		perror("gettimeofday");
804219820Sjeff		return 1;
805219820Sjeff	}
806219820Sjeff
807219820Sjeff	{
808219820Sjeff		float usec = (end.tv_sec - start.tv_sec) * 1000000 +
809219820Sjeff			(end.tv_usec - start.tv_usec);
810219820Sjeff		long long bytes = (long long) size * iters * 2;
811219820Sjeff
812219820Sjeff		printf("%lld bytes in %.2f seconds = %.2f Mbit/sec\n",
813219820Sjeff		       bytes, usec / 1000000., bytes * 8. / usec);
814219820Sjeff		printf("%d iters in %.2f seconds = %.2f usec/iter\n",
815219820Sjeff		       iters, usec / 1000000., usec / iters);
816219820Sjeff	}
817219820Sjeff
818219820Sjeff	ibv_ack_cq_events(ctx->cq, num_cq_events);
819219820Sjeff
820219820Sjeff	if (pp_close_ctx(ctx))
821219820Sjeff		return 1;
822219820Sjeff
823219820Sjeff	ibv_free_device_list(dev_list);
824219820Sjeff	free(rem_dest);
825219820Sjeff
826219820Sjeff	return 0;
827219820Sjeff}
828