dapl_ep_connect.c revision 9517:b4839b0aa7a4
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2002-2003, Network Appliance, Inc. All rights reserved.
24 */
25
26/*
27 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
28 * Use is subject to license terms.
29 */
30
31/*
32 *
33 * MODULE: dapl_ep_connect.c
34 *
35 * PURPOSE: Endpoint management
36 * Description: Interfaces in this file are completely described in
37 *		the DAPL 1.1 API, Chapter 6, section 5
38 *
39 * $Id: dapl_ep_connect.c,v 1.23 2003/07/31 13:55:18 hobie16 Exp $
40 */
41
42#include "dapl.h"
43#include "dapl_ep_util.h"
44#include "dapl_adapter_util.h"
45#include "dapl_evd_util.h"
46
47/*
48 * dapl_ep_connect
49 *
50 * DAPL Requirements Version xxx, 6.5.7
51 *
52 * Request a connection be established between the local Endpoint
53 * and a remote Endpoint. This operation is used by the active/client
54 * side of a connection
55 *
56 * Input:
57 *	ep_handle
58 *	remote_ia_address
59 *	remote_conn_qual
60 *	timeout
61 *	private_data_size
62 *	privaet_data
63 *	qos
64 *	connect_flags
65 *
66 * Output:
67 *	None
68 *
69 * Returns:
70 *	DAT_SUCCESS
71 *	DAT_INSUFFICIENT_RESOUCRES
72 *	DAT_INVALID_PARAMETER
73 *	DAT_MODLE_NOT_SUPPORTED
74 */
75DAT_RETURN
76dapl_ep_connect(
77	IN DAT_EP_HANDLE ep_handle,
78	IN DAT_IA_ADDRESS_PTR remote_ia_address,
79	IN DAT_CONN_QUAL remote_conn_qual,
80	IN DAT_TIMEOUT timeout,
81	IN DAT_COUNT private_data_size,
82	IN const DAT_PVOID private_data,
83	IN DAT_QOS qos,
84	IN DAT_CONNECT_FLAGS connect_flags)
85{
86	DAPL_EP *ep_ptr;
87	DAPL_PRIVATE prd;
88	DAPL_EP	alloc_ep;
89	DAT_RETURN dat_status;
90
91	dapl_dbg_log(DAPL_DBG_TYPE_API | DAPL_DBG_TYPE_CM,
92	    "dapl_ep_connect (%p, {%u.%u.%u.%u}, %X, %d, %d, %p, %x, %x)\n",
93	    ep_handle,
94	    remote_ia_address->sa_data[2],
95	    remote_ia_address->sa_data[3],
96	    remote_ia_address->sa_data[4],
97	    remote_ia_address->sa_data[5],
98	    remote_conn_qual,
99	    timeout,
100	    private_data_size,
101	    private_data,
102	    qos,
103	    connect_flags);
104
105	dat_status = DAT_SUCCESS;
106	ep_ptr = (DAPL_EP *) ep_handle;
107
108	/*
109	 * Verify parameter & state. The connection handle must be good
110	 * at this point.
111	 */
112	if (DAPL_BAD_HANDLE(ep_ptr, DAPL_MAGIC_EP)) {
113		dat_status = DAT_ERROR(DAT_INVALID_HANDLE,
114		    DAT_INVALID_HANDLE_EP);
115		goto bail;
116	}
117
118	if (DAPL_BAD_HANDLE(ep_ptr->param.connect_evd_handle, DAPL_MAGIC_EVD)) {
119		dat_status = DAT_ERROR(DAT_INVALID_HANDLE,
120		    DAT_INVALID_HANDLE_EVD_CONN);
121		goto bail;
122	}
123
124	/*
125	 * If the endpoint needs a QP, associated the QP with it.
126	 * This needs to be done carefully, in order to:
127	 *	* Avoid allocating under a lock.
128	 *  * Not step on data structures being altered by
129	 *    routines with which we are racing.
130	 * So we:
131	 *  * Confirm that a new QP is needed and is not forbidden by the
132	 *    current state.
133	 *  * Allocate it into a separate EP.
134	 *  * Take the EP lock.
135	 *  * Reconfirm that the EP is in a state where it needs a QP.
136	 *  * Assign the QP and release the lock.
137	 */
138	if (ep_ptr->qp_state == DAPL_QP_STATE_UNATTACHED) {
139		if (ep_ptr->param.pz_handle == NULL ||
140		    DAPL_BAD_HANDLE(ep_ptr->param.pz_handle, DAPL_MAGIC_PZ)) {
141			dat_status = DAT_ERROR(DAT_INVALID_STATE,
142			    DAT_INVALID_STATE_EP_NOTREADY);
143			goto bail;
144		}
145		alloc_ep = *ep_ptr;
146
147		dat_status = dapls_ib_qp_alloc(ep_ptr->header.owner_ia,
148		    &alloc_ep, ep_ptr);
149		if (dat_status != DAT_SUCCESS) {
150			dat_status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES,
151			    DAT_RESOURCE_MEMORY);
152			goto bail;
153		}
154
155		dapl_os_lock(&ep_ptr->header.lock);
156		/*
157		 * PZ shouldn't have changed since we're only racing with
158		 * dapl_cr_accept()
159		 */
160		if (ep_ptr->qp_state != DAPL_QP_STATE_UNATTACHED) {
161			/* Bail, cleaning up.  */
162			dapl_os_unlock(&ep_ptr->header.lock);
163			dat_status = dapls_ib_qp_free(ep_ptr->header.owner_ia,
164			    &alloc_ep);
165			if (dat_status != DAT_SUCCESS) {
166				dapl_dbg_log(DAPL_DBG_TYPE_WARN,
167				    "ep_connect: ib_qp_free failed with %x\n",
168				    dat_status);
169			}
170			dat_status = DAT_ERROR(DAT_INVALID_STATE,
171			    dapls_ep_state_subtype(ep_ptr));
172			goto bail;
173		}
174		ep_ptr->qp_handle = alloc_ep.qp_handle;
175		ep_ptr->qpn = alloc_ep.qpn;
176		ep_ptr->qp_state = alloc_ep.qp_state;
177
178		dapl_os_unlock(&ep_ptr->header.lock);
179	}
180
181	/*
182	 * We do state checks and transitions under lock.
183	 * The only code we're racing against is dapl_cr_accept.
184	 */
185	dapl_os_lock(&ep_ptr->header.lock);
186
187	/*
188	 * Verify the attributes of the EP handle before we connect it. Test
189	 * all of the handles to make sure they are currently valid.
190	 * Specifically:
191	 *   pz_handle		required
192	 *   recv_evd_handle	optional, but must be valid
193	 *   request_evd_handle	optional, but must be valid
194	 *   connect_evd_handle	required
195	 */
196	if (ep_ptr->param.pz_handle == NULL ||
197	    DAPL_BAD_HANDLE(ep_ptr->param.pz_handle, DAPL_MAGIC_PZ) ||
198	    ep_ptr->param.connect_evd_handle == NULL ||
199	    DAPL_BAD_HANDLE(ep_ptr->param.connect_evd_handle,
200	    DAPL_MAGIC_EVD) ||
201	    !(((DAPL_EVD *)ep_ptr->param.connect_evd_handle)->evd_flags &
202	    DAT_EVD_CONNECTION_FLAG) ||
203	    (ep_ptr->param.recv_evd_handle != DAT_HANDLE_NULL &&
204	    (DAPL_BAD_HANDLE(ep_ptr->param.recv_evd_handle,
205	    DAPL_MAGIC_EVD))) ||
206	    (ep_ptr->param.request_evd_handle != DAT_HANDLE_NULL &&
207	    (DAPL_BAD_HANDLE(ep_ptr->param.request_evd_handle,
208	    DAPL_MAGIC_EVD)))) {
209		dapl_os_unlock(&ep_ptr->header.lock);
210		dat_status = DAT_ERROR(DAT_INVALID_STATE,
211		    DAT_INVALID_STATE_EP_NOTREADY);
212		goto bail;
213	}
214
215	/*
216	 * Check both the EP state and the QP state: if we don't have a QP
217	 *  we need to attach one now.
218	 */
219	if (ep_ptr->qp_state == DAPL_QP_STATE_UNATTACHED) {
220		dat_status = dapls_ib_qp_alloc(ep_ptr->header.owner_ia,
221		    ep_ptr, ep_ptr);
222
223		if (dat_status != DAT_SUCCESS) {
224			dapl_os_unlock(&ep_ptr->header.lock);
225			dat_status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES,
226			    DAT_RESOURCE_TEP);
227			goto bail;
228		}
229	}
230
231	if (ep_ptr->param.ep_state != DAT_EP_STATE_UNCONNECTED) {
232		dapl_os_unlock(&ep_ptr->header.lock);
233		dat_status = DAT_ERROR(DAT_INVALID_STATE,
234		    dapls_ep_state_subtype(ep_ptr));
235		goto bail;
236	}
237
238	if (qos != DAT_QOS_BEST_EFFORT ||
239	    connect_flags != DAT_CONNECT_DEFAULT_FLAG) {
240		/*
241		 * At this point we only support one QOS level
242		 */
243		dapl_os_unlock(&ep_ptr->header.lock);
244		dat_status = DAT_ERROR(DAT_MODEL_NOT_SUPPORTED, 0);
245		goto bail;
246	}
247
248	/*
249	 * Verify the private data size doesn't exceed the max
250	 */
251	if (private_data_size > DAPL_CONSUMER_MAX_PRIVATE_DATA_SIZE) {
252		dapl_os_unlock(&ep_ptr->header.lock);
253		dat_status = DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG5);
254		goto bail;
255	}
256
257	/*
258	 * transition the state before requesting a connection to avoid
259	 * race conditions
260	 */
261	ep_ptr->param.ep_state = DAT_EP_STATE_ACTIVE_CONNECTION_PENDING;
262
263	/*
264	 * At this point we're committed, and done with the endpoint
265	 * except for the connect, so we can drop the lock.
266	 */
267	dapl_os_unlock(&ep_ptr->header.lock);
268
269	/*
270	 * fill in the private data
271	 */
272	(void) dapl_os_memzero(&prd, sizeof (DAPL_PRIVATE));
273	if (private_data_size > 0)
274		(void) dapl_os_memcpy(prd.private_data, private_data,
275		    private_data_size);
276
277	/* Copy the connection qualifiers */
278	(void) dapl_os_memcpy(ep_ptr->param.remote_ia_address_ptr,
279	    remote_ia_address, sizeof (DAT_SOCK_ADDR6));
280	ep_ptr->param.remote_port_qual = remote_conn_qual;
281
282	dat_status = dapls_ib_connect(ep_handle,
283	    remote_ia_address, remote_conn_qual,
284	    private_data_size, &prd, timeout);
285
286	if (dat_status != DAT_SUCCESS) {
287		DAPL_EVD	*evd_ptr;
288
289		if (dat_status == DAT_ERROR(DAT_INVALID_ADDRESS,
290		    DAT_INVALID_ADDRESS_UNREACHABLE)) {
291			/* Unreachable IP address */
292			evd_ptr = (DAPL_EVD *)ep_ptr->param.connect_evd_handle;
293			if (evd_ptr != NULL) {
294				(void) dapls_evd_post_connection_event(evd_ptr,
295				    DAT_CONNECTION_EVENT_UNREACHABLE,
296				    (DAT_HANDLE) ep_ptr, 0, 0);
297			}
298			ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED;
299			dat_status = DAT_SUCCESS;
300		} else if (dat_status == DAT_ERROR(DAT_INVALID_PARAMETER,
301		    DAT_INVALID_ADDRESS_UNREACHABLE)) {
302			/* Non-existant connection qualifier */
303			evd_ptr = (DAPL_EVD *)ep_ptr->param.connect_evd_handle;
304			if (evd_ptr != NULL) {
305				(void) dapls_evd_post_connection_event(evd_ptr,
306				    DAT_CONNECTION_EVENT_NON_PEER_REJECTED,
307				    (DAT_HANDLE) ep_ptr, 0, 0);
308			}
309			ep_ptr->param.ep_state = DAT_EP_STATE_DISCONNECTED;
310			dat_status = DAT_SUCCESS;
311		} else {
312			ep_ptr->param.ep_state = DAT_EP_STATE_UNCONNECTED;
313		}
314	}
315
316bail:
317	dapl_dbg_log(DAPL_DBG_TYPE_RTN | DAPL_DBG_TYPE_CM,
318	    "dapl_ep_connect () returns 0x%x\n", dat_status);
319
320	return (dat_status);
321}
322