diff --git a/debian/libibumad3.symbols b/debian/libibumad3.symbols index 9b6e74682..471d81dba 100644 --- a/debian/libibumad3.symbols +++ b/debian/libibumad3.symbols @@ -44,3 +44,5 @@ libibumad.so.3 libibumad3 #MINVER# umad_sort_ca_device_list@IBUMAD_1.2 3.2.30 umad_status@IBUMAD_1.0 1.3.9 umad_unregister@IBUMAD_1.0 1.3.9 + umad_get_smi_gsi_pairs@IBUMAD_1.4 3.4.55 + umad_get_smi_gsi_pair_by_ca_name@IBUMAD_1.4 3.4.55 diff --git a/libibumad/CMakeLists.txt b/libibumad/CMakeLists.txt index 3ee12c83d..dcb030e16 100644 --- a/libibumad/CMakeLists.txt +++ b/libibumad/CMakeLists.txt @@ -10,7 +10,7 @@ publish_headers(infiniband rdma_library(ibumad libibumad.map # See Documentation/versioning.md - 3 3.3.${PACKAGE_VERSION} + 3 3.4.${PACKAGE_VERSION} sysfs.c umad.c umad_str.c diff --git a/libibumad/libibumad.map b/libibumad/libibumad.map index 694d1dd2a..0fb572f24 100644 --- a/libibumad/libibumad.map +++ b/libibumad/libibumad.map @@ -55,3 +55,10 @@ IBUMAD_1.3 { global: umad_open_smi_port; } IBUMAD_1.2; + +IBUMAD_1.4 { + global: + umad_get_smi_gsi_pairs; + umad_get_smi_gsi_pair_by_ca_name; +} IBUMAD_1.3; + diff --git a/libibumad/man/umad_get_smi_gsi_pair_by_ca_name.3 b/libibumad/man/umad_get_smi_gsi_pair_by_ca_name.3 new file mode 100644 index 000000000..c70042e33 --- /dev/null +++ b/libibumad/man/umad_get_smi_gsi_pair_by_ca_name.3 @@ -0,0 +1,54 @@ +.. -*- rst -*- +.. Licensed under the OpenIB.org BSD license (FreeBSD Variant) - See COPYING.md + +umad_get_smi_gsi_pair_by_ca_name(3) +=================================== + +Retrieve SMI/GSI pair information based on device name and port number. + +Synopsis +-------- + +.. code-block:: c + + #include + + int umad_get_smi_gsi_pair_by_ca_name(const char *devname, uint8_t portnum, struct umad_ca_pair *ca); + +Description +----------- + +``umad_get_smi_gsi_pair_by_ca_name()`` fills the provided ``ca`` structure with the SMI and GSI pair information +for the specified device name and port number. + +The ``devname`` parameter specifies the name of the device, and ``portnum`` is the associated port number. + +The ``struct umad_ca_pair`` is defined in ```` and includes the following members: + +.. code-block:: c + + struct umad_ca_pair { + char smi_name[UMAD_CA_NAME_LEN]; + /* Name of the SMI */ + uint32_t smi_preferred_port; + /* Preferred port for the SMI */ + char gsi_name[UMAD_CA_NAME_LEN]; + /* Name of the GSI */ + uint32_t gsi_preferred_port; + /* Preferred port for the GSI */ + }; + +The function populates this structure with the relevant data for the given ``devname`` and ``portnum``. + +Return Value +------------ + +``umad_get_smi_gsi_pair_by_ca_name()`` returns: + +- **0**: If the specified device and port are found and the structure is successfully populated. +- **1**: If no matching device or port is found. + +Authors +------- + +- Asaf Mazor diff --git a/libibumad/man/umad_get_smi_gsi_pairs.3 b/libibumad/man/umad_get_smi_gsi_pairs.3 new file mode 100644 index 000000000..ae746df19 --- /dev/null +++ b/libibumad/man/umad_get_smi_gsi_pairs.3 @@ -0,0 +1,50 @@ +.. -*- rst -*- +.. Licensed under the OpenIB.org BSD license (FreeBSD Variant) - See COPYING.md + +umad_get_smi_gsi_pairs(3) +========================= + +Get CAs as SMI/GSI pairs. + +Synopsis +-------- + +.. code-block:: c + + #include + + int umad_get_smi_gsi_pairs(struct umad_ca_pair cas[], size_t max); + +Description +----------- + +``umad_get_smi_gsi_pairs()`` fills a user-allocated array of ``struct umad_ca_pair``. +It fills up to ``max`` devices. + +The argument ``cas`` is an array of ``struct umad_ca_pair`` as specified in ````: + +.. code-block:: c + + struct umad_ca_pair { + char smi_name[UMAD_CA_NAME_LEN]; + /* Name of the SMI */ + uint32_t smi_preferred_port; + /* Preferred port for the SMI */ + char gsi_name[UMAD_CA_NAME_LEN]; + /* Name of the GSI */ + uint32_t gsi_preferred_port; + /* Preferred port for the GSI */ + }; + +The ``smi_preferred_port`` and ``gsi_preferred_port`` fields represent the first ports found active +for the corresponding SMI/GSI device. + +Return Value +------------ + +``umad_get_smi_gsi_pairs()`` returns the number of devices filled, or **-1** on error. + +Authors +------- + +- Asaf Mazor diff --git a/libibumad/umad.c b/libibumad/umad.c index 9de39c7ea..359ce019e 100644 --- a/libibumad/umad.c +++ b/libibumad/umad.c @@ -34,6 +34,7 @@ #include +#include #include #include #include @@ -79,6 +80,16 @@ struct ib_user_mad_reg_req2 { 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 @@ -1354,3 +1365,287 @@ void umad_free_ca_device_list(struct umad_device_node *head) 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 = 1; + + for (p_idx = 1; 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 = 1; + + for (p_idx = 1; 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 = 1; 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) { + 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) +{ + 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 (!cas_pair[i].gsi_name[0] || !cas_pair[i].smi_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) + is_gsi = (name && !strncmp(name, cas_pair[i].gsi_name, UMAD_CA_NAME_LEN)); + + if (portnum) { + if (umad_get_ca(is_gsi ? cas_pair[i].gsi_name : cas_pair[i].smi_name, &ca) < 0) + continue; + + 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; +} + diff --git a/libibumad/umad.h b/libibumad/umad.h index 195216e1b..851a97bf9 100644 --- a/libibumad/umad.h +++ b/libibumad/umad.h @@ -169,6 +169,14 @@ typedef struct umad_ca { umad_port_t *ports[UMAD_CA_MAX_PORTS]; } umad_ca_t; +struct umad_ca_pair { + char smi_name[UMAD_CA_NAME_LEN]; + uint32_t smi_preferred_port; + char gsi_name[UMAD_CA_NAME_LEN]; + uint32_t gsi_preferred_port; +}; + + struct umad_device_node { struct umad_device_node *next; /* next umad device node */ const char *ca_name; /* ca name */ @@ -237,6 +245,8 @@ int umad_register2(int port_fd, struct umad_reg_attr *attr, int umad_debug(int level); void umad_addr_dump(ib_mad_addr_t * addr); void umad_dump(void *umad); +int umad_get_smi_gsi_pairs(struct umad_ca_pair cas[], size_t max); +int umad_get_smi_gsi_pair_by_ca_name(const char *devname, uint8_t portnum, struct umad_ca_pair *ca_pair); static inline void *umad_alloc(int num, size_t size) { /* alloc array of umad buffers */