| /* |
| * Copyright (c) 2013 Intel Corporation. All rights reserved. |
| * Copyright (c) Nvidia Corporation. All rights reserved. |
| * |
| * This software is available to you under the OpenIB.org BSD license |
| * below: |
| * |
| * Redistribution and use in source and binary forms, with or |
| * without modification, are permitted provided that the following |
| * conditions are met: |
| * |
| * - Redistributions of source code must retain the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials |
| * provided with the distribution. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AWV |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <strings.h> |
| #include <stdbool.h> |
| #include <errno.h> |
| #include <getopt.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/time.h> |
| #include <sys/wait.h> |
| #include <netdb.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <stdatomic.h> |
| #include <netinet/tcp.h> |
| #include <ccan/container_of.h> |
| #include <inttypes.h> |
| #include <rdma/rdma_cma.h> |
| #include "common.h" |
| |
| |
| static struct rdma_addrinfo hints, *rai; |
| static struct addrinfo *ai; |
| static struct rdma_event_channel *channel; |
| static int oob_sock = -1; |
| static const char *port = "7471"; |
| static char *dst_addr; |
| static char *src_addr; |
| static int timeout = 2000; |
| static int retries = 2; |
| static uint32_t base_qpn = 1000; |
| static _Atomic(uint32_t) cur_qpn; |
| static uint32_t mimic_qp_delay; |
| static bool mimic; |
| |
| enum step { |
| STEP_FULL_CONNECT, |
| STEP_CREATE_ID, |
| STEP_BIND, |
| STEP_RESOLVE_ADDR, |
| STEP_RESOLVE_ROUTE, |
| STEP_CREATE_QP, |
| STEP_INIT_QP_ATTR, |
| STEP_INIT_QP, |
| STEP_RTR_QP_ATTR, |
| STEP_RTR_QP, |
| STEP_RTS_QP_ATTR, |
| STEP_RTS_QP, |
| STEP_CONNECT, |
| STEP_ESTABLISH, |
| STEP_DISCONNECT, |
| STEP_DESTROY_ID, |
| STEP_DESTROY_QP, |
| STEP_CNT |
| }; |
| |
| static const char *step_str[] = { |
| "full connect", |
| "create id", |
| "bind addr", |
| "resolve addr", |
| "resolve route", |
| "create qp", |
| "init qp attr", |
| "init qp", |
| "rtr qp attr", |
| "rtr qp", |
| "rts qp attr", |
| "rts qp", |
| "cm connect", |
| "establish", |
| "disconnect", |
| "destroy id", |
| "destroy qp" |
| }; |
| |
| struct node { |
| struct work_item work; |
| struct rdma_cm_id *id; |
| int sock; |
| |
| struct ibv_qp *qp; |
| enum ibv_qp_state next_qps; |
| enum step next_step; |
| |
| uint64_t times[STEP_CNT][2]; |
| int retries; |
| }; |
| |
| static struct work_queue wq; |
| |
| static struct node *nodes; |
| static int node_index; |
| static uint64_t times[STEP_CNT][2]; |
| static int connections; |
| static int num_threads = 1; |
| static _Atomic(int) disc_events; |
| |
| static _Atomic(int) completed[STEP_CNT]; |
| |
| static struct ibv_pd *pd; |
| static struct ibv_cq *cq; |
| |
| #define start_perf(n, s) do { (n)->times[s][0] = gettime_us(); } while (0) |
| #define end_perf(n, s) do { (n)->times[s][1] = gettime_us(); } while (0) |
| #define start_time(s) do { times[s][0] = gettime_us(); } while (0) |
| #define end_time(s) do { times[s][1] = gettime_us(); } while (0) |
| |
| |
| static inline bool is_client(void) |
| { |
| return dst_addr != NULL; |
| } |
| |
| static void show_perf(int iter) |
| { |
| uint64_t diff, max[STEP_CNT], min[STEP_CNT], sum[STEP_CNT]; |
| int i, c; |
| |
| for (i = 0; i < STEP_CNT; i++) { |
| sum[i] = 0; |
| max[i] = 0; |
| min[i] = UINT32_MAX; |
| for (c = 0; c < iter; c++) { |
| if (nodes[c].times[i][0] && nodes[c].times[i][1]) { |
| diff = (uint32_t) (nodes[c].times[i][1] - |
| nodes[c].times[i][0]); |
| sum[i] += diff; |
| if (diff > max[i]) |
| max[i] = diff; |
| if (diff < min[i]) |
| min[i] = diff; |
| } |
| } |
| /* Print 0 if we have no data */ |
| if (min[i] == UINT32_MAX) |
| min[i] = 0; |
| } |
| |
| /* Reporting the 'sum' of the full connect is meaningless */ |
| sum[STEP_FULL_CONNECT] = 0; |
| |
| if (atomic_load(&cur_qpn) == 0) |
| printf("qp_conn %10d\n", iter); |
| else |
| printf("cm_conn %10d\n", iter); |
| printf("threads %10d\n", num_threads); |
| |
| printf("step avg/iter total(us) us/conn sum(us) max(us) min(us)\n"); |
| for (i = 0; i < STEP_CNT; i++) { |
| diff = (uint64_t) (times[i][1] - times[i][0]); |
| |
| printf("%-13s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 |
| " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n", |
| step_str[i], diff / iter, diff, |
| sum[i] / iter, sum[i], max[i], min[i]); |
| } |
| } |
| |
| static void sock_listen(int *listen_sock, int backlog) |
| { |
| struct addrinfo aih = {}; |
| int optval = 1; |
| int ret; |
| |
| aih.ai_family = AF_INET; |
| aih.ai_socktype = SOCK_STREAM; |
| aih.ai_flags = AI_PASSIVE; |
| ret = getaddrinfo(src_addr, port, &aih, &ai); |
| if (ret) { |
| perror("getaddrinfo"); |
| exit(EXIT_FAILURE); |
| } |
| |
| *listen_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); |
| if (*listen_sock < 0) { |
| perror("socket"); |
| exit(EXIT_FAILURE); |
| } |
| |
| ret = setsockopt(*listen_sock, SOL_SOCKET, SO_REUSEADDR, |
| (char *) &optval, sizeof(optval)); |
| if (ret) { |
| perror("setsockopt"); |
| exit(EXIT_FAILURE); |
| } |
| |
| ret = bind(*listen_sock, ai->ai_addr, ai->ai_addrlen); |
| if (ret) { |
| perror("bind"); |
| exit(EXIT_FAILURE); |
| } |
| |
| ret = listen(*listen_sock, backlog); |
| if (ret) { |
| perror("listen"); |
| exit(EXIT_FAILURE); |
| } |
| |
| freeaddrinfo(ai); |
| } |
| |
| static void sock_server(int iter) |
| { |
| int listen_sock, i; |
| |
| printf("Server baseline socket setup\n"); |
| sock_listen(&listen_sock, iter); |
| |
| printf("Accept sockets\n"); |
| for (i = 0; i < iter; i++) { |
| nodes[i].sock = accept(listen_sock, NULL, NULL); |
| if (nodes[i].sock < 0) { |
| perror("accept"); |
| exit(EXIT_FAILURE); |
| } |
| |
| if (i == 0) |
| start_time(STEP_FULL_CONNECT); |
| } |
| end_time(STEP_FULL_CONNECT); |
| |
| printf("Closing sockets\n"); |
| start_time(STEP_DESTROY_ID); |
| for (i = 0; i < iter; i++) |
| close(nodes[i].sock); |
| end_time(STEP_DESTROY_ID); |
| close(listen_sock); |
| |
| printf("Server baseline socket results:\n"); |
| show_perf(iter); |
| } |
| |
| static void create_sock(struct work_item *item) |
| { |
| struct node *n = container_of(item, struct node, work); |
| |
| start_perf(n, STEP_CREATE_ID); |
| n->sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); |
| if (n->sock < 0) { |
| perror("socket"); |
| exit(EXIT_FAILURE); |
| } |
| end_perf(n, STEP_CREATE_ID); |
| atomic_fetch_add(&completed[STEP_CREATE_ID], 1); |
| } |
| |
| static void connect_sock(struct work_item *item) |
| { |
| struct node *n = container_of(item, struct node, work); |
| int ret; |
| |
| start_perf(n, STEP_CONNECT); |
| ret = connect(n->sock, ai->ai_addr, ai->ai_addrlen); |
| if (ret) { |
| perror("connect"); |
| exit(EXIT_FAILURE); |
| } |
| end_perf(n, STEP_CONNECT); |
| atomic_fetch_add(&completed[STEP_CONNECT], 1); |
| } |
| |
| static void sock_client(int iter) |
| { |
| int i, ret; |
| |
| printf("Client baseline socket setup\n"); |
| ret = getaddrinfo(dst_addr, port, NULL, &ai); |
| if (ret) { |
| perror("getaddrinfo"); |
| exit(EXIT_FAILURE); |
| } |
| |
| start_time(STEP_FULL_CONNECT); |
| |
| printf("Creating sockets\n"); |
| start_time(STEP_CREATE_ID); |
| for (i = 0; i < iter; i++) |
| wq_insert(&wq, &nodes[i].work, create_sock); |
| |
| while (atomic_load(&completed[STEP_CREATE_ID]) < iter) |
| sched_yield(); |
| end_time(STEP_CREATE_ID); |
| |
| printf("Connecting sockets\n"); |
| start_time(STEP_CONNECT); |
| for (i = 0; i < iter; i++) |
| wq_insert(&wq, &nodes[i].work, connect_sock); |
| |
| while (atomic_load(&completed[STEP_CONNECT]) < iter) |
| sched_yield(); |
| end_time(STEP_CONNECT); |
| |
| end_time(STEP_FULL_CONNECT); |
| |
| printf("Closing sockets\n"); |
| start_time(STEP_DESTROY_ID); |
| for (i = 0; i < iter; i++) |
| close(nodes[i].sock); |
| end_time(STEP_DESTROY_ID); |
| |
| freeaddrinfo(ai); |
| |
| printf("Client baseline socket results:\n"); |
| show_perf(iter); |
| } |
| |
| static inline bool need_verbs(void) |
| { |
| return pd == NULL; |
| } |
| |
| static void open_verbs(struct rdma_cm_id *id) |
| { |
| printf("\tAllocating verbs resources\n"); |
| pd = ibv_alloc_pd(id->verbs); |
| if (!pd) { |
| perror("ibv_alloc_pd"); |
| exit(EXIT_FAILURE); |
| } |
| |
| cq = ibv_create_cq(id->verbs, 1, NULL, NULL, 0); |
| if (!cq) { |
| perror("ibv_create_cq"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| static void create_qp(struct work_item *item) |
| { |
| struct node *n = container_of(item, struct node, work); |
| struct ibv_qp_init_attr attr; |
| |
| if (need_verbs()) |
| open_verbs(n->id); |
| |
| attr.qp_context = n; |
| attr.send_cq = cq; |
| attr.recv_cq = cq; |
| attr.srq = NULL; |
| attr.qp_type = IBV_QPT_RC; |
| attr.sq_sig_all = 1; |
| |
| attr.cap.max_send_wr = 1; |
| attr.cap.max_recv_wr = 1; |
| attr.cap.max_send_sge = 1; |
| attr.cap.max_recv_sge = 1; |
| attr.cap.max_inline_data = 0; |
| |
| start_perf(n, STEP_CREATE_QP); |
| if (atomic_load(&cur_qpn) == 0) { |
| n->qp = ibv_create_qp(pd, &attr); |
| if (!n->qp) { |
| perror("ibv_create_qp"); |
| exit(EXIT_FAILURE); |
| } |
| } else { |
| sleep_us(mimic_qp_delay); |
| } |
| end_perf(n, STEP_CREATE_QP); |
| atomic_fetch_add(&completed[STEP_CREATE_QP], 1); |
| } |
| |
| static void |
| modify_qp(struct node *n, enum ibv_qp_state state, enum step attr_step) |
| { |
| struct ibv_qp_attr attr; |
| int mask, ret; |
| |
| attr.qp_state = state; |
| start_perf(n, attr_step); |
| ret = rdma_init_qp_attr(n->id, &attr, &mask); |
| if (ret) { |
| perror("rdma_init_qp_attr"); |
| exit(EXIT_FAILURE); |
| } |
| end_perf(n, attr_step++); |
| |
| start_perf(n, attr_step); |
| if (n->qp) { |
| ret = ibv_modify_qp(n->qp, &attr, mask); |
| if (ret) { |
| perror("ibv_modify_qp"); |
| exit(EXIT_FAILURE); |
| |
| } |
| } else { |
| sleep_us(mimic_qp_delay); |
| } |
| end_perf(n, attr_step); |
| atomic_fetch_add(&completed[attr_step], 1); |
| } |
| |
| static void modify_qp_work(struct work_item *item) |
| { |
| struct node *n = container_of(item, struct node, work); |
| |
| modify_qp(n, n->next_qps, n->next_step); |
| } |
| |
| static void init_conn_param(struct node *n, struct rdma_conn_param *param) |
| { |
| param->private_data = rai->ai_connect; |
| param->private_data_len = rai->ai_connect_len; |
| param->responder_resources = 1; |
| param->initiator_depth = 1; |
| param->flow_control = 0; |
| param->retry_count = 0; |
| param->rnr_retry_count = 0; |
| param->srq = 0; |
| param->qp_num = n->qp ? n->qp->qp_num : atomic_fetch_add(&cur_qpn, 1); |
| } |
| |
| static void connect_qp(struct node *n) |
| { |
| struct rdma_conn_param conn_param; |
| int ret; |
| |
| init_conn_param(n, &conn_param); |
| |
| start_perf(n, STEP_CONNECT); |
| ret = rdma_connect(n->id, &conn_param); |
| if (ret) { |
| perror("rdma_connect"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| static void resolve_addr(struct work_item *item) |
| { |
| struct node *n = container_of(item, struct node, work); |
| int ret; |
| |
| n->retries = retries; |
| start_perf(n, STEP_RESOLVE_ADDR); |
| ret = rdma_resolve_addr(n->id, rai->ai_src_addr, |
| rai->ai_dst_addr, timeout); |
| if (ret) { |
| perror("rdma_resolve_addr"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| static void resolve_route(struct work_item *item) |
| { |
| struct node *n = container_of(item, struct node, work); |
| int ret; |
| |
| n->retries = retries; |
| start_perf(n, STEP_RESOLVE_ROUTE); |
| ret = rdma_resolve_route(n->id, timeout); |
| if (ret) { |
| perror("rdma_resolve_route"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| static void connect_response(struct work_item *item) |
| { |
| struct node *n = container_of(item, struct node, work); |
| |
| modify_qp(n, IBV_QPS_RTR, STEP_RTR_QP_ATTR); |
| modify_qp(n, IBV_QPS_RTS, STEP_RTS_QP_ATTR); |
| |
| start_perf(n, STEP_ESTABLISH); |
| rdma_establish(n->id); |
| end_perf(n, STEP_ESTABLISH); |
| |
| end_perf(n, STEP_CONNECT); |
| end_perf(n, STEP_FULL_CONNECT); |
| atomic_fetch_add(&completed[STEP_CONNECT], 1); |
| } |
| |
| static void req_handler(struct work_item *item) |
| { |
| struct node *n = container_of(item, struct node, work); |
| struct rdma_conn_param conn_param; |
| int ret; |
| |
| create_qp(&n->work); |
| modify_qp(n, IBV_QPS_INIT, STEP_INIT_QP_ATTR); |
| modify_qp(n, IBV_QPS_RTR, STEP_RTR_QP_ATTR); |
| modify_qp(n, IBV_QPS_RTS, STEP_RTS_QP_ATTR); |
| |
| init_conn_param(n, &conn_param); |
| ret = rdma_accept(n->id, &conn_param); |
| if (ret) { |
| perror("failure accepting"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| static void client_disconnect(struct work_item *item) |
| { |
| struct node *n = container_of(item, struct node, work); |
| |
| start_perf(n, STEP_DISCONNECT); |
| rdma_disconnect(n->id); |
| end_perf(n, STEP_DISCONNECT); |
| atomic_fetch_add(&completed[STEP_DISCONNECT], 1); |
| } |
| |
| static void server_disconnect(struct work_item *item) |
| { |
| struct node *n = container_of(item, struct node, work); |
| |
| start_perf(n, STEP_DISCONNECT); |
| rdma_disconnect(n->id); |
| end_perf(n, STEP_DISCONNECT); |
| |
| if (atomic_load(&disc_events) >= connections) |
| end_time(STEP_DISCONNECT); |
| atomic_fetch_add(&completed[STEP_DISCONNECT], 1); |
| } |
| |
| static void cma_handler(struct rdma_cm_id *id, struct rdma_cm_event *event) |
| { |
| struct node *n = id->context; |
| |
| switch (event->event) { |
| case RDMA_CM_EVENT_ADDR_RESOLVED: |
| end_perf(n, STEP_RESOLVE_ADDR); |
| atomic_fetch_add(&completed[STEP_RESOLVE_ADDR], 1); |
| break; |
| case RDMA_CM_EVENT_ROUTE_RESOLVED: |
| end_perf(n, STEP_RESOLVE_ROUTE); |
| atomic_fetch_add(&completed[STEP_RESOLVE_ROUTE], 1); |
| break; |
| case RDMA_CM_EVENT_CONNECT_REQUEST: |
| if (node_index == 0) { |
| printf("\tAccepting\n"); |
| start_time(STEP_CONNECT); |
| } |
| n = &nodes[node_index++]; |
| n->id = id; |
| id->context = n; |
| wq_insert(&wq, &n->work, req_handler); |
| break; |
| case RDMA_CM_EVENT_CONNECT_RESPONSE: |
| wq_insert(&wq, &n->work, connect_response); |
| break; |
| case RDMA_CM_EVENT_ESTABLISHED: |
| if (atomic_fetch_add(&completed[STEP_CONNECT], 1) >= |
| connections - 1) |
| end_time(STEP_CONNECT); |
| break; |
| case RDMA_CM_EVENT_ADDR_ERROR: |
| if (n->retries--) { |
| if (!rdma_resolve_addr(n->id, rai->ai_src_addr, |
| rai->ai_dst_addr, timeout)) |
| break; |
| } |
| printf("RDMA_CM_EVENT_ADDR_ERROR, error: %d\n", event->status); |
| exit(EXIT_FAILURE); |
| break; |
| case RDMA_CM_EVENT_ROUTE_ERROR: |
| if (n->retries--) { |
| if (!rdma_resolve_route(n->id, timeout)) |
| break; |
| } |
| printf("RDMA_CM_EVENT_ROUTE_ERROR, error: %d\n", event->status); |
| exit(EXIT_FAILURE); |
| break; |
| case RDMA_CM_EVENT_CONNECT_ERROR: |
| case RDMA_CM_EVENT_UNREACHABLE: |
| case RDMA_CM_EVENT_REJECTED: |
| printf("event: %s, error: %d\n", |
| rdma_event_str(event->event), event->status); |
| exit(EXIT_FAILURE); |
| break; |
| case RDMA_CM_EVENT_DISCONNECTED: |
| if (!is_client()) { |
| /* To fix an issue where DREQs are not responded |
| * to, the client completes its disconnect phase |
| * as soon as it calls rdma_disconnect and does |
| * not wait for a response from the server. The |
| * OOB sync handles that coordiation |
| end_perf(n, STEP_DISCONNECT); |
| atomic_fetch_add(&completed[STEP_DISCONNECT], 1); |
| } else { |
| */ |
| if (atomic_fetch_add(&disc_events, 1) == 0) { |
| printf("\tDisconnecting\n"); |
| start_time(STEP_DISCONNECT); |
| } |
| wq_insert(&wq, &n->work, server_disconnect); |
| } |
| break; |
| case RDMA_CM_EVENT_TIMEWAIT_EXIT: |
| break; |
| default: |
| printf("Unhandled event: %d (%s)\n", event->event, |
| rdma_event_str(event->event)); |
| exit(EXIT_FAILURE); |
| break; |
| } |
| rdma_ack_cm_event(event); |
| } |
| |
| static void create_ids(int iter) |
| { |
| int ret, i; |
| |
| printf("\tCreating IDs\n"); |
| start_time(STEP_CREATE_ID); |
| for (i = 0; i < iter; i++) { |
| start_perf(&nodes[i], STEP_FULL_CONNECT); |
| start_perf(&nodes[i], STEP_CREATE_ID); |
| ret = rdma_create_id(channel, &nodes[i].id, &nodes[i], |
| hints.ai_port_space); |
| if (ret) { |
| perror("rdma_create_id"); |
| exit(EXIT_FAILURE); |
| } |
| end_perf(&nodes[i], STEP_CREATE_ID); |
| } |
| end_time(STEP_CREATE_ID); |
| } |
| |
| static void destroy_ids(int iter) |
| { |
| int i; |
| |
| start_time(STEP_DESTROY_ID); |
| for (i = 0; i < iter; i++) { |
| start_perf(&nodes[i], STEP_DESTROY_ID); |
| if (nodes[i].id) |
| rdma_destroy_id(nodes[i].id); |
| end_perf(&nodes[i], STEP_DESTROY_ID); |
| } |
| end_time(STEP_DESTROY_ID); |
| } |
| |
| static void destroy_qps(int iter) |
| { |
| int i; |
| |
| start_time(STEP_DESTROY_QP); |
| for (i = 0; i < iter; i++) { |
| start_perf(&nodes[i], STEP_DESTROY_QP); |
| if (nodes[i].qp) |
| ibv_destroy_qp(nodes[i].qp); |
| end_perf(&nodes[i], STEP_DESTROY_QP); |
| } |
| end_time(STEP_DESTROY_QP); |
| } |
| |
| static void *process_events(void *arg) |
| { |
| struct rdma_cm_event *event; |
| int ret; |
| |
| while (1) { |
| ret = rdma_get_cm_event(channel, &event); |
| if (!ret) { |
| cma_handler(event->id, event); |
| } else { |
| perror("rdma_get_cm_event"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static void server_listen(struct rdma_cm_id **listen_id) |
| { |
| int ret; |
| |
| ret = rdma_create_id(channel, listen_id, NULL, hints.ai_port_space); |
| if (ret) { |
| perror("rdma_create_id"); |
| exit(EXIT_FAILURE); |
| } |
| |
| ret = rdma_bind_addr(*listen_id, rai->ai_src_addr); |
| if (ret) { |
| perror("rdma_bind_addr"); |
| exit(EXIT_FAILURE); |
| } |
| |
| ret = rdma_listen(*listen_id, 0); |
| if (ret) { |
| perror("rdma_listen"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| static void reset_test(int iter) |
| { |
| int i; |
| |
| node_index = 0; |
| atomic_store(&disc_events, 0); |
| connections = iter; |
| |
| memset(times, 0, sizeof times); |
| memset(nodes, 0, sizeof(*nodes) * iter); |
| |
| for (i = 0; i < STEP_CNT; i++) |
| atomic_store(&completed[i], 0); |
| |
| if (is_client()) |
| oob_sendrecv(oob_sock, 0); |
| else |
| oob_recvsend(oob_sock, 0); |
| } |
| |
| static void server_connect(int iter) |
| { |
| reset_test(iter); |
| |
| while (atomic_load(&completed[STEP_CONNECT]) < iter) |
| sched_yield(); |
| |
| oob_recvsend(oob_sock, STEP_CONNECT); |
| |
| while (atomic_load(&completed[STEP_DISCONNECT]) < iter) |
| sched_yield(); |
| |
| oob_recvsend(oob_sock, STEP_DISCONNECT); |
| |
| destroy_qps(iter); |
| destroy_ids(iter); |
| } |
| |
| static void client_connect(int iter) |
| { |
| int i, ret; |
| |
| reset_test(iter); |
| start_time(STEP_FULL_CONNECT); |
| create_ids(iter); |
| |
| if (src_addr) { |
| printf("\tBinding addresses\n"); |
| start_time(STEP_BIND); |
| for (i = 0; i < iter; i++) { |
| start_perf(&nodes[i], STEP_BIND); |
| ret = rdma_bind_addr(nodes[i].id, rai->ai_src_addr); |
| if (ret) { |
| perror("rdma_bind_addr"); |
| exit(EXIT_FAILURE); |
| } |
| end_perf(&nodes[i], STEP_BIND); |
| } |
| end_time(STEP_BIND); |
| } |
| |
| printf("\tResolving addresses\n"); |
| start_time(STEP_RESOLVE_ADDR); |
| for (i = 0; i < iter; i++) |
| wq_insert(&wq, &nodes[i].work, resolve_addr); |
| |
| while (atomic_load(&completed[STEP_RESOLVE_ADDR]) < iter) |
| sched_yield(); |
| end_time(STEP_RESOLVE_ADDR); |
| |
| printf("\tResolving routes\n"); |
| start_time(STEP_RESOLVE_ROUTE); |
| for (i = 0; i < iter; i++) |
| wq_insert(&wq, &nodes[i].work, resolve_route); |
| |
| while (atomic_load(&completed[STEP_RESOLVE_ROUTE]) < iter) |
| sched_yield(); |
| end_time(STEP_RESOLVE_ROUTE); |
| |
| printf("\tCreating QPs\n"); |
| start_time(STEP_CREATE_QP); |
| for (i = 0; i < iter; i++) |
| wq_insert(&wq, &nodes[i].work, create_qp); |
| |
| while (atomic_load(&completed[STEP_CREATE_QP]) < iter) |
| sched_yield(); |
| end_time(STEP_CREATE_QP); |
| |
| printf("\tModify QPs to INIT\n"); |
| start_time(STEP_INIT_QP); |
| for (i = 0; i < iter; i++) { |
| nodes[i].next_qps = IBV_QPS_INIT; |
| nodes[i].next_step = STEP_INIT_QP_ATTR; |
| wq_insert(&wq, &nodes[i].work, modify_qp_work); |
| } |
| while (atomic_load(&completed[STEP_INIT_QP]) < iter) |
| sched_yield(); |
| end_time(STEP_INIT_QP); |
| |
| printf("\tConnecting\n"); |
| start_time(STEP_CONNECT); |
| for (i = 0; i < iter; i++) |
| connect_qp(&nodes[i]); |
| |
| while (atomic_load(&completed[STEP_CONNECT]) < iter) |
| sched_yield(); |
| end_time(STEP_CONNECT); |
| end_time(STEP_FULL_CONNECT); |
| |
| oob_sendrecv(oob_sock, STEP_CONNECT); |
| |
| printf("\tDisconnecting\n"); |
| start_time(STEP_DISCONNECT); |
| for (i = 0; i < iter; i++) |
| wq_insert(&wq, &nodes[i].work, client_disconnect); |
| |
| while (atomic_load(&completed[STEP_DISCONNECT]) < iter) |
| sched_yield(); |
| end_time(STEP_DISCONNECT); |
| |
| oob_sendrecv(oob_sock, STEP_DISCONNECT); |
| |
| /* Wait for event threads to exit before destroying resources */ |
| printf("\tDestroying QPs\n"); |
| destroy_qps(iter); |
| printf("\tDestroying IDs\n"); |
| destroy_ids(iter); |
| } |
| |
| static void run_client(int iter) |
| { |
| int ret; |
| |
| ret = oob_client_setup(dst_addr, port, &oob_sock); |
| if (ret) |
| exit(EXIT_FAILURE); |
| |
| printf("Client warmup\n"); |
| client_connect(1); |
| |
| if (!mimic) { |
| printf("Connect (%d) QPs test\n", iter); |
| } else { |
| printf("Connect (%d) simulated QPs test (delay %d us)\n", |
| iter, mimic_qp_delay); |
| atomic_store(&cur_qpn, base_qpn); |
| } |
| client_connect(iter); |
| show_perf(iter); |
| |
| printf("Connect (%d) test - no QPs\n", iter); |
| atomic_store(&cur_qpn, base_qpn); |
| mimic_qp_delay = 0; |
| client_connect(iter); |
| show_perf(iter); |
| |
| close(oob_sock); |
| } |
| |
| static void run_server(int iter) |
| { |
| struct rdma_cm_id *listen_id; |
| int ret; |
| |
| /* Make sure we're ready for RDMA prior to any OOB sync */ |
| server_listen(&listen_id); |
| |
| ret = oob_server_setup(src_addr, port, &oob_sock); |
| if (ret) |
| exit(EXIT_FAILURE); |
| |
| |
| printf("Server warmup\n"); |
| server_connect(1); |
| |
| if (!mimic) { |
| printf("Accept (%d) QPs test\n", iter); |
| } else { |
| printf("Accept (%d) simulated QPs test (delay %d us)\n", |
| iter, mimic_qp_delay); |
| atomic_store(&cur_qpn, base_qpn); |
| } |
| server_connect(iter); |
| show_perf(iter); |
| |
| printf("Accept (%d) test - no QPs\n", iter); |
| atomic_store(&cur_qpn, base_qpn); |
| mimic_qp_delay = 0; |
| server_connect(iter); |
| show_perf(iter); |
| |
| close(oob_sock); |
| rdma_destroy_id(listen_id); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| pthread_t event_thread; |
| bool socktest = false; |
| int iter = 100; |
| int op, ret; |
| |
| hints.ai_port_space = RDMA_PS_TCP; |
| hints.ai_qp_type = IBV_QPT_RC; |
| while ((op = getopt(argc, argv, "s:b:c:m:n:p:q:r:St:")) != -1) { |
| switch (op) { |
| case 's': |
| dst_addr = optarg; |
| break; |
| case 'b': |
| src_addr = optarg; |
| break; |
| case 'c': |
| iter = atoi(optarg); |
| break; |
| case 'p': |
| port = optarg; |
| break; |
| case 'q': |
| base_qpn = (uint32_t) atoi(optarg); |
| break; |
| case 'm': |
| mimic_qp_delay = (uint32_t) atoi(optarg); |
| mimic = true; |
| break; |
| case 'n': |
| num_threads = (uint32_t) atoi(optarg); |
| break; |
| case 'r': |
| retries = atoi(optarg); |
| break; |
| case 'S': |
| socktest = true; |
| atomic_store(&cur_qpn, 1); |
| break; |
| case 't': |
| timeout = atoi(optarg); |
| break; |
| default: |
| printf("usage: %s\n", argv[0]); |
| printf("\t[-S] (run socket baseline test)\n"); |
| printf("\t[-s server_address]\n"); |
| printf("\t[-b bind_address]\n"); |
| printf("\t[-c connections]\n"); |
| printf("\t[-p port_number]\n"); |
| printf("\t[-q base_qpn]\n"); |
| printf("\t[-m mimic_qp_delay_us]\n"); |
| printf("\t[-n num_threads]\n"); |
| printf("\t[-r retries]\n"); |
| printf("\t[-t timeout_ms]\n"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| if (!is_client()) |
| hints.ai_flags |= RAI_PASSIVE; |
| ret = get_rdma_addr(src_addr, dst_addr, port, &hints, &rai); |
| if (ret) { |
| perror("get_rdma_addr"); |
| exit(EXIT_FAILURE); |
| } |
| |
| channel = create_event_channel(); |
| if (!channel) { |
| perror("create_event_channel"); |
| exit(EXIT_FAILURE); |
| } |
| |
| ret = pthread_create(&event_thread, NULL, process_events, NULL); |
| if (ret) { |
| perror("pthread_create"); |
| exit(EXIT_FAILURE); |
| } |
| |
| nodes = calloc(iter, sizeof *nodes); |
| if (!nodes) { |
| perror("calloc"); |
| exit(EXIT_FAILURE); |
| } |
| |
| ret = wq_init(&wq, num_threads); |
| if (ret) |
| goto free; |
| |
| if (is_client()) { |
| if (socktest) |
| sock_client(iter); |
| else |
| run_client(iter); |
| } else { |
| if (socktest) |
| sock_server(iter); |
| else |
| run_server(iter); |
| } |
| |
| wq_cleanup(&wq); |
| free: |
| free(nodes); |
| rdma_destroy_event_channel(channel); |
| rdma_freeaddrinfo(rai); |
| return 0; |
| } |