/** * Copyright (C) Mellanox Technologies Ltd. 2001-2019. ALL RIGHTS RESERVED. * Copyright (c) 2007-2009 Cisco Systems, Inc. All rights reserved. * Copyright (c) 2009 IBM Corporation. All rights reserved. * * See file LICENSE for terms. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "cm.h" #include #include #include typedef struct uct_cm_iov { uct_pack_callback_t pack; const void *arg; size_t length; } uct_cm_iov_t; static UCS_CLASS_INIT_FUNC(uct_cm_ep_t, const uct_ep_params_t *params) { uct_cm_iface_t *iface = ucs_derived_of(params->iface, uct_cm_iface_t); UCT_EP_PARAMS_CHECK_DEV_IFACE_ADDRS(params); UCS_CLASS_CALL_SUPER_INIT(uct_base_ep_t, &iface->super.super); uct_ib_address_unpack((const uct_ib_address_t*)params->dev_addr, &self->dlid, &self->dgid); self->dest_service_id = *(const uint32_t*)params->iface_addr; return UCS_OK; } static UCS_CLASS_CLEANUP_FUNC(uct_cm_ep_t) { ucs_trace_func(""); } UCS_CLASS_DEFINE(uct_cm_ep_t, uct_base_ep_t); UCS_CLASS_DEFINE_NEW_FUNC(uct_cm_ep_t, uct_ep_t, const uct_ep_params_t *); UCS_CLASS_DEFINE_DELETE_FUNC(uct_cm_ep_t, uct_ep_t); static ucs_status_t uct_cm_ep_fill_path_rec(uct_cm_ep_t *ep, struct ibv_sa_path_rec *path) { uct_cm_iface_t *iface = ucs_derived_of(ep->super.super.iface, uct_cm_iface_t); path->sgid = iface->super.gid; path->dlid = htons(ep->dlid); path->slid = htons(uct_ib_iface_port_attr(&iface->super)->lid); if (iface->super.config.force_global_addr) { ucs_assert_always(ep->dgid.global.interface_id != 0); path->dgid = ep->dgid; path->hop_limit = iface->super.config.hop_limit; } else { memset(&path->dgid, 0, sizeof(path->dgid)); path->hop_limit = 0; } path->raw_traffic = 0; /* IB traffic */ path->flow_label = 0; path->traffic_class = iface->super.config.traffic_class; path->reversible = htonl(1); /* IBCM currently only supports reversible paths */ path->numb_path = 0; path->pkey = ntohs(iface->super.pkey_value); path->sl = iface->super.config.sl; path->mtu_selector = 2; /* EQ */ path->mtu = uct_ib_iface_port_attr(&iface->super)->active_mtu; path->rate_selector = 2; /* EQ */ path->rate = IBV_RATE_MAX; path->packet_life_time_selector = 2; /* EQ */ path->packet_life_time = 0; path->preference = 0; /* Use first path */ return UCS_OK; } static void uct_cm_dump_path(struct ibv_sa_path_rec *path) { char sgid_buf[256]; char dgid_buf[256]; inet_ntop(AF_INET6, &path->dgid, dgid_buf, sizeof(dgid_buf)); inet_ntop(AF_INET6, &path->sgid, sgid_buf, sizeof(sgid_buf)); ucs_trace_data("slid %d sgid %s dlid %d dgid %s", ntohs(path->slid), sgid_buf, ntohs(path->dlid), dgid_buf); ucs_trace_data("traffic %d flow_label %d hop %d class %d revers. 0x%x " "numb %d pkey 0x%x sl %d", path->raw_traffic, path->flow_label, path->hop_limit, path->traffic_class, path->reversible, path->numb_path, path->pkey, path->sl); ucs_trace_data("mtu %d(%d) rate %d(%d) lifetime %d(%d) pref %d", path->mtu, path->mtu_selector, path->rate, path->rate_selector, path->packet_life_time, path->packet_life_time_selector, path->preference); } ssize_t uct_cm_ep_am_bcopy(uct_ep_h tl_ep, uint8_t am_id, uct_pack_callback_t pack_cb, void *arg, unsigned flags) { uct_cm_iface_t *iface = ucs_derived_of(tl_ep->iface, uct_cm_iface_t); uct_cm_ep_t *ep = ucs_derived_of(tl_ep, uct_cm_ep_t); struct ib_cm_sidr_req_param req; struct ibv_sa_path_rec path; uct_cm_iface_op_t *op; ucs_status_t status; uct_cm_hdr_t *hdr; size_t payload_len; size_t total_len; int ret; UCT_CHECK_AM_ID(am_id); uct_cm_enter(iface); if (!uct_cm_iface_has_tx_resources(iface)) { status = UCS_ERR_NO_RESOURCE; goto err; } /* Allocate temporary contiguous buffer */ hdr = ucs_malloc(IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE, "cm_send_buf"); if (hdr == NULL) { status = UCS_ERR_NO_MEMORY; goto err; } payload_len = pack_cb(hdr + 1, arg); hdr->am_id = am_id; hdr->length = payload_len; total_len = sizeof(*hdr) + payload_len; status = uct_cm_ep_fill_path_rec(ep, &path); if (status != UCS_OK) { goto err_free_hdr; } /* Fill SIDR request */ memset(&req, 0, sizeof req); req.path = &path; req.service_id = ep->dest_service_id; req.timeout_ms = iface->config.timeout_ms; req.private_data = hdr; req.private_data_len = total_len; req.max_cm_retries = iface->config.retry_count; op = ucs_malloc(sizeof *op, "cm_op"); if (op == NULL) { status = UCS_ERR_NO_MEMORY; goto err_free_hdr; } op->is_id = 1; /* Create temporary ID for this message. Will be released when getting REP. */ ret = ib_cm_create_id(iface->cmdev, &op->id, NULL); if (ret) { ucs_error("ib_cm_create_id() failed: %m"); status = UCS_ERR_IO_ERROR; goto err_free_op; } uct_cm_dump_path(&path); ret = ib_cm_send_sidr_req(op->id, &req); if (ret) { ucs_error("ib_cm_send_sidr_req() failed: %m"); status = UCS_ERR_IO_ERROR; goto err_destroy_id; } ucs_queue_push(&iface->outstanding_q, &op->queue); ++iface->num_outstanding; ucs_trace("outs=%d", iface->num_outstanding); UCT_TL_EP_STAT_OP(&ep->super, AM, BCOPY, payload_len); uct_cm_iface_trace_data(iface, UCT_AM_TRACE_TYPE_SEND, hdr, "TX: SIDR_REQ [id %p{%u} dlid %d svc 0x%"PRIx64"]", op->id, op->id->handle, ntohs(path.dlid), (uint64_t)req.service_id); uct_cm_leave(iface); ucs_free(hdr); /* coverity[missing_unlock] */ return payload_len; err_destroy_id: ib_cm_destroy_id(op->id); err_free_op: ucs_free(op); err_free_hdr: ucs_free(hdr); err: uct_cm_leave(iface); return status; } ucs_status_t uct_cm_ep_pending_add(uct_ep_h tl_ep, uct_pending_req_t *req, unsigned flags) { uct_cm_iface_t *iface = ucs_derived_of(tl_ep->iface, uct_cm_iface_t); ucs_status_t status; uct_cm_enter(iface); if (iface->num_outstanding < iface->config.max_outstanding) { status = UCS_ERR_BUSY; } else { uct_cm_pending_req_priv(req)->ep = ucs_derived_of(tl_ep, uct_cm_ep_t); uct_pending_req_queue_push(&iface->notify_q, req); status = UCS_OK; UCT_TL_EP_STAT_PEND(ucs_derived_of(tl_ep, uct_base_ep_t)); } uct_cm_leave(iface); return status; } void uct_cm_ep_pending_purge(uct_ep_h tl_ep, uct_pending_purge_callback_t cb, void *arg) { uct_cm_iface_t *iface = ucs_derived_of(tl_ep->iface, uct_cm_iface_t); uct_cm_ep_t *ep = ucs_derived_of(tl_ep, uct_cm_ep_t); uct_cm_pending_req_priv_t *priv; uct_cm_enter(iface); uct_pending_queue_purge(priv, &iface->notify_q, priv->ep == ep, cb, arg); uct_cm_leave(iface); } ucs_status_t uct_cm_ep_flush(uct_ep_h tl_ep, unsigned flags, uct_completion_t *comp) { uct_cm_iface_t *iface = ucs_derived_of(tl_ep->iface, uct_cm_iface_t); ucs_status_t status; uct_cm_enter(iface); if (!uct_cm_iface_has_tx_resources(iface)) { status = UCS_ERR_NO_RESOURCE; } else { status = uct_cm_iface_flush_do(iface, comp); if (status == UCS_OK) { UCT_TL_EP_STAT_FLUSH(ucs_derived_of(tl_ep, uct_base_ep_t)); } else if (status == UCS_INPROGRESS) { UCT_TL_EP_STAT_FLUSH_WAIT(ucs_derived_of(tl_ep, uct_base_ep_t)); } } uct_cm_leave(iface); return status; }