static int __gnix_vc_hndl_conn_req()

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;
}