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