| /* |
| * Copyright (c) 2006 Mellanox Technologies. 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. |
| * |
| * $Author: ishai Rabinovitz [ishai@mellanox.co.il]$ |
| * |
| */ |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <sys/time.h> |
| #include <endian.h> |
| #include <time.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <infiniband/verbs.h> |
| #include <infiniband/umad_sa.h> |
| #include <infiniband/umad_sm.h> |
| |
| #include "srp_ib_types.h" |
| |
| #include "srp_daemon.h" |
| |
| void srp_sleep(time_t sec, time_t usec) |
| { |
| struct timespec req, rem; |
| |
| if (usec > 1000) { |
| sec += usec / 1000; |
| usec = usec % 1000; |
| } |
| req.tv_sec = sec; |
| req.tv_nsec = usec * 1000000; |
| |
| nanosleep(&req, &rem); |
| } |
| |
| /***************************************************************************** |
| * Function: ud_resources_init |
| *****************************************************************************/ |
| void |
| ud_resources_init(struct ud_resources *res) |
| { |
| res->dev_list = NULL; |
| res->ib_ctx = NULL; |
| res->send_cq = NULL; |
| res->recv_cq = NULL; |
| res->channel = NULL; |
| res->qp = NULL; |
| res->pd = NULL; |
| res->mr = NULL; |
| res->ah = NULL; |
| res->send_buf = NULL; |
| res->recv_buf = NULL; |
| } |
| |
| |
| /***************************************************************************** |
| * Function: modify_qp_to_rts |
| *****************************************************************************/ |
| static int modify_qp_to_rts(struct ibv_qp *qp) |
| { |
| struct ibv_qp_attr attr; |
| int flags; |
| int rc; |
| |
| /* RESET -> INIT */ |
| memset(&attr, 0, sizeof(struct ibv_qp_attr)); |
| |
| attr.qp_state = IBV_QPS_INIT; |
| attr.port_num = config->port_num; |
| attr.pkey_index = 0; |
| attr.qkey = UMAD_QKEY; |
| |
| flags = IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT | IBV_QP_QKEY; |
| |
| rc = ibv_modify_qp(qp, &attr, flags); |
| if (rc) { |
| pr_err("failed to modify QP state to INIT\n"); |
| return rc; |
| } |
| |
| /* INIT -> RTR */ |
| memset(&attr, 0, sizeof(attr)); |
| |
| attr.qp_state = IBV_QPS_RTR; |
| |
| flags = IBV_QP_STATE; |
| |
| rc = ibv_modify_qp(qp, &attr, flags); |
| if (rc) { |
| pr_err("failed to modify QP state to RTR\n"); |
| return rc; |
| } |
| |
| /* RTR -> RTS */ |
| /* memset(&attr, 0, sizeof(attr)); */ |
| |
| attr.qp_state = IBV_QPS_RTS; |
| attr.sq_psn = 0; |
| |
| flags = IBV_QP_STATE | IBV_QP_SQ_PSN; |
| |
| rc = ibv_modify_qp(qp, &attr, flags); |
| if (rc) { |
| pr_err("failed to modify QP state to RTS\n"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| int modify_qp_to_err(struct ibv_qp *qp) |
| { |
| static struct ibv_qp_attr attr = { |
| .qp_state = IBV_QPS_ERR, |
| }; |
| |
| return ibv_modify_qp(qp, &attr, IBV_QP_STATE); |
| } |
| |
| /***************************************************************************** |
| * Function: fill_rq_entry |
| *****************************************************************************/ |
| static int fill_rq_entry(struct ud_resources *res, int cur_receive) |
| { |
| struct ibv_recv_wr rr; |
| struct ibv_sge sg; |
| struct ibv_recv_wr *_bad_wr = NULL; |
| struct ibv_recv_wr **bad_wr = &_bad_wr; |
| int ret; |
| |
| memset(&rr, 0, sizeof(rr)); |
| |
| sg.length = RECV_BUF_SIZE; |
| sg.lkey = res->mr->lkey; |
| |
| rr.next = NULL; |
| rr.sg_list = &sg; |
| rr.num_sge = 1; |
| |
| sg.addr = (((unsigned long)res->recv_buf) + RECV_BUF_SIZE * cur_receive); |
| rr.wr_id = cur_receive; |
| |
| ret = ibv_post_recv(res->qp, &rr, bad_wr); |
| if (ret < 0) { |
| pr_err("failed to post RR\n"); |
| return ret; |
| } |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * Function: fill_rq |
| *****************************************************************************/ |
| static int fill_rq(struct ud_resources *res) |
| { |
| int cur_receive; |
| int ret; |
| |
| for (cur_receive=0; cur_receive<config->num_of_oust; ++cur_receive) { |
| ret = fill_rq_entry(res, cur_receive); |
| if (ret < 0) { |
| pr_err("failed to fill_rq_entry\n"); |
| return ret; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * Function: ud_resources_create |
| *****************************************************************************/ |
| int ud_resources_create(struct ud_resources *res) |
| { |
| struct ibv_device *ib_dev = NULL; |
| size_t size; |
| int i; |
| int cq_size; |
| int num_devices; |
| |
| /* get device names in the system */ |
| res->dev_list = ibv_get_device_list(&num_devices); |
| if (!res->dev_list) { |
| pr_err("failed to get IB devices list\n"); |
| return -1; |
| } |
| |
| for (i = 0; i < num_devices; i ++) { |
| if (!strcmp(ibv_get_device_name(res->dev_list[i]), config->dev_name)) { |
| ib_dev = res->dev_list[i]; |
| break; |
| } |
| } |
| |
| if (!ib_dev) { |
| pr_err("IB device %s wasn't found\n", config->dev_name); |
| return -ENXIO; |
| } |
| |
| pr_debug("Device %s was found\n", config->dev_name); |
| |
| /* get device handle */ |
| res->ib_ctx = ibv_open_device(ib_dev); |
| if (!res->ib_ctx) { |
| pr_err("failed to open device %s\n", config->dev_name); |
| return -ENXIO; |
| } |
| |
| res->channel = ibv_create_comp_channel(res->ib_ctx); |
| if (!res->channel) { |
| pr_err("failed to create completion channel \n"); |
| return -ENXIO; |
| } |
| |
| res->pd = ibv_alloc_pd(res->ib_ctx); |
| if (!res->pd) { |
| pr_err("ibv_alloc_pd failed\n"); |
| return -1; |
| } |
| |
| cq_size = config->num_of_oust; |
| res->recv_cq = ibv_create_cq(res->ib_ctx, cq_size, NULL, res->channel, 0); |
| if (!res->recv_cq) { |
| pr_err("failed to create CQ with %u entries\n", cq_size); |
| return -1; |
| } |
| pr_debug("CQ was created with %u CQEs\n", cq_size); |
| |
| if (ibv_req_notify_cq(res->recv_cq, 0)) { |
| pr_err("Couldn't request CQ notification\n"); |
| return -1; |
| } |
| |
| |
| res->send_cq = ibv_create_cq(res->ib_ctx, 1, NULL, NULL, 0); |
| if (!res->send_cq) { |
| pr_err("failed to create CQ with %u entries\n", 1); |
| return -1; |
| } |
| pr_debug("CQ was created with %u CQEs\n", 1); |
| |
| size = cq_size * RECV_BUF_SIZE + SEND_SIZE; |
| res->recv_buf = malloc(size); |
| if (!res->recv_buf) { |
| pr_err("failed to malloc %zu bytes to memory buffer\n", size); |
| return -ENOMEM; |
| } |
| |
| memset(res->recv_buf, 0, size); |
| |
| res->send_buf = res->recv_buf + cq_size * RECV_BUF_SIZE; |
| |
| res->mr = ibv_reg_mr(res->pd, res->recv_buf, size, IBV_ACCESS_LOCAL_WRITE); |
| if (!res->mr) { |
| pr_err("ibv_reg_mr failed\n"); |
| return -1; |
| } |
| pr_debug("MR was created with addr=%p, lkey=0x%x,\n", res->recv_buf, res->mr->lkey); |
| |
| { |
| struct ibv_qp_init_attr attr = { |
| .send_cq = res->send_cq, |
| .recv_cq = res->recv_cq, |
| .cap = { |
| .max_send_wr = 1, |
| .max_recv_wr = config->num_of_oust, |
| .max_send_sge = 1, |
| .max_recv_sge = 1 |
| }, |
| .qp_type = IBV_QPT_UD, |
| .sq_sig_all = 1, |
| }; |
| |
| res->qp = ibv_create_qp(res->pd, &attr); |
| if (!res->qp) { |
| pr_err("failed to create QP\n"); |
| return -1; |
| } |
| pr_debug("QP was created, QP number=0x%x\n", res->qp->qp_num); |
| } |
| |
| /* modify the QP to RTS (connect the QPs) */ |
| if (modify_qp_to_rts(res->qp)) { |
| pr_err("failed to modify QP state from RESET to RTS\n"); |
| return -1; |
| } |
| |
| pr_debug("QPs were modified to RTS\n"); |
| |
| if (fill_rq(res)) |
| return -1; |
| |
| res->mad_buffer = malloc(sizeof(struct umad_sa_packet)); |
| if (!res->mad_buffer) { |
| pr_err("Could not alloc mad_buffer, abort\n"); |
| return -1; |
| } |
| |
| res->mad_buffer_mutex = malloc(sizeof(pthread_mutex_t)); |
| if (!res->mad_buffer_mutex) { |
| pr_err("Could not alloc mad_buffer_mutex, abort\n"); |
| return -1; |
| } |
| |
| if (pthread_mutex_init(res->mad_buffer_mutex, NULL)) { |
| pr_err("Could not init mad_buffer_mutex, abort\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| uint16_t get_port_lid(struct ibv_context *ib_ctx, int port_num, |
| uint16_t *sm_lid) |
| { |
| struct ibv_port_attr port_attr; |
| int ret; |
| |
| ret = ibv_query_port(ib_ctx, port_num, &port_attr); |
| |
| if (!ret) { |
| if (sm_lid) |
| *sm_lid = port_attr.sm_lid; |
| return port_attr.lid; |
| } |
| |
| return 0; |
| } |
| |
| int create_ah(struct ud_resources *ud_res) |
| { |
| struct ibv_ah_attr ah_attr; |
| |
| assert(!ud_res->ah); |
| |
| /* create the UD AV */ |
| memset(&ah_attr, 0, sizeof(ah_attr)); |
| |
| if (ibv_query_port(ud_res->ib_ctx, config->port_num, &ud_res->port_attr)) { |
| pr_err("ibv_query_port on port %u failed\n", config->port_num); |
| return -1; |
| } |
| |
| ah_attr.dlid = ud_res->port_attr.sm_lid; |
| ah_attr.port_num = config->port_num; |
| |
| ud_res->ah = ibv_create_ah(ud_res->pd, &ah_attr); |
| if (!ud_res->ah) { |
| pr_err("failed to create UD AV\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * Function: ud_resources_destroy |
| *****************************************************************************/ |
| int ud_resources_destroy(struct ud_resources *res) |
| { |
| int test_result = 0; |
| |
| if (res->qp) { |
| if (ibv_destroy_qp(res->qp)) { |
| pr_err("failed to destroy QP\n"); |
| test_result = 1; |
| } |
| } |
| |
| if (res->mr) { |
| if (ibv_dereg_mr(res->mr)) { |
| pr_err("ibv_dereg_mr failed\n"); |
| test_result = 1; |
| } |
| } |
| |
| if (res->send_cq) { |
| if (ibv_destroy_cq(res->send_cq)) { |
| pr_err("ibv_destroy_cq of CQ failed\n"); |
| test_result = 1; |
| } |
| } |
| |
| if (res->recv_cq) { |
| if (ibv_destroy_cq(res->recv_cq)) { |
| pr_err("ibv_destroy_cq of CQ failed\n"); |
| test_result = 1; |
| } |
| } |
| |
| if (res->channel) { |
| if (ibv_destroy_comp_channel(res->channel)) { |
| pr_err("ibv_destroy_comp_channel failed\n"); |
| test_result = 1; |
| } |
| } |
| |
| if (res->ah) { |
| if (ibv_destroy_ah(res->ah)) { |
| pr_err("ibv_destroy_ah failed\n"); |
| test_result = 1; |
| } |
| } |
| |
| if (res->pd) { |
| if (ibv_dealloc_pd(res->pd)) { |
| pr_err("ibv_dealloc_pd failed\n"); |
| test_result = 1; |
| } |
| } |
| |
| if (res->ib_ctx) { |
| if (ibv_close_device(res->ib_ctx)) { |
| pr_err("ibv_close_device failed\n"); |
| test_result = 1; |
| } |
| } |
| |
| if (res->dev_list) |
| ibv_free_device_list(res->dev_list); |
| |
| if (res->recv_buf) |
| free(res->recv_buf); |
| |
| if (res->mad_buffer) |
| free(res->mad_buffer); |
| |
| if (res->mad_buffer_mutex) |
| free(res->mad_buffer_mutex); |
| |
| return test_result; |
| } |
| |
| static void fill_send_request(struct ud_resources *res, struct ibv_send_wr *psr, |
| struct ibv_sge *psg, struct umad_hdr *mad_hdr) |
| { |
| static int wr_id=0; |
| |
| assert(res->ah); |
| |
| memset(psr, 0, sizeof(*psr)); |
| |
| psr->next = NULL; |
| psr->wr_id = wr_id++; |
| psr->sg_list = psg; |
| psr->num_sge = 1; |
| psr->opcode = IBV_WR_SEND; |
| // psr->send_flags = IBV_SEND_SIGNALED | IBV_SEND_INLINE; |
| psr->send_flags = IBV_SEND_SIGNALED; |
| psr->wr.ud.ah = res->ah; |
| psr->wr.ud.remote_qpn = 1; |
| psr->wr.ud.remote_qkey = UMAD_QKEY; |
| |
| psg->addr = (uintptr_t) mad_hdr; |
| psg->length = SEND_SIZE; |
| psg->lkey = res->mr->lkey; |
| } |
| |
| static int stop_threads(struct sync_resources *sync_res) |
| { |
| int result; |
| |
| pthread_mutex_lock(&sync_res->retry_mutex); |
| result = sync_res->stop_threads; |
| pthread_mutex_unlock(&sync_res->retry_mutex); |
| |
| return result; |
| } |
| |
| /***************************************************************************** |
| * Function: poll_cq_once |
| * Poll a CQ once. |
| * Returns the number of completion polled (0 or 1). |
| * Returns a negative value on error. |
| *****************************************************************************/ |
| static int poll_cq_once(struct sync_resources *sync_res, struct ibv_cq *cq, |
| struct ibv_wc *wc) |
| { |
| int ret; |
| |
| ret = ibv_poll_cq(cq, 1, wc); |
| if (ret < 0) { |
| pr_err("poll CQ failed\n"); |
| return ret; |
| } |
| |
| if (ret > 0 && wc->status != IBV_WC_SUCCESS) { |
| if (!stop_threads(sync_res)) |
| pr_err("got bad completion with status: 0x%x\n", |
| wc->status); |
| return -ret; |
| } |
| |
| return ret; |
| } |
| |
| |
| static int poll_cq(struct sync_resources *sync_res, struct ibv_cq *cq, |
| struct ibv_wc *wc, struct ibv_comp_channel *channel) |
| { |
| int ret; |
| struct ibv_cq *ev_cq; |
| void *ev_ctx; |
| |
| if (channel) { |
| /* There may be extra completions that |
| * were associated to the previous event. |
| * Only poll for the first one. If there are more than one, |
| * they will be handled by later call to poll_cq */ |
| ret = poll_cq_once(sync_res, cq, wc); |
| /* return directly if there was an error or |
| * 1 completion polled */ |
| if (ret) |
| return ret; |
| |
| if (ibv_get_cq_event(channel, &ev_cq, &ev_ctx)) { |
| pr_err("Failed to get cq_event\n"); |
| return -1; |
| } |
| |
| ibv_ack_cq_events(ev_cq, 1); |
| |
| if (ev_cq != cq) { |
| pr_debug("CQ event for unknown CQ %p\n", ev_cq); |
| return -1; |
| } |
| |
| if (ibv_req_notify_cq(cq, 0)) { |
| pr_err("Couldn't request CQ notification\n"); |
| return -1; |
| } |
| |
| } |
| |
| do { |
| ret = poll_cq_once(sync_res, cq, wc); |
| if (ret < 0) |
| return ret; |
| |
| if (ret == 0) { |
| if (channel) { |
| pr_err("Weird poll returned no cqe after CQ event\n"); |
| return -1; |
| } |
| |
| if (sync_resources_error(sync_res)) |
| return -1; |
| } |
| } while (ret == 0); |
| |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * Function: register_to_trap |
| *****************************************************************************/ |
| static int register_to_trap(struct sync_resources *sync_res, |
| struct ud_resources *res, int dest_lid, |
| int trap_num, int subscribe) |
| { |
| struct ibv_send_wr sr; |
| struct ibv_wc wc; |
| struct ibv_sge sg; |
| struct ibv_send_wr *_bad_wr = NULL; |
| struct ibv_send_wr **bad_wr = &_bad_wr; |
| int counter; |
| int rc; |
| int ret; |
| long long unsigned comp_mask = 0; |
| |
| struct umad_hdr *mad_hdr = (struct umad_hdr *) (res->send_buf); |
| struct umad_sa_packet *p_sa_mad = (struct umad_sa_packet *) (res->send_buf); |
| struct ib_inform_info *data = (struct ib_inform_info *) (p_sa_mad->data); |
| static uint64_t trans_id = 0x0000FFFF; |
| |
| if (subscribe) |
| pr_debug("Registering to trap:%d (sm in %#x)\n", trap_num, dest_lid); |
| else |
| pr_debug("Deregistering from trap:%d (sm in %#x)\n", trap_num, dest_lid); |
| |
| memset(res->send_buf, 0, SEND_SIZE); |
| |
| fill_send_request(res, &sr, &sg, mad_hdr); |
| |
| umad_init_new(mad_hdr, /* Mad Header */ |
| UMAD_CLASS_SUBN_ADM, /* Management Class */ |
| UMAD_SA_CLASS_VERSION, /* Class Version */ |
| UMAD_METHOD_SET, /* Method */ |
| 0, /* Transaction ID - will be set before the send in the loop*/ |
| htobe16(UMAD_ATTR_INFORM_INFO), /* Attribute ID */ |
| 0 ); /* Attribute Modifier */ |
| |
| |
| data->lid_range_begin = htobe16(0xFFFF); |
| data->is_generic = 1; |
| data->subscribe = subscribe; |
| if (trap_num == UMAD_SM_GID_IN_SERVICE_TRAP) |
| data->trap_type = htobe16(3); /* SM */ |
| else if (trap_num == UMAD_SM_LOCAL_CHANGES_TRAP) |
| data->trap_type = htobe16(4); /* Informational */ |
| data->g_or_v.generic.trap_num = htobe16(trap_num); |
| data->g_or_v.generic.node_type_msb = 0; |
| if (trap_num == UMAD_SM_GID_IN_SERVICE_TRAP) |
| /* Class Manager */ |
| data->g_or_v.generic.node_type_lsb = htobe16(4); |
| else if (trap_num == UMAD_SM_LOCAL_CHANGES_TRAP) |
| /* Channel Adapter */ |
| data->g_or_v.generic.node_type_lsb = htobe16(1); |
| |
| comp_mask |= SRP_INFORMINFO_LID_COMP | |
| SRP_INFORMINFO_ISGENERIC_COMP | |
| SRP_INFORMINFO_SUBSCRIBE_COMP | |
| SRP_INFORMINFO_TRAPTYPE_COMP | |
| SRP_INFORMINFO_TRAPNUM_COMP | |
| SRP_INFORMINFO_PRODUCER_COMP; |
| |
| if (!data->subscribe) { |
| data->g_or_v.generic.qpn_resp_time_val = htobe32(res->qp->qp_num << 8); |
| comp_mask |= SRP_INFORMINFO_QPN_COMP; |
| } |
| |
| p_sa_mad->comp_mask = htobe64(comp_mask); |
| pr_debug("comp_mask: %llx\n", comp_mask); |
| |
| for (counter = 3, rc = 0; counter > 0 && rc == 0; counter--) { |
| pthread_mutex_lock(res->mad_buffer_mutex); |
| res->mad_buffer->mad_hdr.base_version = 0; // flag that the buffer is empty |
| pthread_mutex_unlock(res->mad_buffer_mutex); |
| mad_hdr->tid = htobe64(trans_id); |
| trans_id++; |
| |
| ret = ibv_post_send(res->qp, &sr, bad_wr); |
| if (ret) { |
| pr_err("failed to post SR\n"); |
| return ret; |
| } |
| |
| ret = poll_cq(sync_res, res->send_cq, &wc, NULL); |
| if (ret < 0) |
| return ret; |
| |
| /* sleep and check for response from SA */ |
| do { |
| srp_sleep(1, 0); |
| pthread_mutex_lock(res->mad_buffer_mutex); |
| if (res->mad_buffer->mad_hdr.base_version == 0) |
| rc = 0; |
| else if (res->mad_buffer->mad_hdr.tid == mad_hdr->tid) |
| rc = 1; |
| else { |
| res->mad_buffer->mad_hdr.base_version = 0; |
| rc = 2; |
| } |
| pthread_mutex_unlock(res->mad_buffer_mutex); |
| } while (rc == 2); // while old response. |
| } |
| |
| if (counter == 0) { |
| pr_err("No response to inform info registration\n"); |
| return -EAGAIN; |
| } |
| |
| return 0; |
| } |
| |
| |
| /***************************************************************************** |
| * Function: response_to_trap |
| *****************************************************************************/ |
| static int response_to_trap(struct sync_resources *sync_res, |
| struct ud_resources *res, |
| struct umad_sa_packet *mad_buffer) |
| { |
| struct ibv_send_wr sr; |
| struct ibv_sge sg; |
| struct ibv_send_wr *_bad_wr = NULL; |
| struct ibv_send_wr **bad_wr = &_bad_wr; |
| int ret; |
| struct ibv_wc wc; |
| |
| struct umad_sa_packet *response_buffer = (struct umad_sa_packet *) (res->send_buf); |
| |
| memcpy(response_buffer, mad_buffer, sizeof(struct umad_sa_packet)); |
| response_buffer->mad_hdr.method = UMAD_METHOD_REPORT_RESP; |
| |
| fill_send_request(res, &sr, &sg, (struct umad_hdr *) response_buffer); |
| ret = ibv_post_send(res->qp, &sr, bad_wr); |
| if (ret < 0) { |
| pr_err("failed to post response\n"); |
| return ret; |
| } |
| ret = poll_cq(sync_res, res->send_cq, &wc, NULL); |
| |
| return ret; |
| } |
| |
| |
| /***************************************************************************** |
| * Function: get_trap_notices |
| *****************************************************************************/ |
| static int get_trap_notices(struct resources *res) |
| { |
| struct ibv_wc wc; |
| int cur_receive = 0; |
| int ret = 0; |
| int pkey_index; |
| __be16 pkey; |
| char *buffer; |
| struct umad_sa_packet *mad_buffer; |
| struct ib_mad_notice_attr *notice_buffer; |
| int trap_num; |
| |
| while (!stop_threads(res->sync_res)) { |
| |
| ret = poll_cq(res->sync_res, res->ud_res->recv_cq, &wc, |
| res->ud_res->channel); |
| if (ret < 0) { |
| srp_sleep(0, 1); |
| continue; |
| } |
| |
| pr_debug("get_trap_notices: Got CQE wc.wr_id=%lld\n", (long long int) wc.wr_id); |
| cur_receive = wc.wr_id; |
| buffer = res->ud_res->recv_buf + RECV_BUF_SIZE * cur_receive; |
| mad_buffer = (struct umad_sa_packet *) (buffer + GRH_SIZE); |
| |
| if ((mad_buffer->mad_hdr.mgmt_class == UMAD_CLASS_SUBN_ADM) && |
| (mad_buffer->mad_hdr.method == UMAD_METHOD_GET_RESP) && |
| (be16toh(mad_buffer->mad_hdr.attr_id) == UMAD_ATTR_INFORM_INFO)) { |
| /* this is probably a response to register to trap */ |
| pthread_mutex_lock(res->ud_res->mad_buffer_mutex); |
| *res->ud_res->mad_buffer = *mad_buffer; |
| pthread_mutex_unlock(res->ud_res->mad_buffer_mutex); |
| } else if ((mad_buffer->mad_hdr.mgmt_class == UMAD_CLASS_SUBN_ADM) && |
| (mad_buffer->mad_hdr.method == UMAD_METHOD_REPORT) && |
| (be16toh(mad_buffer->mad_hdr.attr_id) == UMAD_ATTR_NOTICE)) |
| { /* this is a trap notice */ |
| pkey_index = wc.pkey_index; |
| ret = pkey_index_to_pkey(res->umad_res, pkey_index, &pkey); |
| if (ret) { |
| pr_err("get_trap_notices: Got Bad pkey_index (%d)\n", |
| pkey_index); |
| wake_up_main_loop(0); |
| break; |
| } |
| |
| notice_buffer = (struct ib_mad_notice_attr *) (mad_buffer->data); |
| trap_num = be16toh(notice_buffer->generic.trap_num); |
| response_to_trap(res->sync_res, res->ud_res, mad_buffer); |
| if (trap_num == UMAD_SM_GID_IN_SERVICE_TRAP) |
| push_gid_to_list(res->sync_res, |
| ¬ice_buffer->ntc_64_67.gid, |
| be16toh(pkey)); |
| else if (trap_num == UMAD_SM_LOCAL_CHANGES_TRAP) { |
| if (be32toh(notice_buffer->ntc_144.new_cap_mask) & SRP_IS_DM) |
| push_lid_to_list(res->sync_res, |
| be16toh(notice_buffer->ntc_144.lid), |
| be16toh(pkey)); |
| } else { |
| pr_err("Unhandled trap_num %d\n", trap_num); |
| } |
| } |
| |
| ret = fill_rq_entry(res->ud_res, cur_receive); |
| if (ret < 0) { |
| wake_up_main_loop(0); |
| break; |
| } |
| } |
| return ret; |
| } |
| |
| void *run_thread_get_trap_notices(void *res_in) |
| { |
| int ret; |
| |
| ret = get_trap_notices((struct resources *)res_in); |
| |
| pr_debug("get_trap_notices thread ended\n"); |
| |
| pthread_exit((void *)(long)ret); |
| } |
| |
| |
| /***************************************************************************** |
| * Function: register_to_traps |
| *****************************************************************************/ |
| int register_to_traps(struct resources *res, int subscribe) |
| { |
| int rc; |
| int trap_numbers[] = {UMAD_SM_GID_IN_SERVICE_TRAP, UMAD_SM_LOCAL_CHANGES_TRAP}; |
| int i; |
| |
| for (i=0; i < sizeof(trap_numbers) / sizeof(*trap_numbers); ++i) { |
| rc = register_to_trap(res->sync_res, res->ud_res, |
| res->ud_res->port_attr.sm_lid, |
| trap_numbers[i], subscribe); |
| if (rc != 0) |
| return rc; |
| } |
| |
| return 0; |
| |
| } |
| |
| void *run_thread_listen_to_events(void *res_in) |
| { |
| struct resources *res = (struct resources *)res_in; |
| struct ibv_async_event event; |
| |
| while (!stop_threads(res->sync_res)) { |
| if (ibv_get_async_event(res->ud_res->ib_ctx, &event)) { |
| if (errno != EINTR) |
| pr_err("ibv_get_async_event failed (errno = %d)\n", |
| errno); |
| break; |
| } |
| |
| pr_debug("event_type %d, port %d\n", |
| event.event_type, event.element.port_num); |
| |
| switch (event.event_type) { |
| case IBV_EVENT_PORT_ACTIVE: |
| case IBV_EVENT_SM_CHANGE: |
| case IBV_EVENT_LID_CHANGE: |
| case IBV_EVENT_CLIENT_REREGISTER: |
| case IBV_EVENT_PKEY_CHANGE: |
| if (event.element.port_num == config->port_num) { |
| pthread_mutex_lock(&res->sync_res->mutex); |
| __schedule_rescan(res->sync_res, 0); |
| wake_up_main_loop(0); |
| pthread_mutex_unlock(&res->sync_res->mutex); |
| } |
| break; |
| |
| case IBV_EVENT_DEVICE_FATAL: |
| case IBV_EVENT_CQ_ERR: |
| case IBV_EVENT_QP_FATAL: |
| /* clean and restart */ |
| pr_err("Critical event %d, raising catastrophic " |
| "error signal\n", event.event_type); |
| raise_catastrophic_error(res->sync_res); |
| break; |
| |
| /* |
| |
| case IBV_EVENT_PORT_ERR: |
| case IBV_EVENT_QP_REQ_ERR: |
| case IBV_EVENT_QP_ACCESS_ERR: |
| case IBV_EVENT_COMM_EST: |
| case IBV_EVENT_SQ_DRAINED: |
| case IBV_EVENT_PATH_MIG: |
| case IBV_EVENT_PATH_MIG_ERR: |
| case IBV_EVENT_SRQ_ERR: |
| case IBV_EVENT_SRQ_LIMIT_REACHED: |
| case IBV_EVENT_QP_LAST_WQE_REACHED: |
| |
| */ |
| |
| |
| default: |
| break; |
| } |
| |
| ibv_ack_async_event(&event); |
| |
| } |
| |
| return NULL; |
| } |
| |