Port of infiniband-diags/src/mcm_rereg_test.c to use umad_sa_mcm.h and libibumad (eliminated use of libibmad) Signed-off-by: Hal Rosenstock <hal@xxxxxxxxxxxx> --- libibumad/tests/CMakeLists.txt | 3 + libibumad/tests/umad_sa_mcm_rereg_test.c | 582 +++++++++++++++++++++++++++++++ 2 files changed, 585 insertions(+) create mode 100644 libibumad/tests/umad_sa_mcm_rereg_test.c diff --git a/libibumad/tests/CMakeLists.txt b/libibumad/tests/CMakeLists.txt index 68bb714..2092b81 100644 --- a/libibumad/tests/CMakeLists.txt +++ b/libibumad/tests/CMakeLists.txt @@ -4,4 +4,7 @@ target_link_libraries(umad_reg2 LINK_PRIVATE ibumad) rdma_test_executable(umad_register2 umad_register2.c) target_link_libraries(umad_register2 LINK_PRIVATE ibumad) +rdma_test_executable(umad_sa_mcm_rereg_test umad_sa_mcm_rereg_test.c) +target_link_libraries(umad_sa_mcm_rereg_test LINK_PRIVATE ibumad) + rdma_test_executable(umad_compile_test umad_compile_test.c) diff --git a/libibumad/tests/umad_sa_mcm_rereg_test.c b/libibumad/tests/umad_sa_mcm_rereg_test.c new file mode 100644 index 0000000..0e82aba --- /dev/null +++ b/libibumad/tests/umad_sa_mcm_rereg_test.c @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2017 Mellanox Technologies Ltd. All rights reserved. + * Copyright (c) 2006-2009 Voltaire, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or 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 AND + * 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 <string.h> +#include <errno.h> +#include <inttypes.h> +#include <unistd.h> + +#include <infiniband/umad.h> +#include <infiniband/umad_sa_mcm.h> + +#define info(fmt, ...) fprintf(stderr, "INFO: " fmt, ## __VA_ARGS__) +#define err(fmt, ...) fprintf(stderr, "ERR: " fmt, ## __VA_ARGS__) +#ifdef NOISY_DEBUG +#define dbg(fmt, ...) fprintf(stderr, "DBG: " fmt, ## __VA_ARGS__) +#else +#define dbg(fmt, ...) {} +#endif + +#define DEFAULT_TIMEOUT 100 /* milliseconds */ +#define MAX_PORT_GUIDS 64 + +/* Use null MGID to request SA assigned MGID */ +static const uint8_t null_mgid[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static int create, join, leave; +static uint8_t rate = 0xff, mtu = 0xff, sl = 0xff; +static umad_port_t umad_port; + +struct guid_trid { + uint8_t gid[16]; + __be64 guid; + uint64_t trid[2]; +}; + +static void build_user_mad_addr(uint8_t *umad) +{ + umad_set_addr(umad, umad_port.sm_lid, 1, umad_port.sm_sl, UMAD_QKEY); + + /* + * The following 2 umad calls are redundant + * as umad was originally cleared to + */ + umad_set_grh(umad, NULL); + umad_set_pkey(umad, 0); /* just pkey index 0 for now !!! */ +} + +static void build_mcm_rec(struct umad_sa_packet *sa, uint8_t method, + const uint8_t mgid[], const uint8_t port_gid[], + uint64_t tid, int creat) +{ + struct umad_sa_mcmember_record *mcm; + + memset(sa, 0, sizeof(*sa)); + + sa->mad_hdr.base_version = UMAD_BASE_VERSION; + sa->mad_hdr.mgmt_class = UMAD_CLASS_SUBN_ADM; + sa->mad_hdr.class_version = UMAD_SA_CLASS_VERSION; + sa->mad_hdr.method = method; + sa->mad_hdr.tid = htobe64(tid); + sa->mad_hdr.attr_id = htons(UMAD_SA_ATTR_MCMEMBER_REC); + if (creat) + sa->comp_mask = htobe64(UMAD_SA_MCM_COMP_MASK_MGID | + UMAD_SA_MCM_COMP_MASK_PORT_GID | + UMAD_SA_MCM_COMP_MASK_QKEY | + UMAD_SA_MCM_COMP_MASK_TCLASS | + UMAD_SA_MCM_COMP_MASK_PKEY | + UMAD_SA_MCM_COMP_MASK_SL | + UMAD_SA_MCM_COMP_MASK_FLOW_LABEL | + UMAD_SA_MCM_COMP_MASK_JOIN_STATE); + else + sa->comp_mask = htobe64(UMAD_SA_MCM_COMP_MASK_MGID | + UMAD_SA_MCM_COMP_MASK_PORT_GID | + UMAD_SA_MCM_COMP_MASK_JOIN_STATE); + + mcm = (struct umad_sa_mcmember_record *) sa->data; + memcpy(mcm->mgid, mgid, sizeof(mcm->mgid)); + memcpy(mcm->portgid, port_gid, sizeof(mcm->portgid)); + umad_sa_mcm_set_join_state(mcm, UMAD_SA_MCM_JOIN_STATE_FULL_MEMBER); + if (creat) { + mcm->qkey = htonl(0xb1b); + /* assume full default partition (in index 0) */ + mcm->pkey = htons(0xffff); + if (rate != 0xff) { + sa->comp_mask |= + htobe64(UMAD_SA_MCM_COMP_MASK_RATE_SEL | + UMAD_SA_MCM_COMP_MASK_RATE); + mcm->rate = (UMAD_SA_MCM_SELECTOR_EXACTLY << + UMAD_SA_MCM_SELECTOR_SHIFT) | + (rate & UMAD_SA_MCM_RATE_MTU_PKT_LIFE_MASK); + } + if (mtu != 0xff) { + sa->comp_mask |= htobe64(UMAD_SA_MCM_COMP_MASK_MTU_SEL | + UMAD_SA_MCM_COMP_MASK_MTU); + mcm->mtu = (UMAD_SA_MCM_SELECTOR_EXACTLY << + UMAD_SA_MCM_SELECTOR_SHIFT) | + (mtu & UMAD_SA_MCM_RATE_MTU_PKT_LIFE_MASK); + } + if (sl != 0xff) { + sa->comp_mask |= htobe64(UMAD_SA_MCM_COMP_MASK_SL); + mcm->sl_flow_hop = + umad_sa_mcm_set_sl_flow_hop(sl, 0, 0); + } + } +} + +static int mcm_send(int portid, int agentid, uint8_t *umad, int len, int tmo, + uint8_t method, const uint8_t mgid[], + struct guid_trid *entry, int creat) +{ + struct umad_sa_packet *sa = umad_get_mad(umad); + + build_mcm_rec(sa, method, mgid, entry->gid, entry->trid[0], creat); + if (umad_send(portid, agentid, umad, len, tmo, 0) < 0) { + err("umad_send %s failed: %s\n", + (method == UMAD_METHOD_GET) ? "query" : "non query", + strerror(errno)); + return -1; + } + dbg("umad_send %d: tid = 0x%" PRIx64 "\n", method, + be64toh(sa->mad_hdr.tid)); + + return 0; +} + +static int rereg_port_gid(int portid, int agentid, + uint8_t *umad, int len, int tmo, + const uint8_t mgid[], struct guid_trid *entry) +{ + struct umad_sa_packet *sa = umad_get_mad(umad); + + build_mcm_rec(sa, UMAD_SA_METHOD_DELETE, mgid, + entry->gid, entry->trid[0], 0); + if (umad_send(portid, agentid, umad, len, tmo, 0) < 0) { + err("umad_send leave failed: %s\n", strerror(errno)); + return -1; + } + dbg("umad_send leave: tid = 0x%" PRIx64 "\n", be64toh(sa->mad_hdr.tid)); + entry->trid[0] = be64toh(sa->mad_hdr.tid); /* for agent ID */ + + sa->mad_hdr.method = UMAD_METHOD_SET; + sa->mad_hdr.tid = htobe64(entry->trid[1]); + if (umad_send(portid, agentid, umad, len, tmo, 0) < 0) { + err("umad_send join failed: %s\n", strerror(errno)); + return -1; + } + dbg("umad_send join: tid = 0x%" PRIx64 "\n", be64toh(sa->mad_hdr.tid)); + entry->trid[1] = be64toh(sa->mad_hdr.tid); /* for agent ID */ + + return 0; +} + +static int rereg_send_all(int portid, int agentid, int tmo, + const uint8_t mgid[], struct guid_trid *list, + unsigned int cnt) +{ + uint8_t *umad; + int len = sizeof(struct umad_hdr) + UMAD_LEN_DATA; + unsigned int i, sent = 0; + int ret; + + info("%s... cnt = %u\n", __func__, cnt); + + umad = calloc(1, len + umad_size()); + if (!umad) { + err("cannot alloc mem for umad: %s\n", strerror(errno)); + return -1; + } + build_user_mad_addr(umad); + + for (i = 0; i < cnt; i++) { + ret = rereg_port_gid(portid, agentid, umad, len, tmo, + mgid, &list[i]); + if (ret < 0) { + err("%s: rereg_port_gid guid 0x%016" PRIx64 + " failed\n", __func__, be64toh(list[i].guid)); + continue; + } + sent++; + } + + info("%s: sent %u of %u requests\n", __func__, sent * 2, cnt * 2); + + free(umad); + + return 0; +} + +static int mcm_recv(int portid, uint8_t *umad, int length, int tmo) +{ + int ret, retry = 0; + int len = length; +#ifdef NOISY_DEBUG + struct umad_hdr *mad; +#endif + + while ((ret = umad_recv(portid, umad, &len, tmo)) < 0 && + errno == ETIMEDOUT) { + if (retry++ > 3) + return 0; + } + if (ret < 0) { + err("umad_recv %d failed: %s\n", ret, strerror(errno)); + return -1; + } + +#ifdef NOISY_DEBUG + mad = umad_get_mad(umad); +#endif + dbg("umad_recv (retries %d), tid = 0x%" PRIx64 + ": len = %d, status = %d\n", retry, + be64toh(mad->tid), len, umad_status(umad)); + + return 1; +} + +static int rereg_recv_all(int portid, int agentid, int tmo, + const uint8_t mgid[], struct guid_trid *list, + unsigned int cnt) +{ + uint8_t *umad; + struct umad_hdr *mad; + int len = sizeof(struct umad_hdr) + UMAD_LEN_DATA; + uint64_t trid; + unsigned int n, i, j; + uint16_t status; + uint8_t method; + + info("%s...\n", __func__); + + umad = calloc(1, len + umad_size()); + if (!umad) { + err("cannot alloc mem for umad: %s\n", strerror(errno)); + return -1; + } + mad = umad_get_mad(umad); + + n = 0; + while (mcm_recv(portid, umad, len, tmo) > 0) { + dbg("%s: done %d\n", __func__, n); + n++; + + method = mad->method; + status = ntohs(mad->status); + trid = be64toh(mad->tid); + + if (status) + dbg("MAD status 0x%x, method 0x%x\n", status, method); + + if (status && + (method == UMAD_METHOD_GET_RESP || + method == UMAD_SA_METHOD_DELETE_RESP)) { + for (i = 0; i < cnt; i++) + for (j = 0; j < 2; j++) + if (trid == list[i].trid[j]) + break; + if (i == cnt) { + err("cannot find trid 0x%" PRIx64 + ", status 0x%x, method 0x%x\n", + trid, status, method); + continue; + } + info("guid 0x%016" PRIx64 + ": status 0x%x, method 0x%x. Retrying\n", + be64toh(list[i].guid), status, method); + rereg_port_gid(portid, agentid, umad, len, tmo, + mgid, &list[i]); + } + } + + info("%s: got %u responses\n", __func__, n); + + free(umad); + return 0; +} + +static int query_all(int portid, int agentid, int tmo, uint8_t method, + const uint8_t mgid[], struct guid_trid *list, + int creat, unsigned int cnt) +{ + uint8_t *umad; + struct umad_hdr *mad; + int len = sizeof(struct umad_hdr) + UMAD_LEN_DATA; + unsigned int i, sent = 0; + int ret; + uint16_t status; + uint8_t mcgid[16]; + + info("%s...\n", __func__); + + memcpy(mcgid, mgid, 16); + + umad = calloc(1, len + umad_size()); + if (!umad) { + err("cannot alloc mem for umad: %s\n", strerror(errno)); + return -1; + } + build_user_mad_addr(umad); + mad = umad_get_mad(umad); + + for (i = 0; i < cnt; i++) { + ret = mcm_send(portid, agentid, umad, len, tmo, + method, mcgid, &list[i], creat); + if (ret < 0) { + err("%s: mcm_send failed\n", __func__); + continue; + } + sent++; + + ret = mcm_recv(portid, umad, len, tmo); + if (ret < 0) { + err("%s: mcm_recv failed\n", __func__); + continue; + } + + status = ntohs(mad->status); + if (status) + info( + "guid 0x%016" PRIx64 ": status 0x%x, method 0x%x\n", + be64toh(list[i].guid), status, mad->method); + else if (creat && i == 0) { + if (memcmp(mgid, null_mgid, 16) == 0) { + struct umad_sa_packet *sa = (void *) mad; + struct umad_sa_mcmember_record *mcm; + + mcm = (struct umad_sa_mcmember_record *) + sa->data; + memcpy(mcgid, mcm->mgid, 16); + } + } + } + + info("%s: %u of %u queried\n", __func__, sent, cnt); + + free(umad); + return 0; +} + +static int test_port(const char *guid_file, int portid, int agentid, int tmo, + const uint8_t mgid[]) +{ + char line[256]; + FILE *f; + uint8_t port_gid[16]; + uint64_t guidho; + __be64 prefix, guid; + uint64_t trid; + struct guid_trid *list; + int i = 0, j; + + list = calloc(MAX_PORT_GUIDS, sizeof(*list)); + if (!list) { + err("cannot alloc mem for guid/trid list: %s\n", + strerror(errno)); + return -1; + } + + f = fopen(guid_file, "r"); + if (!f) { + err("cannot open %s: %s\n", guid_file, strerror(errno)); + return -1; + } + + trid = 0x12345678; /* starting tid */ + prefix = umad_port.gid_prefix; + + while (fgets(line, sizeof(line), f)) { + guidho = strtoull(line, NULL, 0); + guid = htobe64(guidho); + memcpy(&port_gid[0], &prefix, 8); + memcpy(&port_gid[8], &guid, 8); + + list[i].guid = guid; + memcpy(list[i].gid, port_gid, sizeof(list[i].gid)); + for (j = 0; j < 2; j++) + list[i].trid[j] = trid++; + + if (++i >= MAX_PORT_GUIDS) + break; + } + fclose(f); + + if (create) + query_all(portid, agentid, tmo, UMAD_METHOD_SET, + mgid, list, 1, i); + else if (join) + query_all(portid, agentid, tmo, UMAD_METHOD_SET, + mgid, list, 0, i); + else if (leave) + query_all(portid, agentid, tmo, UMAD_SA_METHOD_DELETE, + mgid, list, 0, i); + else { + /* no operation specified - default to rereg */ + rereg_send_all(portid, agentid, tmo, mgid, list, i); + rereg_recv_all(portid, agentid, tmo, mgid, list, i); + + query_all(portid, agentid, tmo, UMAD_METHOD_GET, + mgid, list, 0, i); + } + + free(list); + return 0; +} + +static void show_usage(const char *prog_name) +{ + fprintf(stderr, + "%s [-C <ca_name>] [-P <ca_port>] [-F <port_guid_file>] [-t <timeout_ms>] [-g <mgid>] [-c] [-j] [-l] [-r <rate>] [-m <mtu>] [-s <sl>] [-h]\n", + prog_name); + fprintf(stderr, + " -C <ca_name> use the specified ca_name\n"); + fprintf(stderr, + " -P <ca_port> use the specific ca_port\n"); + fprintf(stderr, + " -F <port_guid_file> use the specified port_guid_file\n"); + fprintf(stderr, + " defaults to port_guids.lst\n"); + fprintf(stderr, + " -t <timeout_ms> override the default timeout of 100 milliseconds\n"); + fprintf(stderr, + " -g <mgid> MGID of MC group in IPv6 format\n"); + fprintf(stderr, + " defaults to IPv4 broadcast group if not specified\n"); + fprintf(stderr, + " To create SA assigned group, use either :: or 0:0:0:0:0:0:0:0\n"); + fprintf(stderr, " -c create MC group with ports\n"); + fprintf(stderr, " -j join ports to MC group\n"); + fprintf(stderr, + " -l remove ports from MC group (leave)\n"); + fprintf(stderr, + " operation defaults to reregister ports if none if c, j, l are specified\n\n"); + fprintf(stderr, + " -r <rate> Encoded rate value (for create)\n"); + fprintf(stderr, + " -m <mtu> Encoded mtu value (for create)\n"); + fprintf(stderr, " -s <sl> SL (for create)\n"); + fprintf(stderr, " -h show this usage message\n"); +} + +int main(int argc, char **argv) +{ + char *ibd_ca = NULL; + int ibd_ca_port = 0; + const char *guid_file = "port_guids.list"; + int tmo = DEFAULT_TIMEOUT; + int c, portid, agentid; + const char *prog_name; + const char *const optstring = "F:C:P:t:g:cjlr:m:s:h"; + /* IPoIB broadcast group (for full default pkey) */ + uint8_t mgid[16] = { + 0xff, 0x12, 0x40, 0x1b, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff + }; + + prog_name = argv[0]; + while ((c = getopt(argc, argv, optstring)) != -1) { + switch (c) { + case 'C': + ibd_ca = optarg; + break; + case 'P': + ibd_ca_port = strtoul(optarg, NULL, 0); + break; + case 'F': + guid_file = optarg; + break; + case 't': + tmo = atoi(optarg); + break; + case 'g': + if (inet_pton(AF_INET6, optarg, &mgid) <= 0) { + fprintf(stderr, "mgid could not be parsed\n"); + exit(EXIT_FAILURE); + } + break; + case 'c': + create = 1; + break; + case 'j': + join = 1; + break; + case 'l': + leave = 1; + break; + case 'r': + rate = atoi(optarg); + break; + case 'm': + mtu = atoi(optarg); + break; + case 's': + sl = atoi(optarg); + break; + case 'h': + show_usage(prog_name); + exit(EXIT_SUCCESS); + break; + default: + fprintf(stderr, "Unrecognized option: -%c\n", optopt); + show_usage(prog_name); + exit(EXIT_FAILURE); + break; + } + } + + if (umad_get_port(ibd_ca, ibd_ca_port, &umad_port) < 0) { + if (ibd_ca == NULL) + err( + "umad_get_port failed for first IB CA port %d: %s\n", + ibd_ca_port, strerror(errno)); + else + err("umad_get_port failed for CA %s port %d: %s\n", + ibd_ca, ibd_ca_port, strerror(errno)); + umad_done(); + return -1; + } + info("using %s port %d guid 0x%016" PRIx64 "\n", + umad_port.ca_name, umad_port.portnum, + be64toh(umad_port.port_guid)); + + portid = umad_open_port(umad_port.ca_name, umad_port.portnum); + if (portid < 0) { + err("umad_open_port failed: %s\n", strerror(errno)); + umad_release_port(&umad_port); + umad_done(); + return -1; + } + + agentid = umad_register(portid, UMAD_CLASS_SUBN_ADM, + UMAD_SA_CLASS_VERSION, 0, NULL); + if (agentid < 0) { + err("umad_register failed: %s\n", strerror(errno)); + umad_release_port(&umad_port); + umad_close_port(portid); + umad_done(); + return -1; + } + + test_port(guid_file, portid, agentid, tmo, mgid); + + umad_release_port(&umad_port); + umad_unregister(portid, agentid); + umad_close_port(portid); + umad_done(); + + return 0; +} -- 2.8.4 -- To unsubscribe from this list: send the line "unsubscribe linux-rdma" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html