| /* | |
| * Copyright (c) 2004-2009 Voltaire Inc. All rights reserved. | |
| * Copyright (c) 2007 Xsigo Systems Inc. All rights reserved. | |
| * Copyright (c) 2008 Lawrence Livermore National Laboratory | |
| * Copyright (c) 2010-2011 Mellanox Technologies LTD. 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. | |
| * | |
| */ | |
| #define _GNU_SOURCE | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stddef.h> | |
| #include <unistd.h> | |
| #include <string.h> | |
| #include <errno.h> | |
| #include <inttypes.h> | |
| #include <infiniband/umad.h> | |
| #include <infiniband/mad.h> | |
| #include <util/iba_types.h> | |
| #include <infiniband/ibnetdisc.h> | |
| #include "internal.h" | |
| #include "chassis.h" | |
| #define container_of(ptr, type, member) \ | |
| ((type *)((uint8_t *)(ptr)-offsetof(type, member))) | |
| /* forward declarations */ | |
| struct ni_cbdata | |
| { | |
| ibnd_node_t *node; | |
| int port_num; | |
| }; | |
| static int query_node_info(smp_engine_t * engine, ib_portid_t * portid, | |
| struct ni_cbdata * cbdata); | |
| static int query_port_info(smp_engine_t * engine, ib_portid_t * portid, | |
| ibnd_node_t * node, int portnum); | |
| static int recv_switch_info(smp_engine_t * engine, ibnd_smp_t * smp, | |
| uint8_t * mad, void *cb_data) | |
| { | |
| uint8_t *switch_info = mad + IB_SMP_DATA_OFFS; | |
| ibnd_node_t *node = cb_data; | |
| memcpy(node->switchinfo, switch_info, sizeof(node->switchinfo)); | |
| mad_decode_field(node->switchinfo, IB_SW_ENHANCED_PORT0_F, | |
| &node->smaenhsp0); | |
| return 0; | |
| } | |
| static int query_switch_info(smp_engine_t * engine, ib_portid_t * portid, | |
| ibnd_node_t * node) | |
| { | |
| node->smaenhsp0 = 0; /* assume base SP0 */ | |
| return issue_smp(engine, portid, IB_ATTR_SWITCH_INFO, 0, | |
| recv_switch_info, node); | |
| } | |
| static int add_port_to_dpath(ib_dr_path_t * path, int nextport) | |
| { | |
| if (path->cnt > sizeof(path->p) - 2) | |
| return -1; | |
| ++path->cnt; | |
| path->p[path->cnt] = (uint8_t) nextport; | |
| return path->cnt; | |
| } | |
| static int retract_dpath(smp_engine_t * engine, ib_portid_t * portid) | |
| { | |
| ibnd_scan_t *scan = engine->user_data; | |
| f_internal_t *f_int = scan->f_int; | |
| if (scan->cfg->max_hops && | |
| f_int->fabric.maxhops_discovered > scan->cfg->max_hops) | |
| return 0; | |
| /* this may seem wrong but the only time we would retract the path is | |
| * if the user specified a CA for the DR path and we are retracting | |
| * from that to find the node it is connected to. This counts as a | |
| * positive hop discovered | |
| */ | |
| f_int->fabric.maxhops_discovered++; | |
| portid->drpath.p[portid->drpath.cnt] = 0; | |
| portid->drpath.cnt--; | |
| return 1; | |
| } | |
| static int extend_dpath(smp_engine_t * engine, ib_portid_t * portid, | |
| int nextport) | |
| { | |
| ibnd_scan_t *scan = engine->user_data; | |
| f_internal_t *f_int = scan->f_int; | |
| if (scan->cfg->max_hops && | |
| f_int->fabric.maxhops_discovered > scan->cfg->max_hops) | |
| return 0; | |
| if (portid->lid) { | |
| /* If we were LID routed we need to set up the drslid */ | |
| portid->drpath.drslid = (uint16_t) scan->selfportid.lid; | |
| portid->drpath.drdlid = 0xFFFF; | |
| } | |
| if (add_port_to_dpath(&portid->drpath, nextport) < 0) { | |
| IBND_ERROR("add port %d to DR path failed; %s\n", nextport, | |
| portid2str(portid)); | |
| return -1; | |
| } | |
| if (((unsigned) portid->drpath.cnt - scan->initial_hops) > | |
| f_int->fabric.maxhops_discovered) | |
| f_int->fabric.maxhops_discovered++; | |
| return 1; | |
| } | |
| static int recv_node_desc(smp_engine_t * engine, ibnd_smp_t * smp, | |
| uint8_t * mad, void *cb_data) | |
| { | |
| uint8_t *node_desc = mad + IB_SMP_DATA_OFFS; | |
| ibnd_node_t *node = cb_data; | |
| memcpy(node->nodedesc, node_desc, sizeof(node->nodedesc)); | |
| return 0; | |
| } | |
| static int query_node_desc(smp_engine_t * engine, ib_portid_t * portid, | |
| ibnd_node_t * node) | |
| { | |
| return issue_smp(engine, portid, IB_ATTR_NODE_DESC, 0, | |
| recv_node_desc, node); | |
| } | |
| static void debug_port(ib_portid_t * portid, ibnd_port_t * port) | |
| { | |
| char width[64], speed[64]; | |
| int iwidth; | |
| int ispeed, fdr10, espeed; | |
| uint8_t *info; | |
| iwidth = mad_get_field(port->info, 0, IB_PORT_LINK_WIDTH_ACTIVE_F); | |
| ispeed = mad_get_field(port->info, 0, IB_PORT_LINK_SPEED_ACTIVE_F); | |
| fdr10 = mad_get_field(port->ext_info, 0, | |
| IB_MLNX_EXT_PORT_LINK_SPEED_ACTIVE_F); | |
| if (port->node->type == IB_NODE_SWITCH) | |
| info = (uint8_t *)&port->node->ports[0]->info; | |
| else | |
| info = (uint8_t *)&port->info; | |
| espeed = ibnd_get_agg_linkspeedext(info, port->info); | |
| IBND_DEBUG | |
| ("portid %s portnum %d: base lid %d state %d physstate %d %s %s %s %s\n", | |
| portid2str(portid), port->portnum, port->base_lid, | |
| mad_get_field(port->info, 0, IB_PORT_STATE_F), | |
| mad_get_field(port->info, 0, IB_PORT_PHYS_STATE_F), | |
| mad_dump_val(IB_PORT_LINK_WIDTH_ACTIVE_F, width, 64, &iwidth), | |
| mad_dump_val(IB_PORT_LINK_SPEED_ACTIVE_F, speed, 64, &ispeed), | |
| (fdr10 & FDR10) ? "FDR10" : "", | |
| ibnd_dump_agg_linkspeedext(speed, 64, espeed)); | |
| } | |
| static int is_mlnx_ext_port_info_supported(ibnd_port_t * port) | |
| { | |
| uint16_t devid = (uint16_t) mad_get_field(port->node->info, 0, IB_NODE_DEVID_F); | |
| uint32_t vendorid = (uint32_t) mad_get_field(port->node->info, 0, IB_NODE_VENDORID_F); | |
| if ((devid >= 0xc738 && devid <= 0xc73b) || | |
| devid == 0xc839 || devid == 0xcb20 || devid == 0xcf08 || | |
| devid == 0xcf09 || devid == 0xd2f0 || | |
| ((vendorid == 0x119f) && | |
| /* Bull SwitchX */ | |
| (devid == 0x1b02 || devid == 0x1b50 || | |
| /* Bull SwitchIB and SwitchIB2 */ | |
| devid == 0x1ba0 || | |
| (devid >= 0x1bd0 && devid <= 0x1bd5) || | |
| /* Bull Quantum */ | |
| devid == 0x1bf0))) | |
| return 1; | |
| if ((devid >= 0x1003 && devid <= 0x101b) || (devid == 0xa2d2) || | |
| ((vendorid == 0x119f) && | |
| /* Bull ConnectX3 */ | |
| (devid == 0x1b33 || devid == 0x1b73 || | |
| devid == 0x1b40 || devid == 0x1b41 || | |
| devid == 0x1b60 || devid == 0x1b61 || | |
| /* Bull ConnectIB */ | |
| devid == 0x1b83 || | |
| devid == 0x1b93 || devid == 0x1b94 || | |
| /* Bull ConnectX4, Sequana HDR and HDR100 */ | |
| devid == 0x1bb4 || devid == 0x1bb5 || | |
| (devid >= 0x1bc4 && devid <= 0x1bc6)))) | |
| return 1; | |
| return 0; | |
| } | |
| int mlnx_ext_port_info_err(smp_engine_t * engine, ibnd_smp_t * smp, | |
| uint8_t * mad, void *cb_data) | |
| { | |
| f_internal_t *f_int = ((ibnd_scan_t *) engine->user_data)->f_int; | |
| ibnd_node_t *node = cb_data; | |
| ibnd_port_t *port; | |
| uint8_t port_num, local_port; | |
| port_num = (uint8_t) mad_get_field(mad, 0, IB_MAD_ATTRMOD_F); | |
| port = node->ports[port_num]; | |
| if (!port) { | |
| IBND_ERROR("Failed to find 0x%" PRIx64 " port %u\n", | |
| node->guid, port_num); | |
| return -1; | |
| } | |
| local_port = (uint8_t) mad_get_field(port->info, 0, IB_PORT_LOCAL_PORT_F); | |
| debug_port(&smp->path, port); | |
| if (port_num && mad_get_field(port->info, 0, IB_PORT_PHYS_STATE_F) | |
| == IB_PORT_PHYS_STATE_LINKUP | |
| && ((node->type == IB_NODE_SWITCH && port_num != local_port) || | |
| (node == f_int->fabric.from_node && port_num == f_int->fabric.from_portnum))) { | |
| int rc = 0; | |
| ib_portid_t path = smp->path; | |
| if (node->type != IB_NODE_SWITCH && | |
| node == f_int->fabric.from_node && | |
| path.drpath.cnt > 1) | |
| rc = retract_dpath(engine, &path); | |
| else { | |
| /* we can't proceed through an HCA with DR */ | |
| if (path.lid == 0 || node->type == IB_NODE_SWITCH) | |
| rc = extend_dpath(engine, &path, port_num); | |
| } | |
| if (rc > 0) { | |
| struct ni_cbdata * cbdata = malloc(sizeof(*cbdata)); | |
| cbdata->node = node; | |
| cbdata->port_num = port_num; | |
| query_node_info(engine, &path, cbdata); | |
| } | |
| } | |
| return 0; | |
| } | |
| static int recv_mlnx_ext_port_info(smp_engine_t * engine, ibnd_smp_t * smp, | |
| uint8_t * mad, void *cb_data) | |
| { | |
| f_internal_t *f_int = ((ibnd_scan_t *) engine->user_data)->f_int; | |
| ibnd_node_t *node = cb_data; | |
| ibnd_port_t *port; | |
| uint8_t *ext_port_info = mad + IB_SMP_DATA_OFFS; | |
| uint8_t port_num, local_port; | |
| port_num = (uint8_t) mad_get_field(mad, 0, IB_MAD_ATTRMOD_F); | |
| port = node->ports[port_num]; | |
| if (!port) { | |
| IBND_ERROR("Failed to find 0x%" PRIx64 " port %u\n", | |
| node->guid, port_num); | |
| return -1; | |
| } | |
| memcpy(port->ext_info, ext_port_info, sizeof(port->ext_info)); | |
| local_port = (uint8_t) mad_get_field(port->info, 0, IB_PORT_LOCAL_PORT_F); | |
| debug_port(&smp->path, port); | |
| if (port_num && mad_get_field(port->info, 0, IB_PORT_PHYS_STATE_F) | |
| == IB_PORT_PHYS_STATE_LINKUP | |
| && ((node->type == IB_NODE_SWITCH && port_num != local_port) || | |
| (node == f_int->fabric.from_node && port_num == f_int->fabric.from_portnum))) { | |
| int rc = 0; | |
| ib_portid_t path = smp->path; | |
| if (node->type != IB_NODE_SWITCH && | |
| node == f_int->fabric.from_node && | |
| path.drpath.cnt > 1) | |
| rc = retract_dpath(engine, &path); | |
| else { | |
| /* we can't proceed through an HCA with DR */ | |
| if (path.lid == 0 || node->type == IB_NODE_SWITCH) | |
| rc = extend_dpath(engine, &path, port_num); | |
| } | |
| if (rc > 0) { | |
| struct ni_cbdata * cbdata = malloc(sizeof(*cbdata)); | |
| cbdata->node = node; | |
| cbdata->port_num = port_num; | |
| query_node_info(engine, &path, cbdata); | |
| } | |
| } | |
| return 0; | |
| } | |
| static int query_mlnx_ext_port_info(smp_engine_t * engine, ib_portid_t * portid, | |
| ibnd_node_t * node, int portnum) | |
| { | |
| IBND_DEBUG("Query MLNX Extended Port Info; %s (0x%" PRIx64 "):%d\n", | |
| portid2str(portid), node->guid, portnum); | |
| return issue_smp(engine, portid, IB_ATTR_MLNX_EXT_PORT_INFO, portnum, | |
| recv_mlnx_ext_port_info, node); | |
| } | |
| static int recv_port_info(smp_engine_t * engine, ibnd_smp_t * smp, | |
| uint8_t * mad, void *cb_data) | |
| { | |
| ibnd_scan_t *scan = (ibnd_scan_t *)engine->user_data; | |
| f_internal_t *f_int = scan->f_int; | |
| ibnd_node_t *node = cb_data; | |
| ibnd_port_t *port; | |
| uint8_t *port_info = mad + IB_SMP_DATA_OFFS; | |
| uint8_t port_num, local_port; | |
| int phystate, ispeed, espeed; | |
| uint8_t *info; | |
| port_num = (uint8_t) mad_get_field(mad, 0, IB_MAD_ATTRMOD_F); | |
| local_port = (uint8_t) mad_get_field(port_info, 0, IB_PORT_LOCAL_PORT_F); | |
| /* this may have been created before */ | |
| port = node->ports[port_num]; | |
| if (!port) { | |
| port = node->ports[port_num] = calloc(1, sizeof(*port)); | |
| if (!port) { | |
| IBND_ERROR("Failed to allocate 0x%" PRIx64 " port %u\n", | |
| node->guid, port_num); | |
| return -1; | |
| } | |
| port->guid = | |
| mad_get_field64(node->info, 0, IB_NODE_PORT_GUID_F); | |
| } | |
| memcpy(port->info, port_info, sizeof(port->info)); | |
| port->node = node; | |
| port->portnum = port_num; | |
| port->ext_portnum = 0; | |
| port->base_lid = (uint16_t) mad_get_field(port->info, 0, IB_PORT_LID_F); | |
| port->lmc = (uint8_t) mad_get_field(port->info, 0, IB_PORT_LMC_F); | |
| if (port_num == 0) { | |
| node->smalid = port->base_lid; | |
| node->smalmc = port->lmc; | |
| } else if (node->type == IB_NODE_SWITCH) { | |
| port->base_lid = node->smalid; | |
| port->lmc = node->smalmc; | |
| } | |
| int rc1 = add_to_portguid_hash(port, f_int->fabric.portstbl); | |
| if (rc1) | |
| IBND_ERROR("Error Occurred when trying" | |
| " to insert new port guid 0x%016" PRIx64 " to DB\n", | |
| port->guid); | |
| add_to_portlid_hash(port, f_int); | |
| if ((scan->cfg->flags & IBND_CONFIG_MLX_EPI) | |
| && is_mlnx_ext_port_info_supported(port)) { | |
| phystate = mad_get_field(port->info, 0, IB_PORT_PHYS_STATE_F); | |
| ispeed = mad_get_field(port->info, 0, IB_PORT_LINK_SPEED_ACTIVE_F); | |
| if (port->node->type == IB_NODE_SWITCH) | |
| info = (uint8_t *)&port->node->ports[0]->info; | |
| else | |
| info = (uint8_t *)&port->info; | |
| espeed = ibnd_get_agg_linkspeedext(info, port->info); | |
| if (phystate == IB_PORT_PHYS_STATE_LINKUP && | |
| ispeed == IB_LINK_SPEED_ACTIVE_10 && | |
| espeed == IB_LINK_SPEED_EXT_ACTIVE_NONE) { /* LinkUp/QDR */ | |
| query_mlnx_ext_port_info(engine, &smp->path, | |
| node, port_num); | |
| return 0; | |
| } | |
| } | |
| debug_port(&smp->path, port); | |
| if (port_num && mad_get_field(port->info, 0, IB_PORT_PHYS_STATE_F) | |
| == IB_PORT_PHYS_STATE_LINKUP | |
| && ((node->type == IB_NODE_SWITCH && port_num != local_port) || | |
| (node == f_int->fabric.from_node && port_num == f_int->fabric.from_portnum))) { | |
| int rc = 0; | |
| ib_portid_t path = smp->path; | |
| if (node->type != IB_NODE_SWITCH && | |
| node == f_int->fabric.from_node && | |
| path.drpath.cnt > 1) | |
| rc = retract_dpath(engine, &path); | |
| else { | |
| /* we can't proceed through an HCA with DR */ | |
| if (path.lid == 0 || node->type == IB_NODE_SWITCH) | |
| rc = extend_dpath(engine, &path, port_num); | |
| } | |
| if (rc > 0) { | |
| struct ni_cbdata * cbdata = malloc(sizeof(*cbdata)); | |
| cbdata->node = node; | |
| cbdata->port_num = port_num; | |
| query_node_info(engine, &path, cbdata); | |
| } | |
| } | |
| return 0; | |
| } | |
| static int recv_port0_info(smp_engine_t * engine, ibnd_smp_t * smp, | |
| uint8_t * mad, void *cb_data) | |
| { | |
| ibnd_node_t *node = cb_data; | |
| int i, status; | |
| status = recv_port_info(engine, smp, mad, cb_data); | |
| /* Query PortInfo on switch external/physical ports */ | |
| for (i = 1; i <= node->numports; i++) | |
| query_port_info(engine, &smp->path, node, i); | |
| return status; | |
| } | |
| static int query_port_info(smp_engine_t * engine, ib_portid_t * portid, | |
| ibnd_node_t * node, int portnum) | |
| { | |
| IBND_DEBUG("Query Port Info; %s (0x%" PRIx64 "):%d\n", | |
| portid2str(portid), node->guid, portnum); | |
| return issue_smp(engine, portid, IB_ATTR_PORT_INFO, portnum, | |
| portnum ? recv_port_info : recv_port0_info, node); | |
| } | |
| static ibnd_node_t *create_node(smp_engine_t * engine, ib_portid_t * path, | |
| uint8_t * node_info) | |
| { | |
| f_internal_t *f_int = ((ibnd_scan_t *) engine->user_data)->f_int; | |
| ibnd_node_t *rc = calloc(1, sizeof(*rc)); | |
| if (!rc) { | |
| IBND_ERROR("OOM: node creation failed\n"); | |
| return NULL; | |
| } | |
| /* decode just a couple of fields for quicker reference. */ | |
| mad_decode_field(node_info, IB_NODE_GUID_F, &rc->guid); | |
| mad_decode_field(node_info, IB_NODE_TYPE_F, &rc->type); | |
| mad_decode_field(node_info, IB_NODE_NPORTS_F, &rc->numports); | |
| rc->ports = calloc(rc->numports + 1, sizeof(*rc->ports)); | |
| if (!rc->ports) { | |
| free(rc); | |
| IBND_ERROR("OOM: Failed to allocate the ports array\n"); | |
| return NULL; | |
| } | |
| rc->path_portid = *path; | |
| memcpy(rc->info, node_info, sizeof(rc->info)); | |
| int rc1 = add_to_nodeguid_hash(rc, f_int->fabric.nodestbl); | |
| if (rc1) | |
| IBND_ERROR("Error Occurred when trying" | |
| " to insert new node guid 0x%016" PRIx64 " to DB\n", | |
| rc->guid); | |
| /* add this to the all nodes list */ | |
| rc->next = f_int->fabric.nodes; | |
| f_int->fabric.nodes = rc; | |
| add_to_type_list(rc, f_int); | |
| return rc; | |
| } | |
| static void link_ports(ibnd_node_t * node, ibnd_port_t * port, | |
| ibnd_node_t * remotenode, ibnd_port_t * remoteport) | |
| { | |
| IBND_DEBUG("linking: 0x%" PRIx64 " %p->%p:%u and 0x%" PRIx64 | |
| " %p->%p:%u\n", node->guid, node, port, port->portnum, | |
| remotenode->guid, remotenode, remoteport, | |
| remoteport->portnum); | |
| if (port->remoteport) | |
| port->remoteport->remoteport = NULL; | |
| if (remoteport->remoteport) | |
| remoteport->remoteport->remoteport = NULL; | |
| port->remoteport = remoteport; | |
| remoteport->remoteport = port; | |
| } | |
| static void dump_endnode(ib_portid_t *path, const char *prompt, | |
| ibnd_node_t *node, ibnd_port_t *port) | |
| { | |
| char type[64]; | |
| mad_dump_node_type(type, sizeof(type), &node->type, sizeof(int)); | |
| printf("%s -> %s %s {%016" PRIx64 "} portnum %d lid %d-%d \"%s\"\n", | |
| portid2str(path), prompt, type, node->guid, | |
| node->type == IB_NODE_SWITCH ? 0 : port->portnum, | |
| port->base_lid, port->base_lid + (1 << port->lmc) - 1, | |
| node->nodedesc); | |
| } | |
| static int recv_node_info(smp_engine_t * engine, ibnd_smp_t * smp, | |
| uint8_t * mad, void *cb_data) | |
| { | |
| ibnd_scan_t *scan = engine->user_data; | |
| f_internal_t *f_int = scan->f_int; | |
| uint8_t *node_info = mad + IB_SMP_DATA_OFFS; | |
| struct ni_cbdata *ni_cbdata = (struct ni_cbdata *)cb_data; | |
| ibnd_node_t *rem_node = NULL; | |
| int rem_port_num = 0; | |
| ibnd_node_t *node; | |
| int node_is_new = 0; | |
| uint64_t node_guid = mad_get_field64(node_info, 0, IB_NODE_GUID_F); | |
| uint64_t port_guid = mad_get_field64(node_info, 0, IB_NODE_PORT_GUID_F); | |
| int port_num = mad_get_field(node_info, 0, IB_NODE_LOCAL_PORT_F); | |
| ibnd_port_t *port = NULL; | |
| if (ni_cbdata) { | |
| rem_node = ni_cbdata->node; | |
| rem_port_num = ni_cbdata->port_num; | |
| free(ni_cbdata); | |
| } | |
| node = ibnd_find_node_guid(&f_int->fabric, node_guid); | |
| if (!node) { | |
| node = create_node(engine, &smp->path, node_info); | |
| if (!node) | |
| return -1; | |
| node_is_new = 1; | |
| } | |
| IBND_DEBUG("Found %s node GUID 0x%" PRIx64 " (%s)\n", | |
| node_is_new ? "new" : "old", node->guid, | |
| portid2str(&smp->path)); | |
| port = node->ports[port_num]; | |
| if (!port) { | |
| /* If we have not see this port before create a shell for it */ | |
| port = node->ports[port_num] = calloc(1, sizeof(*port)); | |
| if (!port) | |
| return -1; | |
| port->node = node; | |
| port->portnum = port_num; | |
| } | |
| port->guid = port_guid; | |
| if (scan->cfg->show_progress) | |
| dump_endnode(&smp->path, node_is_new ? "new" : "known", | |
| node, port); | |
| if (rem_node == NULL) { /* this is the start node */ | |
| f_int->fabric.from_node = node; | |
| f_int->fabric.from_portnum = port_num; | |
| } else { | |
| /* link ports... */ | |
| if (!rem_node->ports[rem_port_num]) { | |
| IBND_ERROR("Internal Error; " | |
| "Node(%p) 0x%" PRIx64 | |
| " Port %d no port created!?!?!?\n\n", | |
| rem_node, rem_node->guid, rem_port_num); | |
| return -1; | |
| } | |
| link_ports(node, port, rem_node, rem_node->ports[rem_port_num]); | |
| } | |
| if (node_is_new) { | |
| query_node_desc(engine, &smp->path, node); | |
| if (node->type == IB_NODE_SWITCH) { | |
| query_switch_info(engine, &smp->path, node); | |
| /* Query PortInfo on Switch Port 0 first */ | |
| query_port_info(engine, &smp->path, node, 0); | |
| } | |
| } | |
| if (node->type != IB_NODE_SWITCH) | |
| query_port_info(engine, &smp->path, node, port_num); | |
| return 0; | |
| } | |
| static int query_node_info(smp_engine_t * engine, ib_portid_t * portid, | |
| struct ni_cbdata * cbdata) | |
| { | |
| IBND_DEBUG("Query Node Info; %s\n", portid2str(portid)); | |
| return issue_smp(engine, portid, IB_ATTR_NODE_INFO, 0, | |
| recv_node_info, (void *)cbdata); | |
| } | |
| ibnd_node_t *ibnd_find_node_guid(ibnd_fabric_t * fabric, uint64_t guid) | |
| { | |
| int hash = HASHGUID(guid) % HTSZ; | |
| ibnd_node_t *node; | |
| if (!fabric) { | |
| IBND_DEBUG("fabric parameter NULL\n"); | |
| return NULL; | |
| } | |
| for (node = fabric->nodestbl[hash]; node; node = node->htnext) | |
| if (node->guid == guid) | |
| return node; | |
| return NULL; | |
| } | |
| ibnd_node_t *ibnd_find_node_dr(ibnd_fabric_t * fabric, char *dr_str) | |
| { | |
| ibnd_port_t *rc = ibnd_find_port_dr(fabric, dr_str); | |
| return rc->node; | |
| } | |
| int add_to_nodeguid_hash(ibnd_node_t * node, ibnd_node_t * hash[]) | |
| { | |
| int rc = 0; | |
| ibnd_node_t *tblnode; | |
| int hash_idx = HASHGUID(node->guid) % HTSZ; | |
| for (tblnode = hash[hash_idx]; tblnode; tblnode = tblnode->htnext) { | |
| if (tblnode == node) { | |
| IBND_ERROR("Duplicate Node: Node with guid 0x%016" | |
| PRIx64 " already exists in nodes DB\n", | |
| node->guid); | |
| return 1; | |
| } | |
| } | |
| node->htnext = hash[hash_idx]; | |
| hash[hash_idx] = node; | |
| return rc; | |
| } | |
| int add_to_portguid_hash(ibnd_port_t * port, ibnd_port_t * hash[]) | |
| { | |
| int rc = 0; | |
| ibnd_port_t *tblport; | |
| int hash_idx = HASHGUID(port->guid) % HTSZ; | |
| for (tblport = hash[hash_idx]; tblport; tblport = tblport->htnext) { | |
| if (tblport == port) { | |
| IBND_ERROR("Duplicate Port: Port with guid 0x%016" | |
| PRIx64 " already exists in ports DB\n", | |
| port->guid); | |
| return 1; | |
| } | |
| } | |
| port->htnext = hash[hash_idx]; | |
| hash[hash_idx] = port; | |
| return rc; | |
| } | |
| struct lid2guid_item { | |
| cl_map_item_t cl_map; | |
| ibnd_port_t *port; | |
| }; | |
| void create_lid2guid(f_internal_t *f_int) | |
| { | |
| cl_qmap_init(&f_int->lid2guid); | |
| } | |
| void destroy_lid2guid(f_internal_t *f_int) | |
| { | |
| cl_map_item_t *item; | |
| for (item = cl_qmap_head(&f_int->lid2guid); item != cl_qmap_end(&f_int->lid2guid); | |
| item = cl_qmap_head(&f_int->lid2guid)) { | |
| cl_qmap_remove_item(&f_int->lid2guid, item); | |
| free(container_of(item, struct lid2guid_item, cl_map)); | |
| } | |
| } | |
| void add_to_portlid_hash(ibnd_port_t * port, f_internal_t *f_int) | |
| { | |
| uint16_t base_lid = port->base_lid; | |
| uint16_t lid_mask = ((1 << port->lmc) -1); | |
| uint16_t lid = 0; | |
| /* 0 < valid lid <= 0xbfff */ | |
| if (base_lid > 0 && base_lid <= 0xbfff) { | |
| /* We add the port for all lids | |
| * so it is easier to find any "random" lid specified */ | |
| for (lid = base_lid; lid <= (base_lid + lid_mask); lid++) { | |
| struct lid2guid_item *item; | |
| item = malloc(sizeof(*item)); | |
| if (item) { | |
| item->port = port; | |
| if (cl_qmap_insert(&f_int->lid2guid, lid, | |
| &item->cl_map) != &item->cl_map) { | |
| /* Port is already in map, release item */ | |
| free(item); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void add_to_type_list(ibnd_node_t * node, f_internal_t * f_int) | |
| { | |
| ibnd_fabric_t *fabric = &f_int->fabric; | |
| switch (node->type) { | |
| case IB_NODE_CA: | |
| node->type_next = fabric->ch_adapters; | |
| fabric->ch_adapters = node; | |
| break; | |
| case IB_NODE_SWITCH: | |
| node->type_next = fabric->switches; | |
| fabric->switches = node; | |
| break; | |
| case IB_NODE_ROUTER: | |
| node->type_next = fabric->routers; | |
| fabric->routers = node; | |
| break; | |
| } | |
| } | |
| static int set_config(struct ibnd_config *config, struct ibnd_config *cfg) | |
| { | |
| if (!config) | |
| return (-EINVAL); | |
| if (cfg) | |
| memcpy(config, cfg, sizeof(*config)); | |
| if (!config->max_smps) | |
| config->max_smps = DEFAULT_MAX_SMP_ON_WIRE; | |
| if (!config->timeout_ms) | |
| config->timeout_ms = DEFAULT_TIMEOUT; | |
| if (!config->retries) | |
| config->retries = DEFAULT_RETRIES; | |
| return (0); | |
| } | |
| f_internal_t *allocate_fabric_internal(void) | |
| { | |
| f_internal_t *f = calloc(1, sizeof(*f)); | |
| if (f) | |
| create_lid2guid(f); | |
| return (f); | |
| } | |
| ibnd_fabric_t *ibnd_discover_fabric(char * ca_name, int ca_port, | |
| ib_portid_t * from, | |
| struct ibnd_config *cfg) | |
| { | |
| struct ibnd_config config = { 0 }; | |
| f_internal_t *f_int = NULL; | |
| ib_portid_t my_portid = { 0 }; | |
| smp_engine_t engine; | |
| ibnd_scan_t scan; | |
| struct ibmad_port *ibmad_port; | |
| struct ibmad_ports_pair *ibmad_ports; | |
| int nc = 2; | |
| int mc[2] = { IB_SMI_CLASS, IB_SMI_DIRECT_CLASS }; | |
| /* If not specified start from "my" port */ | |
| if (!from) | |
| from = &my_portid; | |
| if (set_config(&config, cfg)) { | |
| IBND_ERROR("Invalid ibnd_config\n"); | |
| return NULL; | |
| } | |
| f_int = allocate_fabric_internal(); | |
| if (!f_int) { | |
| IBND_ERROR("OOM: failed to calloc ibnd_fabric_t\n"); | |
| return NULL; | |
| } | |
| memset(&scan.selfportid, 0, sizeof(scan.selfportid)); | |
| scan.f_int = f_int; | |
| scan.cfg = &config; | |
| scan.initial_hops = from->drpath.cnt; | |
| ibmad_ports = mad_rpc_open_port2(ca_name, ca_port, mc, nc, 1); | |
| if (!ibmad_ports) { | |
| IBND_ERROR("can't open MAD port (%s:%d)\n", ca_name, ca_port); | |
| goto error_int; | |
| } | |
| ibmad_port = ibmad_ports->smi.port; | |
| if (!ibmad_port) { | |
| IBND_ERROR("can't open MAD port (%s:%d)\n", ca_name, ca_port); | |
| goto error_int; | |
| } | |
| mad_rpc_set_timeout(ibmad_port, cfg->timeout_ms); | |
| mad_rpc_set_retries(ibmad_port, cfg->retries); | |
| smp_mkey_set(ibmad_port, cfg->mkey); | |
| if (ib_resolve_self_via(&scan.selfportid, | |
| NULL, NULL, ibmad_port) < 0) { | |
| IBND_ERROR("Failed to resolve self\n"); | |
| mad_rpc_close_port2(ibmad_ports); | |
| goto error_int; | |
| } | |
| //in case of smi/gsi seperation make sure we take the smi name | |
| char fixed_ca_name[UMAD_CA_NAME_LEN]; | |
| memset(fixed_ca_name, 0, UMAD_CA_NAME_LEN); | |
| strncpy(fixed_ca_name, ibmad_ports->smi.ca_name, UMAD_CA_NAME_LEN); | |
| mad_rpc_close_port2(ibmad_ports); | |
| if (smp_engine_init(&engine, fixed_ca_name, ca_port, &scan, &config)) { | |
| goto error_int; | |
| } | |
| IBND_DEBUG("from %s\n", portid2str(from)); | |
| if (!query_node_info(&engine, from, NULL)) | |
| if (process_mads(&engine) != 0) | |
| goto error; | |
| f_int->fabric.total_mads_used = engine.total_smps; | |
| f_int->fabric.maxhops_discovered += scan.initial_hops; | |
| if (group_nodes(&f_int->fabric)) | |
| goto error; | |
| smp_engine_destroy(&engine); | |
| return (ibnd_fabric_t *)f_int; | |
| error: | |
| smp_engine_destroy(&engine); | |
| ibnd_destroy_fabric(&f_int->fabric); | |
| error_int: | |
| free(f_int); | |
| return NULL; | |
| } | |
| void destroy_node(ibnd_node_t * node) | |
| { | |
| int p = 0; | |
| if (node->ports) { | |
| for (p = 0; p <= node->numports; p++) | |
| free(node->ports[p]); | |
| free(node->ports); | |
| } | |
| free(node); | |
| } | |
| void ibnd_destroy_fabric(ibnd_fabric_t * fabric) | |
| { | |
| ibnd_node_t *node = NULL; | |
| ibnd_node_t *next = NULL; | |
| ibnd_chassis_t *ch, *ch_next; | |
| if (!fabric) | |
| return; | |
| ch = fabric->chassis; | |
| while (ch) { | |
| ch_next = ch->next; | |
| free(ch); | |
| ch = ch_next; | |
| } | |
| node = fabric->nodes; | |
| while (node) { | |
| next = node->next; | |
| destroy_node(node); | |
| node = next; | |
| } | |
| destroy_lid2guid((f_internal_t *)fabric); | |
| free(fabric); | |
| } | |
| void ibnd_iter_nodes(ibnd_fabric_t * fabric, ibnd_iter_node_func_t func, | |
| void *user_data) | |
| { | |
| ibnd_node_t *cur = NULL; | |
| if (!fabric) { | |
| IBND_DEBUG("fabric parameter NULL\n"); | |
| return; | |
| } | |
| if (!func) { | |
| IBND_DEBUG("func parameter NULL\n"); | |
| return; | |
| } | |
| for (cur = fabric->nodes; cur; cur = cur->next) | |
| func(cur, user_data); | |
| } | |
| void ibnd_iter_nodes_type(ibnd_fabric_t * fabric, ibnd_iter_node_func_t func, | |
| int node_type, void *user_data) | |
| { | |
| ibnd_node_t *list = NULL; | |
| ibnd_node_t *cur = NULL; | |
| if (!fabric) { | |
| IBND_DEBUG("fabric parameter NULL\n"); | |
| return; | |
| } | |
| if (!func) { | |
| IBND_DEBUG("func parameter NULL\n"); | |
| return; | |
| } | |
| switch (node_type) { | |
| case IB_NODE_SWITCH: | |
| list = fabric->switches; | |
| break; | |
| case IB_NODE_CA: | |
| list = fabric->ch_adapters; | |
| break; | |
| case IB_NODE_ROUTER: | |
| list = fabric->routers; | |
| break; | |
| default: | |
| IBND_DEBUG("Invalid node_type specified %d\n", node_type); | |
| break; | |
| } | |
| for (cur = list; cur; cur = cur->type_next) | |
| func(cur, user_data); | |
| } | |
| ibnd_port_t *ibnd_find_port_lid(ibnd_fabric_t * fabric, | |
| uint16_t lid) | |
| { | |
| f_internal_t *f = (f_internal_t *)fabric; | |
| cl_map_item_t *p_item = cl_qmap_get(&f->lid2guid, lid); | |
| if (p_item == &f->lid2guid.nil) | |
| return NULL; | |
| return container_of(p_item, | |
| struct lid2guid_item, cl_map) | |
| ->port; | |
| } | |
| ibnd_port_t *ibnd_find_port_guid(ibnd_fabric_t * fabric, uint64_t guid) | |
| { | |
| int hash = HASHGUID(guid) % HTSZ; | |
| ibnd_port_t *port; | |
| if (!fabric) { | |
| IBND_DEBUG("fabric parameter NULL\n"); | |
| return NULL; | |
| } | |
| for (port = fabric->portstbl[hash]; port; port = port->htnext) | |
| if (port->guid == guid) | |
| return port; | |
| return NULL; | |
| } | |
| ibnd_port_t *ibnd_find_port_dr(ibnd_fabric_t * fabric, char *dr_str) | |
| { | |
| int i = 0; | |
| ibnd_node_t *cur_node; | |
| ibnd_port_t *rc = NULL; | |
| ib_dr_path_t path; | |
| if (!fabric) { | |
| IBND_DEBUG("fabric parameter NULL\n"); | |
| return NULL; | |
| } | |
| if (!dr_str) { | |
| IBND_DEBUG("dr_str parameter NULL\n"); | |
| return NULL; | |
| } | |
| cur_node = fabric->from_node; | |
| if (str2drpath(&path, dr_str, 0, 0) == -1) | |
| return NULL; | |
| for (i = 0; i <= path.cnt; i++) { | |
| ibnd_port_t *remote_port = NULL; | |
| if (path.p[i] == 0) | |
| continue; | |
| if (!cur_node->ports) | |
| return NULL; | |
| remote_port = cur_node->ports[path.p[i]]->remoteport; | |
| if (!remote_port) | |
| return NULL; | |
| rc = remote_port; | |
| cur_node = remote_port->node; | |
| } | |
| return rc; | |
| } | |
| void ibnd_iter_ports(ibnd_fabric_t * fabric, ibnd_iter_port_func_t func, | |
| void *user_data) | |
| { | |
| int i = 0; | |
| ibnd_port_t *cur = NULL; | |
| if (!fabric) { | |
| IBND_DEBUG("fabric parameter NULL\n"); | |
| return; | |
| } | |
| if (!func) { | |
| IBND_DEBUG("func parameter NULL\n"); | |
| return; | |
| } | |
| for (i = 0; i<HTSZ; i++) | |
| for (cur = fabric->portstbl[i]; cur; cur = cur->htnext) | |
| func(cur, user_data); | |
| } | |
| int ibnd_get_agg_linkspeedext_field(void *cap_info, void *info, | |
| enum MAD_FIELDS efield, enum MAD_FIELDS e2field) | |
| { | |
| int espeed = 0, e2speed = 0; | |
| int cap_mask = cap_info ? mad_get_field(cap_info, 0, IB_PORT_CAPMASK_F) : 0; | |
| int cap_mask2 = 0; | |
| if (cap_mask & be32toh(IB_PORT_CAP_HAS_EXT_SPEEDS)) { | |
| espeed = mad_get_field(info, 0, efield); | |
| if (efield == IB_PORT_LINK_SPEED_EXT_ENABLED_F) | |
| if (espeed == 30) | |
| espeed = 0; | |
| if (cap_mask & be32toh(IB_PORT_CAP_HAS_CAP_MASK2)) | |
| cap_mask2 = cap_info ? mad_get_field(cap_info, 0, IB_PORT_CAPMASK2_F) : 0; | |
| if (cap_mask2 & be16toh(IB_PORT_CAP2_IS_EXT_SPEEDS_2_SUPPORTED)) { | |
| e2speed = (mad_get_field(info, 0, e2field) << 5); | |
| } | |
| } | |
| if (efield == IB_PORT_LINK_SPEED_EXT_ACTIVE_F) | |
| return e2speed ? e2speed : espeed; | |
| return espeed | e2speed; | |
| } | |
| int ibnd_get_agg_linkspeedext(void *cap_info, void *info) | |
| { | |
| return ibnd_get_agg_linkspeedext_field(cap_info, info, | |
| IB_PORT_LINK_SPEED_EXT_ACTIVE_F, | |
| IB_PORT_LINK_SPEED_EXT_ACTIVE_2_F); | |
| } | |
| int ibnd_get_agg_linkspeedexten(void *cap_info, void *info) | |
| { | |
| return ibnd_get_agg_linkspeedext_field(cap_info, info, | |
| IB_PORT_LINK_SPEED_EXT_ENABLED_F, | |
| IB_PORT_LINK_SPEED_EXT_ENABLED_2_F); | |
| } | |
| int ibnd_get_agg_linkspeedextsup(void *cap_info, void *info) | |
| { | |
| return ibnd_get_agg_linkspeedext_field(cap_info, info, | |
| IB_PORT_LINK_SPEED_EXT_SUPPORTED_F, | |
| IB_PORT_LINK_SPEED_EXT_SUPPORTED_2_F); | |
| } | |
| char *ibnd_dump_agg_linkspeedext(char *buf, int bufsz, int speed) | |
| { | |
| switch (speed) { | |
| case 0: | |
| snprintf(buf, bufsz, "No Extended Speed"); | |
| break; | |
| case 1: | |
| snprintf(buf, bufsz, "14.0625 Gbps"); | |
| break; | |
| case 2: | |
| snprintf(buf, bufsz, "25.78125 Gbps"); | |
| break; | |
| case 4: | |
| snprintf(buf, bufsz, "53.125 Gbps"); | |
| break; | |
| case 8: | |
| snprintf(buf, bufsz, "106.25 Gbps"); | |
| break; | |
| /* case 16: not used value */ | |
| case 32: | |
| snprintf(buf, bufsz, "212.5 Gbps"); | |
| break; | |
| default: | |
| snprintf(buf, bufsz, "undefined (%d)", speed); | |
| break; | |
| } | |
| return buf; | |
| } | |
| char *ibnd_dump_agg_linkspeedext_bits(char *buf, int bufsz, int speed) | |
| { | |
| int n = 0; | |
| if (speed == 0) { | |
| snprintf(buf, bufsz, "%d", speed); | |
| return buf; | |
| } | |
| if (speed & 0x1) | |
| n += snprintf(buf + n, bufsz - n, "14.0625 Gbps or "); | |
| if (n < bufsz && (speed & 0x02)) | |
| n += snprintf(buf + n, bufsz - n, "25.78125 Gbps or "); | |
| if (n < bufsz && (speed & 0x04)) | |
| n += snprintf(buf + n, bufsz - n, "53.125 Gbps or "); | |
| if (n < bufsz && (speed & 0x08)) | |
| n += snprintf(buf + n, bufsz - n, "106.25 Gbps or "); | |
| if (n < bufsz && (speed & 0x20)) | |
| n += snprintf(buf + n, bufsz - n, "212.5 Gbps or "); | |
| if (speed >> 6) { | |
| n += snprintf(buf + n, bufsz - n, "undefined (%d)", speed); | |
| return buf; | |
| } else if (bufsz > 3) | |
| buf[n - 4] = '\0'; | |
| return buf; | |
| } | |
| char *ibnd_dump_agg_linkspeedexten(char *buf, int bufsz, int speed) | |
| { | |
| return ibnd_dump_agg_linkspeedext_bits(buf, bufsz, speed); | |
| } | |
| char *ibnd_dump_agg_linkspeedextsup(char *buf, int bufsz, int speed) | |
| { | |
| return ibnd_dump_agg_linkspeedext_bits(buf, bufsz, speed); | |
| } |