| /* |
| * Copyright (c) 2004-2009 Voltaire Inc. All rights reserved. |
| * Copyright (c) 2014 Intel Corporation. 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 <config.h> |
| |
| #include <stdbool.h> |
| #include <sys/poll.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <sys/ioctl.h> |
| #include <dirent.h> |
| #include <ctype.h> |
| #include <inttypes.h> |
| #include <util/compiler.h> |
| |
| #include <infiniband/umad.h> |
| |
| #define IB_OPENIB_OUI (0x001405) |
| #define CAPMASK_IS_SM_DISABLED (0x400) |
| |
| #include <valgrind/memcheck.h> |
| #include "sysfs.h" |
| |
| typedef struct ib_user_mad_reg_req { |
| uint32_t id; |
| uint32_t method_mask[4]; |
| uint8_t qpn; |
| uint8_t mgmt_class; |
| uint8_t mgmt_class_version; |
| uint8_t oui[3]; |
| uint8_t rmpp_version; |
| } ib_user_mad_reg_req_t; |
| |
| struct ib_user_mad_reg_req2 { |
| uint32_t id; |
| uint32_t qpn; |
| uint8_t mgmt_class; |
| uint8_t mgmt_class_version; |
| uint16_t res; |
| uint32_t flags; |
| uint64_t method_mask[2]; |
| uint32_t oui; |
| uint8_t rmpp_version; |
| uint8_t reserved[3]; |
| }; |
| |
| struct port_guid_port_count { |
| __be64 port_guid; |
| uint8_t count; |
| }; |
| |
| struct guid_ca_pairs_mapping { |
| __be64 port_guid; |
| struct umad_ca_pair *ca_pair; |
| }; |
| |
| #define IBWARN(fmt, args...) fprintf(stderr, "ibwarn: [%d] %s: " fmt "\n", getpid(), __func__, ## args) |
| |
| #define TRACE if (umaddebug) IBWARN |
| #define DEBUG if (umaddebug) IBWARN |
| |
| static int umaddebug = 0; |
| |
| #define UMAD_DEV_FILE_SZ 256 |
| |
| static const char *def_ca_name = "mthca0"; |
| static int def_ca_port = 1; |
| |
| static unsigned new_user_mad_api; |
| |
| static unsigned int get_abi_version(void) |
| { |
| static unsigned int abi_version; |
| |
| if (abi_version != 0) |
| return abi_version & 0x7FFFFFFF; |
| |
| if (sys_read_uint(IB_UMAD_ABI_DIR, IB_UMAD_ABI_FILE, &abi_version) < |
| 0) { |
| IBWARN("can't read ABI version from %s/%s (%m): is ib_umad module loaded?", |
| IB_UMAD_ABI_DIR, IB_UMAD_ABI_FILE); |
| abi_version = (1U) << 31; |
| return 0; |
| } |
| |
| if (abi_version < IB_UMAD_ABI_VERSION) { |
| abi_version = (1U) << 31; |
| return 0; |
| } |
| |
| return abi_version; |
| } |
| |
| /************************************* |
| * Port |
| */ |
| static int find_cached_ca(const char *ca_name, umad_ca_t * ca) |
| { |
| return 0; /* caching not implemented yet */ |
| } |
| |
| static int put_ca(umad_ca_t * ca) |
| { |
| return 0; /* caching not implemented yet */ |
| } |
| |
| static unsigned is_smi_disabled(umad_port_t *port) |
| { |
| return (be32toh(port->capmask) & CAPMASK_IS_SM_DISABLED); |
| } |
| |
| static int release_port(umad_port_t * port) |
| { |
| free(port->pkeys); |
| port->pkeys = NULL; |
| port->pkeys_size = 0; |
| return 0; |
| } |
| |
| static int check_for_digit_name(const struct dirent *dent) |
| { |
| const char *p = dent->d_name; |
| while (*p && isdigit(*p)) |
| p++; |
| return *p ? 0 : 1; |
| } |
| |
| static int get_port(const char *ca_name, const char *dir, int portnum, umad_port_t * port) |
| { |
| char port_dir[256]; |
| union umad_gid gid; |
| struct dirent **namelist; |
| int i, len, num_pkeys = 0; |
| uint32_t capmask; |
| |
| strncpy(port->ca_name, ca_name, sizeof port->ca_name - 1); |
| port->portnum = portnum; |
| port->pkeys = NULL; |
| |
| len = snprintf(port_dir, sizeof(port_dir), "%s/%d", dir, portnum); |
| if (len < 0 || len > sizeof(port_dir)) |
| return -EIO; |
| |
| if (sys_read_uint(port_dir, SYS_PORT_LMC, &port->lmc) < 0) |
| return -EIO; |
| if (sys_read_uint(port_dir, SYS_PORT_SMLID, &port->sm_lid) < 0) |
| return -EIO; |
| if (sys_read_uint(port_dir, SYS_PORT_SMSL, &port->sm_sl) < 0) |
| return -EIO; |
| if (sys_read_uint(port_dir, SYS_PORT_LID, &port->base_lid) < 0) |
| return -EIO; |
| if (sys_read_uint(port_dir, SYS_PORT_STATE, &port->state) < 0) |
| return -EIO; |
| if (sys_read_uint(port_dir, SYS_PORT_PHY_STATE, &port->phys_state) < 0) |
| return -EIO; |
| if (sys_read_uint(port_dir, SYS_PORT_RATE, &port->rate) < 0) |
| return -EIO; |
| if (sys_read_uint(port_dir, SYS_PORT_CAPMASK, &capmask) < 0) |
| return -EIO; |
| |
| if (sys_read_string(port_dir, SYS_PORT_LINK_LAYER, |
| port->link_layer, UMAD_CA_NAME_LEN) < 0) |
| /* assume IB by default */ |
| sprintf(port->link_layer, "IB"); |
| |
| port->capmask = htobe32(capmask); |
| |
| if (sys_read_gid(port_dir, SYS_PORT_GID, &gid) < 0) |
| return -EIO; |
| |
| port->gid_prefix = gid.global.subnet_prefix; |
| port->port_guid = gid.global.interface_id; |
| |
| snprintf(port_dir + len, sizeof(port_dir) - len, "/pkeys"); |
| num_pkeys = scandir(port_dir, &namelist, check_for_digit_name, NULL); |
| if (num_pkeys <= 0) { |
| IBWARN("no pkeys found for %s:%u (at dir %s)...", |
| port->ca_name, port->portnum, port_dir); |
| return -EIO; |
| } |
| port->pkeys = calloc(num_pkeys, sizeof(port->pkeys[0])); |
| if (!port->pkeys) { |
| IBWARN("get_port: calloc failed: %s", strerror(errno)); |
| goto clean_names; |
| } |
| for (i = 0; i < num_pkeys; i++) { |
| unsigned idx, val; |
| |
| idx = strtoul(namelist[i]->d_name, NULL, 0); |
| if (sys_read_uint(port_dir, namelist[i]->d_name, &val) < 0) |
| goto clean_pkeys; |
| port->pkeys[idx] = val; |
| } |
| port->pkeys_size = num_pkeys; |
| for (i = 0; i < num_pkeys; i++) |
| free(namelist[i]); |
| free(namelist); |
| port_dir[len] = '\0'; |
| |
| /* FIXME: handle gids */ |
| |
| return 0; |
| |
| clean_pkeys: |
| free(port->pkeys); |
| clean_names: |
| for (i = 0; i < num_pkeys; i++) |
| free(namelist[i]); |
| free(namelist); |
| return -EIO; |
| } |
| |
| static int release_ca(umad_ca_t * ca) |
| { |
| int i; |
| |
| for (i = 0; i <= ca->numports; i++) { |
| if (!ca->ports[i]) |
| continue; |
| release_port(ca->ports[i]); |
| free(ca->ports[i]); |
| ca->ports[i] = NULL; |
| } |
| return 0; |
| } |
| |
| /* |
| * if *port > 0, check ca[port] state. Otherwise set *port to |
| * the first port that is active, and if such is not found, to |
| * the first port that is link up and if none are linkup, then |
| * the first port that is not disabled. Otherwise return -1. |
| * if enforce_smi > 0, only search smi ports. if none are found, return -1. |
| */ |
| static int resolve_ca_port(const char *ca_name, int *port, unsigned enforce_smi) |
| { |
| umad_ca_t ca; |
| int active = -1, up = -1; |
| int i, ret = 0; |
| |
| TRACE("checking ca '%s'", ca_name); |
| |
| if (umad_get_ca(ca_name, &ca) < 0) |
| return -1; |
| |
| if (ca.node_type == 2) { |
| *port = 0; /* switch sma port 0 */ |
| ret = 1; |
| goto Exit; |
| } |
| |
| if (*port > 0) { /* check only the port the user wants */ |
| if (*port > ca.numports) { |
| ret = -1; |
| goto Exit; |
| } |
| if (!ca.ports[*port]) { |
| ret = -1; |
| goto Exit; |
| } |
| if (strcmp(ca.ports[*port]->link_layer, "InfiniBand") && |
| strcmp(ca.ports[*port]->link_layer, "IB")) { |
| ret = -1; |
| goto Exit; |
| } |
| if (enforce_smi && is_smi_disabled(ca.ports[*port])) { |
| ret = -1; |
| goto Exit; |
| } |
| if (ca.ports[*port]->state == 4) { |
| ret = 1; |
| goto Exit; |
| } |
| if (ca.ports[*port]->phys_state != 3) |
| goto Exit; |
| ret = -1; |
| goto Exit; |
| } |
| |
| for (i = 0; i <= ca.numports; i++) { |
| DEBUG("checking port %d", i); |
| if (!ca.ports[i]) |
| continue; |
| if (strcmp(ca.ports[i]->link_layer, "InfiniBand") && |
| strcmp(ca.ports[i]->link_layer, "IB")) |
| continue; |
| if (enforce_smi && is_smi_disabled(ca.ports[i])) |
| continue; |
| if (up < 0 && ca.ports[i]->phys_state == 5) |
| up = *port = i; |
| if (ca.ports[i]->state == 4) { |
| active = *port = i; |
| DEBUG("found active port %d", i); |
| break; |
| } |
| } |
| |
| if (active == -1 && up == -1) { /* no active or linkup port found */ |
| for (i = 0; i <= ca.numports; i++) { |
| DEBUG("checking port %d", i); |
| if (!ca.ports[i]) |
| continue; |
| if (enforce_smi && is_smi_disabled(ca.ports[i])) |
| continue; |
| if (ca.ports[i]->phys_state != 3) { |
| up = *port = i; |
| break; |
| } |
| } |
| } |
| |
| if (active >= 0) { |
| ret = 1; |
| goto Exit; |
| } |
| if (up >= 0) { |
| ret = 0; |
| goto Exit; |
| } |
| ret = -1; |
| Exit: |
| release_ca(&ca); |
| return ret; |
| } |
| |
| static int resolve_ca_name(const char *ca_in, int *best_port, |
| char **ca_name, unsigned enforce_smi) |
| { |
| struct umad_device_node *device_list; |
| struct umad_device_node *node; |
| struct umad_device_node *phys_found = NULL; |
| const char *name_found; |
| int port_found = 0, port, port_type; |
| |
| *ca_name = NULL; |
| if (ca_in && (!best_port || *best_port)) { |
| *ca_name = strdup(ca_in); |
| if (!(*ca_name)) |
| return -1; |
| return 0; |
| } |
| |
| if (ca_in) { |
| if (resolve_ca_port(ca_in, best_port, enforce_smi) < 0) |
| return -1; |
| *ca_name = strdup(ca_in); |
| if (!(*ca_name)) |
| return -1; |
| return 0; |
| } |
| |
| /* Get the list of CA names */ |
| device_list = umad_get_ca_device_list(); |
| if (!device_list) |
| return -1; |
| |
| /* Find the first existing CA with an active port */ |
| for (node = device_list; node; node = node->next) { |
| name_found = node->ca_name; |
| |
| TRACE("checking ca '%s'", name_found); |
| |
| port = best_port ? *best_port : 0; |
| port_type = resolve_ca_port(name_found, &port, enforce_smi); |
| if (port_type < 0) |
| continue; |
| |
| DEBUG("found ca %s with port %d type %d", |
| name_found, port, port_type); |
| |
| if (port_type > 0) { |
| if (best_port) |
| *best_port = port; |
| DEBUG("found ca %s with active port %d", |
| name_found, port); |
| *ca_name = strdup(name_found); |
| umad_free_ca_device_list(device_list); |
| if (!(*ca_name)) |
| return -1; |
| return 0; |
| } |
| |
| if (!phys_found) { |
| phys_found = node; |
| port_found = port; |
| } |
| } |
| |
| DEBUG("phys found on %s port %d", |
| phys_found ? phys_found->ca_name : NULL, |
| port_found); |
| |
| if (phys_found) { |
| name_found = phys_found->ca_name; |
| DEBUG("phys found on %s port %d", |
| phys_found ? name_found : NULL, |
| port_found); |
| if (best_port) |
| *best_port = port_found; |
| *ca_name = strdup(name_found); |
| umad_free_ca_device_list(device_list); |
| if (!(*ca_name)) |
| return -1; |
| return 0; |
| } |
| |
| umad_free_ca_device_list(device_list); |
| |
| if (best_port) |
| *best_port = def_ca_port; |
| |
| *ca_name = strdup(def_ca_name); |
| if (!(*ca_name)) |
| return -1; |
| return 0; |
| } |
| |
| static int get_ca(const char *ca_name, umad_ca_t * ca) |
| { |
| DIR *dir; |
| char dir_name[256]; |
| struct dirent **namelist; |
| int r, i, ret; |
| int portnum; |
| |
| ca->numports = 0; |
| memset(ca->ports, 0, sizeof ca->ports); |
| strncpy(ca->ca_name, ca_name, sizeof(ca->ca_name) - 1); |
| |
| snprintf(dir_name, sizeof(dir_name), "%s/%s", SYS_INFINIBAND, |
| ca->ca_name); |
| |
| if ((r = sys_read_uint(dir_name, SYS_NODE_TYPE, &ca->node_type)) < 0) |
| return r; |
| if (sys_read_string(dir_name, SYS_CA_FW_VERS, ca->fw_ver, |
| sizeof ca->fw_ver) < 0) |
| ca->fw_ver[0] = '\0'; |
| if (sys_read_string(dir_name, SYS_CA_HW_VERS, ca->hw_ver, |
| sizeof ca->hw_ver) < 0) |
| ca->hw_ver[0] = '\0'; |
| if ((r = sys_read_string(dir_name, SYS_CA_TYPE, ca->ca_type, |
| sizeof ca->ca_type)) < 0) |
| ca->ca_type[0] = '\0'; |
| if ((r = sys_read_guid(dir_name, SYS_CA_NODE_GUID, &ca->node_guid)) < 0) |
| return r; |
| if ((r = |
| sys_read_guid(dir_name, SYS_CA_SYS_GUID, &ca->system_guid)) < 0) |
| return r; |
| |
| snprintf(dir_name, sizeof(dir_name), "%s/%s/%s", |
| SYS_INFINIBAND, ca->ca_name, SYS_CA_PORTS_DIR); |
| |
| if (!(dir = opendir(dir_name))) |
| return -ENOENT; |
| |
| if ((r = scandir(dir_name, &namelist, NULL, alphasort)) < 0) { |
| ret = errno < 0 ? errno : -EIO; |
| goto error; |
| } |
| |
| ret = 0; |
| for (i = 0; i < r; i++) { |
| portnum = 0; |
| if (!strcmp(".", namelist[i]->d_name) || |
| !strcmp("..", namelist[i]->d_name)) |
| continue; |
| if (strcmp("0", namelist[i]->d_name) && |
| ((portnum = atoi(namelist[i]->d_name)) <= 0 || |
| portnum >= UMAD_CA_MAX_PORTS)) { |
| ret = -EIO; |
| goto clean; |
| } |
| if (!(ca->ports[portnum] = |
| calloc(1, sizeof(*ca->ports[portnum])))) { |
| ret = -ENOMEM; |
| goto clean; |
| } |
| if (get_port(ca_name, dir_name, portnum, ca->ports[portnum]) < |
| 0) { |
| free(ca->ports[portnum]); |
| ca->ports[portnum] = NULL; |
| ret = -EIO; |
| goto clean; |
| } |
| if (ca->numports < portnum) |
| ca->numports = portnum; |
| } |
| |
| for (i = 0; i < r; i++) |
| free(namelist[i]); |
| free(namelist); |
| |
| closedir(dir); |
| put_ca(ca); |
| return 0; |
| |
| clean: |
| for (i = 0; i < r; i++) |
| free(namelist[i]); |
| free(namelist); |
| error: |
| closedir(dir); |
| release_ca(ca); |
| |
| return ret; |
| } |
| |
| static int umad_id_to_dev(int umad_id, char *dev, unsigned *port) |
| { |
| char path[256]; |
| int r; |
| |
| snprintf(path, sizeof(path), SYS_INFINIBAND_MAD "/umad%d/", umad_id); |
| |
| if ((r = |
| sys_read_string(path, SYS_IB_MAD_DEV, dev, UMAD_CA_NAME_LEN)) < 0) |
| return r; |
| |
| if ((r = sys_read_uint(path, SYS_IB_MAD_PORT, port)) < 0) |
| return r; |
| |
| return 0; |
| } |
| |
| static int dev_to_umad_id(const char *dev, unsigned port) |
| { |
| char umad_dev[UMAD_CA_NAME_LEN]; |
| unsigned umad_port; |
| int id; |
| |
| for (id = 0; id < UMAD_MAX_PORTS; id++) { |
| if (umad_id_to_dev(id, umad_dev, &umad_port) < 0) |
| continue; |
| if (strncmp(dev, umad_dev, UMAD_CA_NAME_LEN)) |
| continue; |
| if (port != umad_port) |
| continue; |
| |
| DEBUG("mapped %s %d to %d", dev, port, id); |
| return id; |
| } |
| |
| return -1; /* not found */ |
| } |
| |
| static int umad_ca_device_list_compare_function(const void *node_a, |
| const void *node_b) |
| { |
| return strcmp((*((const struct umad_device_node **)node_a))->ca_name, |
| (*((const struct umad_device_node **)node_b))->ca_name); |
| } |
| |
| /******************************* |
| * Public interface |
| */ |
| |
| int umad_init(void) |
| { |
| TRACE("umad_init"); |
| return 0; |
| } |
| |
| int umad_done(void) |
| { |
| TRACE("umad_done"); |
| /* FIXME - verify that all ports are closed */ |
| return 0; |
| } |
| |
| static unsigned is_ib_type(const char *ca_name) |
| { |
| char dir_name[256]; |
| unsigned type; |
| |
| snprintf(dir_name, sizeof(dir_name), "%s/%s", SYS_INFINIBAND, ca_name); |
| |
| if (sys_read_uint(dir_name, SYS_NODE_TYPE, &type) < 0) |
| return 0; |
| |
| return type >= 1 && type <= 3 ? 1 : 0; |
| } |
| |
| int umad_get_cas_names(char cas[][UMAD_CA_NAME_LEN], int max) |
| { |
| struct dirent **namelist; |
| int n, i, j = 0; |
| |
| TRACE("max %d", max); |
| |
| n = scandir(SYS_INFINIBAND, &namelist, NULL, alphasort); |
| if (n > 0) { |
| for (i = 0; i < n; i++) { |
| if (strcmp(namelist[i]->d_name, ".") && |
| strcmp(namelist[i]->d_name, "..") && |
| strlen(namelist[i]->d_name) < UMAD_CA_NAME_LEN) { |
| if (j < max && is_ib_type(namelist[i]->d_name)) |
| strcpy(cas[j++], namelist[i]->d_name); |
| } |
| free(namelist[i]); |
| } |
| DEBUG("return %d cas", j); |
| } else { |
| /* Is this still needed ? */ |
| strncpy((char *)cas, def_ca_name, UMAD_CA_NAME_LEN); |
| DEBUG("return 1 ca"); |
| j = 1; |
| } |
| if (n >= 0) |
| free(namelist); |
| return j; |
| } |
| |
| int umad_get_ca_portguids(const char *ca_name, __be64 *portguids, int max) |
| { |
| umad_ca_t ca; |
| int ports = 0, i, result; |
| char *found_ca_name; |
| |
| TRACE("ca name %s max port guids %d", ca_name, max); |
| if (resolve_ca_name(ca_name, NULL, &found_ca_name, 0) < 0) { |
| result = -ENODEV; |
| goto exit; |
| } |
| |
| if (umad_get_ca(found_ca_name, &ca) < 0) { |
| result = -1; |
| goto exit; |
| } |
| |
| if (portguids) { |
| if (ca.numports + 1 > max) { |
| result = -ENOMEM; |
| goto clean; |
| } |
| |
| for (i = 0; i <= ca.numports; i++) |
| portguids[ports++] = ca.ports[i] ? |
| ca.ports[i]->port_guid : htobe64(0); |
| } |
| |
| DEBUG("%s: %d ports", found_ca_name, ports); |
| |
| result = ports; |
| clean: |
| release_ca(&ca); |
| exit: |
| free(found_ca_name); |
| |
| return result; |
| } |
| |
| int umad_get_issm_path(const char *ca_name, int portnum, char path[], int max) |
| { |
| int umad_id, result; |
| char *found_ca_name; |
| |
| TRACE("ca %s port %d", ca_name, portnum); |
| |
| if (resolve_ca_name(ca_name, &portnum, &found_ca_name, 0) < 0) { |
| result = -ENODEV; |
| goto exit; |
| } |
| |
| umad_id = dev_to_umad_id(found_ca_name, portnum); |
| if (umad_id < 0) { |
| result = -EINVAL; |
| goto exit; |
| } |
| |
| snprintf(path, max, "%s/issm%u", RDMA_CDEV_DIR, umad_id); |
| |
| result = 0; |
| exit: |
| free(found_ca_name); |
| |
| return result; |
| } |
| |
| static int do_umad_open_port(const char *ca_name, int portnum, unsigned enforce_smi) |
| { |
| char dev_file[UMAD_DEV_FILE_SZ]; |
| int umad_id, fd, result; |
| unsigned int abi_version = get_abi_version(); |
| char *found_ca_name = NULL; |
| |
| TRACE("ca %s port %d", ca_name, portnum); |
| |
| if (!abi_version) { |
| result = -EOPNOTSUPP; |
| goto exit; |
| } |
| |
| if (resolve_ca_name(ca_name, &portnum, &found_ca_name, enforce_smi) < 0) { |
| result = -ENODEV; |
| goto exit; |
| } |
| |
| DEBUG("opening %s port %d", found_ca_name, portnum); |
| |
| umad_id = dev_to_umad_id(found_ca_name, portnum); |
| if (umad_id < 0) { |
| result = -EINVAL; |
| goto exit; |
| } |
| |
| snprintf(dev_file, sizeof(dev_file), "%s/umad%d", |
| RDMA_CDEV_DIR, umad_id); |
| |
| if ((fd = open(dev_file, O_RDWR | O_NONBLOCK)) < 0) { |
| DEBUG("open %s failed: %s", dev_file, strerror(errno)); |
| result = -EIO; |
| goto exit; |
| } |
| |
| if (abi_version > 5 || !ioctl(fd, IB_USER_MAD_ENABLE_PKEY, NULL)) |
| new_user_mad_api = 1; |
| else |
| new_user_mad_api = 0; |
| |
| DEBUG("opened %s fd %d portid %d", dev_file, fd, umad_id); |
| |
| result = fd; |
| exit: |
| free(found_ca_name); |
| |
| return result; |
| } |
| |
| int umad_open_port(const char *ca_name, int portnum) |
| { |
| return do_umad_open_port(ca_name, portnum, 0); |
| } |
| |
| int umad_open_smi_port(const char *ca_name, int portnum) |
| { |
| return do_umad_open_port(ca_name, portnum, 1); |
| } |
| |
| int umad_get_ca(const char *ca_name, umad_ca_t *ca) |
| { |
| int r = 0; |
| char *found_ca_name; |
| |
| TRACE("ca_name %s", ca_name); |
| if (resolve_ca_name(ca_name, NULL, &found_ca_name, 0) < 0) { |
| r = -ENODEV; |
| goto exit; |
| } |
| |
| if (find_cached_ca(found_ca_name, ca) > 0) |
| goto exit; |
| |
| r = get_ca(found_ca_name, ca); |
| if (r < 0) |
| goto exit; |
| |
| DEBUG("opened %s", found_ca_name); |
| exit: |
| free(found_ca_name); |
| |
| return r; |
| } |
| |
| int umad_release_ca(umad_ca_t * ca) |
| { |
| int r; |
| |
| TRACE("ca_name %s", ca->ca_name); |
| if (!ca) |
| return -ENODEV; |
| |
| if ((r = release_ca(ca)) < 0) |
| return r; |
| |
| DEBUG("releasing %s", ca->ca_name); |
| return 0; |
| } |
| |
| int umad_get_port(const char *ca_name, int portnum, umad_port_t *port) |
| { |
| char dir_name[256]; |
| char *found_ca_name; |
| int result; |
| |
| TRACE("ca_name %s portnum %d", ca_name, portnum); |
| |
| if (resolve_ca_name(ca_name, &portnum, &found_ca_name, 0) < 0) { |
| result = -ENODEV; |
| goto exit; |
| } |
| |
| snprintf(dir_name, sizeof(dir_name), "%s/%s/%s", |
| SYS_INFINIBAND, found_ca_name, SYS_CA_PORTS_DIR); |
| |
| result = get_port(found_ca_name, dir_name, portnum, port); |
| exit: |
| free(found_ca_name); |
| |
| return result; |
| } |
| |
| int umad_release_port(umad_port_t * port) |
| { |
| int r; |
| |
| TRACE("port %s:%d", port->ca_name, port->portnum); |
| if (!port) |
| return -ENODEV; |
| |
| if ((r = release_port(port)) < 0) |
| return r; |
| |
| DEBUG("releasing %s:%d", port->ca_name, port->portnum); |
| return 0; |
| } |
| |
| int umad_close_port(int fd) |
| { |
| close(fd); |
| DEBUG("closed fd %d", fd); |
| return 0; |
| } |
| |
| void *umad_get_mad(void *umad) |
| { |
| return new_user_mad_api ? ((struct ib_user_mad *)umad)->data : |
| (void *)&((struct ib_user_mad *)umad)->addr.pkey_index; |
| } |
| |
| size_t umad_size(void) |
| { |
| return new_user_mad_api ? sizeof(struct ib_user_mad) : |
| sizeof(struct ib_user_mad) - 8; |
| } |
| |
| int umad_set_grh(void *umad, void *mad_addr) |
| { |
| struct ib_user_mad *mad = umad; |
| struct ib_mad_addr *addr = mad_addr; |
| |
| if (mad_addr) { |
| mad->addr.grh_present = 1; |
| mad->addr.ib_gid = addr->ib_gid; |
| /* The definition for umad_set_grh requires that the input be |
| * in host order */ |
| mad->addr.flow_label = htobe32((__force uint32_t)addr->flow_label); |
| mad->addr.hop_limit = addr->hop_limit; |
| mad->addr.traffic_class = addr->traffic_class; |
| } else |
| mad->addr.grh_present = 0; |
| return 0; |
| } |
| |
| int umad_set_pkey(void *umad, int pkey_index) |
| { |
| struct ib_user_mad *mad = umad; |
| |
| if (new_user_mad_api) |
| mad->addr.pkey_index = pkey_index; |
| |
| return 0; |
| } |
| |
| int umad_get_pkey(void *umad) |
| { |
| struct ib_user_mad *mad = umad; |
| |
| if (new_user_mad_api) |
| return mad->addr.pkey_index; |
| |
| return 0; |
| } |
| |
| int umad_set_addr(void *umad, int dlid, int dqp, int sl, int qkey) |
| { |
| struct ib_user_mad *mad = umad; |
| |
| TRACE("umad %p dlid %u dqp %d sl %d, qkey %x", |
| umad, dlid, dqp, sl, qkey); |
| mad->addr.qpn = htobe32(dqp); |
| mad->addr.lid = htobe16(dlid); |
| mad->addr.qkey = htobe32(qkey); |
| mad->addr.sl = sl; |
| |
| return 0; |
| } |
| |
| int umad_set_addr_net(void *umad, __be16 dlid, __be32 dqp, int sl, __be32 qkey) |
| { |
| struct ib_user_mad *mad = umad; |
| |
| TRACE("umad %p dlid %u dqp %d sl %d qkey %x", |
| umad, be16toh(dlid), be32toh(dqp), sl, be32toh(qkey)); |
| mad->addr.qpn = dqp; |
| mad->addr.lid = dlid; |
| mad->addr.qkey = qkey; |
| mad->addr.sl = sl; |
| |
| return 0; |
| } |
| |
| int umad_send(int fd, int agentid, void *umad, int length, |
| int timeout_ms, int retries) |
| { |
| struct ib_user_mad *mad = umad; |
| int n; |
| |
| TRACE("fd %d agentid %d umad %p timeout %u", |
| fd, agentid, umad, timeout_ms); |
| errno = 0; |
| |
| mad->timeout_ms = timeout_ms; |
| mad->retries = retries; |
| mad->agent_id = agentid; |
| |
| if (umaddebug > 1) |
| umad_dump(mad); |
| |
| n = write(fd, mad, length + umad_size()); |
| if (n == length + umad_size()) |
| return 0; |
| |
| DEBUG("write returned %d != sizeof umad %zu + length %d (%m)", |
| n, umad_size(), length); |
| if (!errno) |
| errno = EIO; |
| return -EIO; |
| } |
| |
| static int dev_poll(int fd, int timeout_ms) |
| { |
| struct pollfd ufds; |
| int n; |
| |
| ufds.fd = fd; |
| ufds.events = POLLIN; |
| |
| if ((n = poll(&ufds, 1, timeout_ms)) == 1) |
| return 0; |
| |
| if (n == 0) |
| return -ETIMEDOUT; |
| |
| return -EIO; |
| } |
| |
| int umad_recv(int fd, void *umad, int *length, int timeout_ms) |
| { |
| struct ib_user_mad *mad = umad; |
| int n; |
| |
| errno = 0; |
| TRACE("fd %d umad %p timeout %u", fd, umad, timeout_ms); |
| |
| if (!umad || !length) { |
| errno = EINVAL; |
| return -EINVAL; |
| } |
| |
| if (timeout_ms && (n = dev_poll(fd, timeout_ms)) < 0) { |
| if (!errno) |
| errno = -n; |
| return n; |
| } |
| |
| n = read(fd, umad, umad_size() + *length); |
| |
| VALGRIND_MAKE_MEM_DEFINED(umad, umad_size() + *length); |
| |
| if ((n >= 0) && (n <= umad_size() + *length)) { |
| DEBUG("mad received by agent %d length %d", mad->agent_id, n); |
| if (n > umad_size()) |
| *length = n - umad_size(); |
| else |
| *length = 0; |
| return mad->agent_id; |
| } |
| |
| if (n == -EWOULDBLOCK) { |
| if (!errno) |
| errno = EWOULDBLOCK; |
| return n; |
| } |
| |
| DEBUG("read returned %zu > sizeof umad %zu + length %d (%m)", |
| mad->length - umad_size(), umad_size(), *length); |
| |
| *length = mad->length - umad_size(); |
| if (!errno) |
| errno = EIO; |
| return -errno; |
| } |
| |
| int umad_poll(int fd, int timeout_ms) |
| { |
| TRACE("fd %d timeout %u", fd, timeout_ms); |
| return dev_poll(fd, timeout_ms); |
| } |
| |
| int umad_get_fd(int fd) |
| { |
| TRACE("fd %d", fd); |
| return fd; |
| } |
| |
| int umad_register_oui(int fd, int mgmt_class, uint8_t rmpp_version, |
| uint8_t oui[3], long method_mask[16 / sizeof(long)]) |
| { |
| struct ib_user_mad_reg_req req; |
| |
| TRACE("fd %d mgmt_class %u rmpp_version %d oui 0x%x%x%x method_mask %p", |
| fd, mgmt_class, (int)rmpp_version, (int)oui[0], (int)oui[1], |
| (int)oui[2], method_mask); |
| |
| if (mgmt_class < 0x30 || mgmt_class > 0x4f) { |
| DEBUG("mgmt class %d not in vendor range 2", mgmt_class); |
| return -EINVAL; |
| } |
| |
| req.qpn = 1; |
| req.mgmt_class = mgmt_class; |
| req.mgmt_class_version = 1; |
| memcpy(req.oui, oui, sizeof req.oui); |
| req.rmpp_version = rmpp_version; |
| |
| if (method_mask) |
| memcpy(req.method_mask, method_mask, sizeof req.method_mask); |
| else |
| memset(req.method_mask, 0, sizeof req.method_mask); |
| |
| VALGRIND_MAKE_MEM_DEFINED(&req, sizeof req); |
| |
| if (!ioctl(fd, IB_USER_MAD_REGISTER_AGENT, (void *)&req)) { |
| DEBUG |
| ("fd %d registered to use agent %d qp %d class 0x%x oui %p", |
| fd, req.id, req.qpn, req.mgmt_class, oui); |
| return req.id; /* return agentid */ |
| } |
| |
| DEBUG("fd %d registering qp %d class 0x%x version %d oui %p failed: %m", |
| fd, req.qpn, req.mgmt_class, req.mgmt_class_version, oui); |
| return -EPERM; |
| } |
| |
| int umad_register(int fd, int mgmt_class, int mgmt_version, |
| uint8_t rmpp_version, long method_mask[16 / sizeof(long)]) |
| { |
| struct ib_user_mad_reg_req req; |
| __be32 oui = htobe32(IB_OPENIB_OUI); |
| int qp; |
| |
| TRACE |
| ("fd %d mgmt_class %u mgmt_version %u rmpp_version %d method_mask %p", |
| fd, mgmt_class, mgmt_version, rmpp_version, method_mask); |
| |
| req.qpn = qp = (mgmt_class == 0x1 || mgmt_class == 0x81) ? 0 : 1; |
| req.mgmt_class = mgmt_class; |
| req.mgmt_class_version = mgmt_version; |
| req.rmpp_version = rmpp_version; |
| |
| if (method_mask) |
| memcpy(req.method_mask, method_mask, sizeof req.method_mask); |
| else |
| memset(req.method_mask, 0, sizeof req.method_mask); |
| |
| memcpy(&req.oui, (char *)&oui + 1, sizeof req.oui); |
| |
| VALGRIND_MAKE_MEM_DEFINED(&req, sizeof req); |
| |
| if (!ioctl(fd, IB_USER_MAD_REGISTER_AGENT, (void *)&req)) { |
| DEBUG("fd %d registered to use agent %d qp %d", fd, req.id, qp); |
| return req.id; /* return agentid */ |
| } |
| |
| DEBUG("fd %d registering qp %d class 0x%x version %d failed: %m", |
| fd, qp, mgmt_class, mgmt_version); |
| return -EPERM; |
| } |
| |
| int umad_register2(int port_fd, struct umad_reg_attr *attr, uint32_t *agent_id) |
| { |
| struct ib_user_mad_reg_req2 req; |
| int rc; |
| |
| if (!attr || !agent_id) |
| return EINVAL; |
| |
| TRACE("fd %d mgmt_class %u mgmt_class_version %u flags 0x%08x " |
| "method_mask 0x%016" PRIx64 " %016" PRIx64 |
| "oui 0x%06x rmpp_version %u ", |
| port_fd, attr->mgmt_class, attr->mgmt_class_version, |
| attr->flags, attr->method_mask[0], attr->method_mask[1], |
| attr->oui, attr->rmpp_version); |
| |
| if (attr->mgmt_class >= 0x30 && attr->mgmt_class <= 0x4f && |
| ((attr->oui & 0x00ffffff) == 0 || (attr->oui & 0xff000000) != 0)) { |
| DEBUG("mgmt class %d is in vendor range 2 but oui (0x%08x) is invalid", |
| attr->mgmt_class, attr->oui); |
| return EINVAL; |
| } |
| |
| memset(&req, 0, sizeof(req)); |
| |
| req.mgmt_class = attr->mgmt_class; |
| req.mgmt_class_version = attr->mgmt_class_version; |
| req.qpn = (attr->mgmt_class == 0x1 || attr->mgmt_class == 0x81) ? 0 : 1; |
| req.flags = attr->flags; |
| memcpy(req.method_mask, attr->method_mask, sizeof req.method_mask); |
| req.oui = attr->oui; |
| req.rmpp_version = attr->rmpp_version; |
| |
| VALGRIND_MAKE_MEM_DEFINED(&req, sizeof req); |
| |
| if ((rc = ioctl(port_fd, IB_USER_MAD_REGISTER_AGENT2, (void *)&req)) == 0) { |
| DEBUG("fd %d registered to use agent %d qp %d class 0x%x oui 0x%06x", |
| port_fd, req.id, req.qpn, req.mgmt_class, attr->oui); |
| *agent_id = req.id; |
| return 0; |
| } |
| |
| if (errno == ENOTTY || errno == EINVAL) { |
| |
| TRACE("no kernel support for registration flags"); |
| req.flags = 0; |
| |
| if (attr->flags == 0) { |
| struct ib_user_mad_reg_req req_v1; |
| |
| TRACE("attempting original register ioctl"); |
| |
| memset(&req_v1, 0, sizeof(req_v1)); |
| req_v1.mgmt_class = req.mgmt_class; |
| req_v1.mgmt_class_version = req.mgmt_class_version; |
| req_v1.qpn = req.qpn; |
| req_v1.rmpp_version = req.rmpp_version; |
| req_v1.oui[0] = (req.oui & 0xff0000) >> 16; |
| req_v1.oui[1] = (req.oui & 0x00ff00) >> 8; |
| req_v1.oui[2] = req.oui & 0x0000ff; |
| |
| memcpy(req_v1.method_mask, req.method_mask, sizeof req_v1.method_mask); |
| |
| if ((rc = ioctl(port_fd, IB_USER_MAD_REGISTER_AGENT, |
| (void *)&req_v1)) == 0) { |
| DEBUG("fd %d registered to use agent %d qp %d class 0x%x oui 0x%06x", |
| port_fd, req_v1.id, req_v1.qpn, req_v1.mgmt_class, attr->oui); |
| *agent_id = req_v1.id; |
| return 0; |
| } |
| } |
| } |
| |
| rc = errno; |
| attr->flags = req.flags; |
| |
| DEBUG("fd %d registering qp %d class 0x%x version %d " |
| "oui 0x%06x failed flags returned 0x%x : %m", |
| port_fd, req.qpn, req.mgmt_class, req.mgmt_class_version, |
| attr->oui, req.flags); |
| |
| return rc; |
| } |
| |
| int umad_unregister(int fd, int agentid) |
| { |
| TRACE("fd %d unregistering agent %d", fd, agentid); |
| return ioctl(fd, IB_USER_MAD_UNREGISTER_AGENT, &agentid); |
| } |
| |
| int umad_status(void *umad) |
| { |
| struct ib_user_mad *mad = umad; |
| |
| return mad->status; |
| } |
| |
| ib_mad_addr_t *umad_get_mad_addr(void *umad) |
| { |
| struct ib_user_mad *mad = umad; |
| |
| return &mad->addr; |
| } |
| |
| int umad_debug(int level) |
| { |
| if (level >= 0) |
| umaddebug = level; |
| return umaddebug; |
| } |
| |
| void umad_addr_dump(ib_mad_addr_t * addr) |
| { |
| #define HEX(x) ((x) < 10 ? '0' + (x) : 'a' + ((x) -10)) |
| char gid_str[64]; |
| int i; |
| |
| for (i = 0; i < sizeof addr->gid; i++) { |
| gid_str[i * 2] = HEX(addr->gid[i] >> 4); |
| gid_str[i * 2 + 1] = HEX(addr->gid[i] & 0xf); |
| } |
| gid_str[i * 2] = 0; |
| IBWARN("qpn %d qkey 0x%x lid %u sl %d\n" |
| "grh_present %d gid_index %d hop_limit %d traffic_class %d flow_label 0x%x pkey_index 0x%x\n" |
| "Gid 0x%s", |
| be32toh(addr->qpn), be32toh(addr->qkey), be16toh(addr->lid), addr->sl, |
| addr->grh_present, (int)addr->gid_index, (int)addr->hop_limit, |
| (int)addr->traffic_class, addr->flow_label, addr->pkey_index, |
| gid_str); |
| } |
| |
| void umad_dump(void *umad) |
| { |
| struct ib_user_mad *mad = umad; |
| |
| IBWARN("agent id %d status %x timeout %d", |
| mad->agent_id, mad->status, mad->timeout_ms); |
| umad_addr_dump(&mad->addr); |
| } |
| |
| int umad_sort_ca_device_list(struct umad_device_node **head, size_t size) |
| { |
| int errsv = 0; |
| size_t i; |
| struct umad_device_node *node; |
| struct umad_device_node **nodes_array = NULL; |
| |
| if (!size) |
| for (node = *head; node; node = node->next) |
| size++; |
| if (size < 2) |
| return 0; |
| |
| nodes_array = calloc(size, sizeof(struct umad_device_node *)); |
| if (!nodes_array) { |
| errsv = ENOMEM; |
| goto exit; |
| } |
| |
| node = *head; |
| for (i = 0; i < size; i++) { |
| if (!node) { |
| errsv = EINVAL; |
| goto exit; |
| } |
| nodes_array[i] = node; |
| node = node->next; |
| } |
| if (node) { |
| errsv = EINVAL; |
| goto exit; |
| } |
| |
| qsort(nodes_array, size, sizeof(struct umad_device_node *), |
| umad_ca_device_list_compare_function); |
| |
| for (i = 0; i < size - 1; i++) |
| nodes_array[i]->next = nodes_array[i + 1]; |
| |
| *head = nodes_array[0]; |
| nodes_array[size - 1]->next = NULL; |
| exit: |
| free(nodes_array); |
| |
| return errsv; |
| } |
| |
| struct umad_device_node *umad_get_ca_device_list(void) |
| { |
| DIR *dir; |
| struct dirent *entry; |
| struct umad_device_node *head = NULL; |
| struct umad_device_node *tail; |
| struct umad_device_node *node; |
| char *ca_name; |
| size_t cas_num = 0; |
| size_t d_name_size; |
| int errsv = 0; |
| |
| dir = opendir(SYS_INFINIBAND); |
| if (!dir) { |
| if (errno == ENOENT) |
| errno = 0; |
| return NULL; |
| } |
| |
| while ((entry = readdir(dir))) { |
| if ((strcmp(entry->d_name, ".") == 0) || |
| (strcmp(entry->d_name, "..") == 0)) |
| continue; |
| |
| if (!is_ib_type(entry->d_name)) |
| continue; |
| |
| d_name_size = strlen(entry->d_name) + 1; |
| node = calloc(1, sizeof(struct umad_device_node) + d_name_size); |
| if (!node) { |
| errsv = ENOMEM; |
| umad_free_ca_device_list(head); |
| head = NULL; |
| goto exit; |
| } |
| |
| if (!head) |
| head = node; |
| else |
| tail->next = node; |
| tail = node; |
| |
| ca_name = (char *)(node + 1); |
| strncpy(ca_name, entry->d_name, d_name_size); |
| node->ca_name = ca_name; |
| |
| cas_num++; |
| } |
| |
| DEBUG("return %zu cas", cas_num); |
| exit: |
| closedir(dir); |
| errno = errsv; |
| |
| return head; |
| } |
| |
| void umad_free_ca_device_list(struct umad_device_node *head) |
| { |
| struct umad_device_node *node; |
| struct umad_device_node *next; |
| |
| for (node = head; node; node = next) { |
| next = node->next; |
| free(node); |
| } |
| } |
| |
| static struct umad_ca_pair *get_ca_pair_from_arr_by_guid(__be64 port_guid, |
| struct guid_ca_pairs_mapping mapping[], |
| size_t map_max, size_t *map_added, |
| struct umad_ca_pair devs[], |
| size_t devs_max, size_t *devs_added) |
| { |
| struct umad_ca_pair *dev = NULL; |
| // attempt to find the port guid in the mapping |
| size_t i = 0; |
| |
| for (i = 0; i < *map_added; ++i) { |
| if (mapping[i].port_guid == port_guid) |
| return mapping[i].ca_pair; |
| } |
| |
| // attempt to add a new mapping/device |
| if (*map_added >= map_max || *devs_added >= devs_max) |
| return NULL; |
| |
| dev = &devs[*devs_added]; |
| mapping[*map_added].port_guid = port_guid; |
| mapping[*map_added].ca_pair = dev; |
| (*devs_added)++; |
| (*map_added)++; |
| |
| return dev; |
| } |
| |
| static uint8_t get_port_guid_count(__be64 guid, const struct port_guid_port_count counts[], |
| size_t max_guids) |
| { |
| size_t i = 0; |
| |
| for (i = 0; i < max_guids; ++i) { |
| if (counts[i].port_guid == guid) |
| return counts[i].count; |
| } |
| |
| return 0; |
| } |
| |
| |
| static bool find_port_guid_count(struct port_guid_port_count counts[], size_t max, |
| __be64 port_guid, size_t *index) |
| { |
| size_t i = 0; |
| |
| for (i = 0; i < max; ++i) { |
| if (counts[i].port_guid == 0) { |
| *index = i; |
| return false; |
| } |
| if (counts[i].port_guid == port_guid) { |
| *index = i; |
| return true; |
| } |
| } |
| |
| *index = max; |
| return false; |
| } |
| |
| static int count_ports_by_guid(char legacy_ca_names[][UMAD_CA_NAME_LEN], size_t num_cas, |
| struct port_guid_port_count counts[], size_t max) |
| { |
| // how many unique port GUIDs were added |
| size_t num_of_guid = 0; |
| |
| memset(counts, 0, max * sizeof(struct port_guid_port_count)); |
| |
| size_t c_idx = 0; |
| |
| for (c_idx = 0; c_idx < num_cas; ++c_idx) { |
| umad_ca_t curr_ca; |
| |
| if (umad_get_ca(legacy_ca_names[c_idx], &curr_ca) < 0) |
| continue; |
| |
| size_t p_idx = 0; |
| |
| for (p_idx = 0; p_idx < (size_t)curr_ca.numports + 1; ++p_idx) { |
| umad_port_t *p_port = curr_ca.ports[p_idx]; |
| size_t count_idx = 0; |
| |
| if (!p_port) |
| continue; |
| |
| if (find_port_guid_count(counts, max, p_port->port_guid, &count_idx)) { |
| // port GUID already has a count struct |
| ++counts[count_idx].count; |
| } else { |
| // add a new count struct for this GUID. |
| // if the maximum amount was already added, do nothing. |
| if (count_idx != max) { |
| counts[count_idx].port_guid = p_port->port_guid; |
| counts[count_idx].count = 1; |
| ++num_of_guid; |
| } |
| } |
| } |
| |
| umad_release_ca(&curr_ca); |
| } |
| |
| return num_of_guid; |
| } |
| |
| int umad_get_smi_gsi_pairs(struct umad_ca_pair cas[], size_t max) |
| { |
| size_t added_devices = 0, added_mappings = 0; |
| char legacy_ca_names[UMAD_MAX_DEVICES][UMAD_CA_NAME_LEN] = {}; |
| struct port_guid_port_count counts[UMAD_MAX_PORTS] = {}; |
| struct guid_ca_pairs_mapping mapping[UMAD_MAX_PORTS] = {}; |
| |
| memset(cas, 0, sizeof(struct umad_ca_pair) * max); |
| int cas_found = umad_get_cas_names(legacy_ca_names, UMAD_MAX_DEVICES); |
| |
| if (cas_found < 0) |
| return 0; |
| |
| count_ports_by_guid(legacy_ca_names, cas_found, counts, UMAD_MAX_PORTS); |
| |
| size_t c_idx = 0; |
| |
| for (c_idx = 0; c_idx < (size_t)cas_found; ++c_idx) { |
| umad_ca_t curr_ca; |
| |
| if (umad_get_ca(legacy_ca_names[c_idx], &curr_ca) < 0) |
| continue; |
| |
| size_t p_idx = 0; |
| |
| for (p_idx = 0; p_idx < (size_t)curr_ca.numports + 1; ++p_idx) { |
| umad_port_t *p_port = curr_ca.ports[p_idx]; |
| uint8_t guid_count = 0; |
| |
| if (!p_port) |
| continue; |
| |
| guid_count = get_port_guid_count(curr_ca.ports[p_idx]->port_guid, |
| counts, UMAD_MAX_PORTS); |
| struct umad_ca_pair *dev = get_ca_pair_from_arr_by_guid(p_port->port_guid, |
| mapping, UMAD_MAX_PORTS, |
| &added_mappings, cas, |
| max, &added_devices); |
| if (!dev) |
| continue; |
| if (guid_count > 1) { |
| // planarized port |
| char *dev_name = is_smi_disabled(p_port) ? dev->gsi_name : dev->smi_name; |
| strncpy(dev_name, curr_ca.ca_name, UMAD_CA_NAME_LEN); |
| break; |
| } else if (guid_count == 1) { |
| if (!is_smi_disabled(p_port)) |
| strncpy(dev->smi_name, curr_ca.ca_name, UMAD_CA_NAME_LEN); |
| strncpy(dev->gsi_name, curr_ca.ca_name, UMAD_CA_NAME_LEN); |
| break; |
| } else { |
| umad_release_ca(&curr_ca); |
| return -1; |
| } |
| } |
| |
| umad_release_ca(&curr_ca); |
| } |
| |
| return added_devices; |
| } |
| |
| static int umad_check_active(const umad_ca_t *ca, int prefered_portnum) |
| { |
| if (!ca) |
| return 1; |
| |
| if (!ca->ports[prefered_portnum]) |
| return 1; |
| |
| int state = ca->ports[prefered_portnum]->state; |
| |
| return !(state > 1); |
| } |
| |
| static int umad_find_active(struct umad_ca_pair *ca_pair, const umad_ca_t *ca, bool is_gsi) |
| { |
| size_t i = 1; |
| uint32_t *portnum_to_set = is_gsi ? |
| &ca_pair->gsi_preferred_port : |
| &ca_pair->smi_preferred_port; |
| |
| if (!ca_pair) |
| return 1; |
| |
| for (i = 0; i < (size_t)ca->numports + 1; ++i) { |
| if (!umad_check_active(ca, i)) { |
| *portnum_to_set = ca->ports[i]->portnum; |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| static int find_preferred_ports(struct umad_ca_pair *ca_pair, const umad_ca_t *ca, bool is_gsi, int portnum) |
| { |
| if (portnum) { |
| //in case we have same device, use same port for smi/gsi |
| if (!strncmp(ca_pair->gsi_name, ca_pair->smi_name, UMAD_CA_NAME_LEN)) { |
| if (!umad_check_active(ca, portnum)) { |
| ca_pair->gsi_preferred_port = portnum; |
| ca_pair->smi_preferred_port = portnum; |
| return 0; |
| } |
| return 1; |
| } |
| uint32_t *port_to_set = is_gsi ? |
| &ca_pair->gsi_preferred_port : |
| &ca_pair->smi_preferred_port; |
| if (!umad_check_active(ca, portnum)) { |
| *port_to_set = portnum; |
| return umad_find_active(ca_pair, ca, !is_gsi); |
| } |
| return 1; |
| } |
| |
| return umad_find_active(ca_pair, ca, false) |
| + umad_find_active(ca_pair, ca, true); |
| |
| } |
| |
| int umad_get_smi_gsi_pair_by_ca_name(const char *name, uint8_t portnum, struct umad_ca_pair *ca_pair, |
| unsigned enforce_smi) |
| { |
| int rc = 1; |
| size_t i = 0; |
| int num_cas = 0; |
| bool is_gsi = false; |
| |
| umad_ca_t ca; |
| struct umad_ca_pair cas_pair[UMAD_MAX_PORTS] = {}; |
| |
| if (!ca_pair) |
| return -1; |
| |
| memset(cas_pair, 0, sizeof(cas_pair)); |
| memset(ca_pair, 0, sizeof(*ca_pair)); |
| |
| num_cas = umad_get_smi_gsi_pairs(cas_pair, UMAD_MAX_PORTS); |
| |
| if (num_cas <= 0) |
| return num_cas; |
| |
| for (i = 0; i < (size_t)num_cas; ++i) { |
| if (enforce_smi && !cas_pair[i].smi_name[0]) |
| continue; |
| if (!cas_pair[i].gsi_name[0]) |
| continue; |
| |
| if (name) |
| // name doesn't match - keep searching |
| if (strncmp(cas_pair[i].gsi_name, name, UMAD_CA_NAME_LEN) |
| && strncmp(cas_pair[i].smi_name, name, UMAD_CA_NAME_LEN)) { |
| continue; |
| } |
| |
| // check that the device given by "name" has a port number "portnum" |
| // (if name doesn't exist, assume SMI port is given) |
| // (if enforce_smi is false assume GSI in case no SMI present) |
| is_gsi = (!enforce_smi && !cas_pair[i].smi_name[0]) || |
| (name && !strncmp(name, cas_pair[i].gsi_name, UMAD_CA_NAME_LEN)); |
| |
| if (umad_get_ca(is_gsi ? cas_pair[i].gsi_name : cas_pair[i].smi_name, &ca) < 0) |
| continue; |
| |
| if (portnum) { |
| if (!ca.ports[portnum]) { |
| umad_release_ca(&ca); |
| continue; |
| } |
| } |
| |
| // fill candidate |
| *ca_pair = cas_pair[i]; |
| rc = find_preferred_ports(ca_pair, &ca, is_gsi, portnum); |
| umad_release_ca(&ca); |
| |
| if (!rc) |
| break; |
| } |
| |
| if (rc) { |
| errno = ENODEV; |
| return -errno; |
| } |
| |
| return rc; |
| } |
| |