| /* | |
| * Copyright (c) 2010 Lawrence Livermore National Laboratory | |
| * Copyright (c) 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. | |
| * | |
| */ | |
| #include <errno.h> | |
| #include <infiniband/ibnetdisc.h> | |
| #include <infiniband/umad.h> | |
| #include "internal.h" | |
| static void queue_smp(smp_engine_t * engine, ibnd_smp_t * smp) | |
| { | |
| smp->qnext = NULL; | |
| if (!engine->smp_queue_head) { | |
| engine->smp_queue_head = smp; | |
| engine->smp_queue_tail = smp; | |
| } else { | |
| engine->smp_queue_tail->qnext = smp; | |
| engine->smp_queue_tail = smp; | |
| } | |
| } | |
| static ibnd_smp_t *get_smp(smp_engine_t * engine) | |
| { | |
| ibnd_smp_t *head = engine->smp_queue_head; | |
| ibnd_smp_t *tail = engine->smp_queue_tail; | |
| ibnd_smp_t *rc = head; | |
| if (head) { | |
| if (tail == head) | |
| engine->smp_queue_tail = NULL; | |
| engine->smp_queue_head = head->qnext; | |
| } | |
| return rc; | |
| } | |
| static int send_smp(ibnd_smp_t * smp, smp_engine_t * engine) | |
| { | |
| int rc = 0; | |
| uint8_t umad[1024]; | |
| ib_rpc_t *rpc = &smp->rpc; | |
| int agent = 0; | |
| memset(umad, 0, umad_size() + IB_MAD_SIZE); | |
| if (rpc->mgtclass == IB_SMI_CLASS) { | |
| agent = engine->smi_agent; | |
| } else if (rpc->mgtclass == IB_SMI_DIRECT_CLASS) { | |
| agent = engine->smi_dir_agent; | |
| } else { | |
| IBND_ERROR("Invalid class for RPC\n"); | |
| return (-EIO); | |
| } | |
| if ((rc = mad_build_pkt(umad, &smp->rpc, &smp->path, NULL, NULL)) | |
| < 0) { | |
| IBND_ERROR("mad_build_pkt failed; %d\n", rc); | |
| return rc; | |
| } | |
| if ((rc = umad_send(engine->umad_fd, agent, umad, IB_MAD_SIZE, | |
| engine->cfg->timeout_ms, engine->cfg->retries)) < 0) { | |
| IBND_ERROR("send failed; %d\n", rc); | |
| return rc; | |
| } | |
| return 0; | |
| } | |
| static int process_smp_queue(smp_engine_t * engine) | |
| { | |
| int rc = 0; | |
| ibnd_smp_t *smp; | |
| while (cl_qmap_count(&engine->smps_on_wire) | |
| < engine->cfg->max_smps) { | |
| smp = get_smp(engine); | |
| if (!smp) | |
| return 0; | |
| if ((rc = send_smp(smp, engine)) != 0) { | |
| free(smp); | |
| return rc; | |
| } | |
| cl_qmap_insert(&engine->smps_on_wire, (uint32_t) smp->rpc.trid, | |
| (cl_map_item_t *) smp); | |
| engine->total_smps++; | |
| } | |
| return 0; | |
| } | |
| int issue_smp(smp_engine_t * engine, ib_portid_t * portid, | |
| unsigned attrid, unsigned mod, smp_comp_cb_t cb, void *cb_data) | |
| { | |
| ibnd_smp_t *smp = calloc(1, sizeof *smp); | |
| if (!smp) { | |
| IBND_ERROR("OOM\n"); | |
| return -ENOMEM; | |
| } | |
| smp->cb = cb; | |
| smp->cb_data = cb_data; | |
| smp->path = *portid; | |
| smp->rpc.method = IB_MAD_METHOD_GET; | |
| smp->rpc.attr.id = attrid; | |
| smp->rpc.attr.mod = mod; | |
| smp->rpc.timeout = engine->cfg->timeout_ms; | |
| smp->rpc.datasz = IB_SMP_DATA_SIZE; | |
| smp->rpc.dataoffs = IB_SMP_DATA_OFFS; | |
| smp->rpc.trid = mad_trid(); | |
| smp->rpc.mkey = engine->cfg->mkey; | |
| if (portid->lid <= 0 || portid->drpath.drslid == 0xffff || | |
| portid->drpath.drdlid == 0xffff) | |
| smp->rpc.mgtclass = IB_SMI_DIRECT_CLASS; /* direct SMI */ | |
| else | |
| smp->rpc.mgtclass = IB_SMI_CLASS; /* Lid routed SMI */ | |
| portid->sl = 0; | |
| portid->qp = 0; | |
| queue_smp(engine, smp); | |
| return process_smp_queue(engine); | |
| } | |
| static int process_one_recv(smp_engine_t * engine) | |
| { | |
| int rc = 0; | |
| int status = 0; | |
| ibnd_smp_t *smp; | |
| uint8_t *mad; | |
| uint32_t trid; | |
| uint8_t umad[sizeof(struct ib_user_mad) + IB_MAD_SIZE]; | |
| int length = umad_size() + IB_MAD_SIZE; | |
| memset(umad, 0, sizeof(umad)); | |
| /* wait for the next message */ | |
| if ((rc = umad_recv(engine->umad_fd, umad, &length, | |
| -1)) < 0) { | |
| IBND_ERROR("umad_recv failed: %d\n", rc); | |
| return -1; | |
| } | |
| mad = umad_get_mad(umad); | |
| trid = (uint32_t) mad_get_field64(mad, 0, IB_MAD_TRID_F); | |
| smp = (ibnd_smp_t *) cl_qmap_remove(&engine->smps_on_wire, trid); | |
| if ((cl_map_item_t *) smp == cl_qmap_end(&engine->smps_on_wire)) { | |
| IBND_ERROR("Failed to find matching smp for trid (%x)\n", trid); | |
| return -1; | |
| } | |
| rc = process_smp_queue(engine); | |
| if (rc) | |
| goto error; | |
| if ((status = umad_status(umad))) { | |
| IBND_ERROR("umad (%s Attr 0x%x:%u) bad status %d; %s\n", | |
| portid2str(&smp->path), smp->rpc.attr.id, | |
| smp->rpc.attr.mod, status, strerror(status)); | |
| if (smp->rpc.attr.id == IB_ATTR_MLNX_EXT_PORT_INFO) | |
| rc = mlnx_ext_port_info_err(engine, smp, mad, | |
| smp->cb_data); | |
| } else if ((status = mad_get_field(mad, 0, IB_DRSMP_STATUS_F))) { | |
| IBND_ERROR("mad (%s Attr 0x%x:%u) bad status 0x%x\n", | |
| portid2str(&smp->path), smp->rpc.attr.id, | |
| smp->rpc.attr.mod, status); | |
| if (smp->rpc.attr.id == IB_ATTR_MLNX_EXT_PORT_INFO) | |
| rc = mlnx_ext_port_info_err(engine, smp, mad, | |
| smp->cb_data); | |
| } else | |
| rc = smp->cb(engine, smp, mad, smp->cb_data); | |
| error: | |
| free(smp); | |
| return rc; | |
| } | |
| int smp_engine_init(smp_engine_t * engine, char * ca_name, int ca_port, | |
| void *user_data, ibnd_config_t *cfg) | |
| { | |
| memset(engine, 0, sizeof(*engine)); | |
| if (umad_init() < 0) { | |
| IBND_ERROR("umad_init failed\n"); | |
| return -EIO; | |
| } | |
| engine->umad_fd = umad_open_port(ca_name, ca_port); | |
| if (engine->umad_fd < 0) { | |
| IBND_ERROR("can't open UMAD port (%s:%d)\n", ca_name, ca_port); | |
| return -EIO; | |
| } | |
| if ((engine->smi_agent = umad_register(engine->umad_fd, | |
| IB_SMI_CLASS, 1, 0, NULL)) < 0) { | |
| IBND_ERROR("Failed to register SMI agent on (%s:%d)\n", | |
| ca_name, ca_port); | |
| goto eio_close; | |
| } | |
| if ((engine->smi_dir_agent = umad_register(engine->umad_fd, | |
| IB_SMI_DIRECT_CLASS, 1, 0, NULL)) < 0) { | |
| IBND_ERROR("Failed to register SMI_DIRECT agent on (%s:%d)\n", | |
| ca_name, ca_port); | |
| goto eio_close; | |
| } | |
| engine->user_data = user_data; | |
| cl_qmap_init(&engine->smps_on_wire); | |
| engine->cfg = cfg; | |
| return (0); | |
| eio_close: | |
| umad_close_port(engine->umad_fd); | |
| return (-EIO); | |
| } | |
| void smp_engine_destroy(smp_engine_t * engine) | |
| { | |
| cl_map_item_t *item; | |
| ibnd_smp_t *smp; | |
| /* remove queued smps */ | |
| smp = get_smp(engine); | |
| if (smp) | |
| IBND_ERROR("outstanding SMP's\n"); | |
| for ( /* */ ; smp; smp = get_smp(engine)) | |
| free(smp); | |
| /* remove smps from the wire queue */ | |
| item = cl_qmap_head(&engine->smps_on_wire); | |
| if (item != cl_qmap_end(&engine->smps_on_wire)) | |
| IBND_ERROR("outstanding SMP's on wire\n"); | |
| for ( /* */ ; item != cl_qmap_end(&engine->smps_on_wire); | |
| item = cl_qmap_head(&engine->smps_on_wire)) { | |
| cl_qmap_remove_item(&engine->smps_on_wire, item); | |
| free(item); | |
| } | |
| umad_close_port(engine->umad_fd); | |
| } | |
| int process_mads(smp_engine_t * engine) | |
| { | |
| int rc; | |
| while (!cl_is_qmap_empty(&engine->smps_on_wire)) | |
| if ((rc = process_one_recv(engine)) != 0) | |
| return rc; | |
| return 0; | |
| } |