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  */
75 DAT_RETURN
dapl_ep_connect(IN DAT_EP_HANDLE ep_handle,IN DAT_IA_ADDRESS_PTR remote_ia_address,IN DAT_CONN_QUAL remote_conn_qual,IN DAT_TIMEOUT timeout,IN DAT_COUNT private_data_size,IN const DAT_PVOID private_data,IN DAT_QOS qos,IN DAT_CONNECT_FLAGS connect_flags)76 dapl_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 
316 bail:
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