in prov/gni/src/gnix_vc.c [833:1087]
static int __gnix_vc_hndl_conn_req(struct gnix_cm_nic *cm_nic,
char *msg_buffer,
struct gnix_address src_cm_nic_addr)
{
int ret = FI_SUCCESS;
gni_return_t __attribute__((unused)) status;
struct gnix_fid_ep *ep = NULL;
gnix_ht_key_t key;
struct gnix_av_addr_entry entry;
struct gnix_address src_addr, target_addr;
struct gnix_vc *vc = NULL;
struct gnix_work_req *work_req;
int src_vc_id;
gni_smsg_attr_t src_smsg_attr;
uint64_t src_vc_ptr;
uint64_t peer_caps;
struct wq_hndl_conn_req *data = NULL;
gni_mem_handle_t tmp_mem_hndl;
int src_mapped = 0;
fi_addr_t fi_addr;
xpmem_segid_t peer_segid;
xpmem_apid_t peer_apid;
uint8_t name_type, rx_ctx_cnt;
bool accessible;
ssize_t __attribute__((unused)) len;
struct gnix_ep_name *error_data;
uint32_t key_offset;
GNIX_TRACE(FI_LOG_EP_CTRL, "\n");
/*
* unpack the message
*/
__gnix_vc_unpack_conn_req(msg_buffer,
&target_addr,
&src_addr,
&src_vc_id,
&src_vc_ptr,
&src_smsg_attr,
&tmp_mem_hndl,
&peer_caps,
&peer_segid,
&name_type,
&rx_ctx_cnt,
&key_offset);
GNIX_DEBUG(FI_LOG_EP_CTRL,
"conn req rx: (From Aries addr 0x%x Id %d to Aries 0x%x Id %d src vc 0x%lx )\n",
src_addr.device_addr,
src_addr.cdm_id,
target_addr.device_addr,
target_addr.cdm_id,
src_vc_ptr);
/*
* lookup the ep from the addr_to_ep_ht using the target_addr
* in the datagram
*/
__gnix_vc_set_ht_key(&target_addr, &key);
ep = (struct gnix_fid_ep *)_gnix_ht_lookup(cm_nic->addr_to_ep_ht,
key);
if (ep == NULL) {
GNIX_WARN(FI_LOG_EP_DATA,
"_gnix_ht_lookup addr_to_ep failed\n");
return -FI_ENOENT;
}
/*
* look to see if there is a VC already for the
* address of the connecting EP.
*/
COND_ACQUIRE(ep->requires_lock, &ep->vc_lock);
/* If we already have an AV bound, see if sender's address is already
* mapped. */
if (ep->av) {
ret = _gnix_av_reverse_lookup(ep->av, src_addr, &fi_addr);
if (ret == FI_SUCCESS) {
src_mapped = 1;
vc = _gnix_ep_vc_lookup(ep, fi_addr);
}
}
/*
* if there is no corresponding vc in the hash,
* or there is an entry and it's not in connecting state
* go down the conn req ack route.
*/
if ((vc == NULL) ||
(vc->conn_state == GNIX_VC_CONN_NONE)) {
if (vc == NULL) {
entry.gnix_addr = src_addr;
entry.cm_nic_cdm_id = src_cm_nic_addr.cdm_id;
ret = _gnix_vc_alloc(ep,
&entry,
&vc);
if (ret != FI_SUCCESS) {
GNIX_WARN(FI_LOG_EP_CTRL,
"_gnix_vc_alloc returned %s\n",
fi_strerror(-ret));
goto err;
}
vc->conn_state = GNIX_VC_CONNECTING;
vc->peer_key_offset = key_offset;
if (src_mapped) {
/* We have an AV which maps the incoming
* address. Store the new VC in our VC lookup
* table. */
ret = _gnix_ep_vc_store(ep, vc, fi_addr);
if (OFI_UNLIKELY(ret != FI_SUCCESS)) {
_gnix_vc_destroy(vc);
GNIX_WARN(FI_LOG_EP_DATA,
"_gnix_ep_vc_store returned %s\n",
fi_strerror(-ret));
goto err;
}
} else {
/* We lack an AV and/or the entry to map the
* incoming address. Keep VC in special table
* until it is mapped for a TX operation. */
GNIX_INFO(FI_LOG_EP_CTRL,
"Received conn. request from unmapped peer EP, vc: %p addr: 0x%lx\n",
vc, src_addr);
dlist_insert_tail(&vc->list, &ep->unmapped_vcs);
/*
* see issue 4521 for the error_data size allocated
*/
if (vc->ep->caps & FI_SOURCE) {
error_data =
calloc(1, GNIX_CQ_MAX_ERR_DATA_SIZE);
if (error_data == NULL) {
ret = -FI_ENOMEM;
goto err;
}
vc->gnix_ep_name = (void *) error_data;
error_data->gnix_addr = src_addr;
error_data->name_type = name_type;
error_data->cm_nic_cdm_id =
cm_nic->my_name.cm_nic_cdm_id;
error_data->cookie =
cm_nic->my_name.cookie;
error_data->rx_ctx_cnt = rx_ctx_cnt;
}
}
} else {
vc->conn_state = GNIX_VC_CONNECTING;
}
vc->peer_caps = peer_caps;
vc->peer_key_offset = key_offset;
/*
* prepare a work request to
* initiate an request response
*/
work_req = calloc(1, sizeof(*work_req));
if (work_req == NULL) {
ret = -FI_ENOMEM;
goto err;
}
data = calloc(1, sizeof(struct wq_hndl_conn_req));
if (data == NULL) {
ret = -FI_ENOMEM;
goto err;
}
memcpy(&data->src_smsg_attr,
&src_smsg_attr,
sizeof(src_smsg_attr));
data->vc = vc;
data->src_vc_id = src_vc_id;
data->src_vc_ptr = src_vc_ptr;
data->irq_mem_hndl = tmp_mem_hndl;
data->peer_segid = peer_segid;
work_req->progress_fn = __gnix_vc_conn_ack_prog_fn;
work_req->data = data;
work_req->completer_fn = __gnix_vc_conn_ack_comp_fn;
work_req->completer_data = data;
/*
* add the work request to the tail of the
* cm_nic's work queue, progress the cm_nic.
*/
fastlock_acquire(&cm_nic->wq_lock);
dlist_insert_before(&work_req->list, &cm_nic->cm_nic_wq);
fastlock_release(&cm_nic->wq_lock);
} else {
/*
* we can only be in connecting state if we
* reach here. We have all the informatinon,
* and the other side will get the information
* at some point, so go ahead and build SMSG connection.
*/
if (vc->conn_state != GNIX_VC_CONNECTING) {
GNIX_WARN(FI_LOG_EP_CTRL,
"vc %p not in connecting state nor in cm wq\n",
vc, vc->conn_state);
ret = -FI_EINVAL;
goto err;
}
ret = _gnix_vc_smsg_init(vc, src_vc_id,
&src_smsg_attr,
&tmp_mem_hndl);
if (ret != FI_SUCCESS) {
GNIX_WARN(FI_LOG_EP_CTRL,
"_gnix_vc_smsg_init returned %s\n",
fi_strerror(-ret));
goto err;
}
ret = _gnix_xpmem_accessible(ep, src_cm_nic_addr, &accessible);
if ((ret == FI_SUCCESS) && (accessible == true)) {
ret = _gnix_xpmem_get_apid(ep->xpmem_hndl,
peer_segid,
&peer_apid);
if (ret == FI_SUCCESS) {
vc->modes |= GNIX_VC_MODE_XPMEM;
vc->peer_apid = peer_apid;
}
}
vc->peer_caps = peer_caps;
vc->peer_key_offset = key_offset;
vc->peer_id = src_vc_id;
vc->conn_state = GNIX_VC_CONNECTED;
GNIX_DEBUG(FI_LOG_EP_CTRL, "moving vc %p state to connected\n",
vc);
ret = _gnix_vc_sched_new_conn(vc);
if (ret != FI_SUCCESS)
GNIX_WARN(FI_LOG_EP_DATA,
"_gnix_vc_sched_new_conn returned %s\n",
fi_strerror(-ret));
}
err:
COND_RELEASE(ep->requires_lock, &ep->vc_lock);
return ret;
}