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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright (c) 2016 by Delphix. All rights reserved.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/ddi.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <sys/sunddi.h>
33 #include <sys/ib/ibtl/ibti.h>
34 #include <sys/ib/ibtl/ibtl_types.h>
35 
36 #include <sys/ib/clients/iser/iser.h>
37 
38 extern idm_transport_ops_t	iser_transport_ops;
39 
40 /*
41  * iser_cm.c
42  *    InfiniBand Communication Manager routines for iSER
43  */
44 static ibt_cm_status_t iser_ib_handle_cm_req(idm_svc_t *svc_hdl,
45     ibt_cm_event_t *evp, ibt_cm_return_args_t *rargsp, void *rcmp,
46     ibt_priv_data_len_t rcmp_len);
47 
48 static ibt_cm_status_t iser_ib_handle_cm_rep(iser_state_t *statep,
49     ibt_cm_event_t *evp, ibt_cm_return_args_t *rargsp, void *rcmp,
50     ibt_priv_data_len_t rcmp_len);
51 
52 static ibt_cm_status_t iser_handle_cm_conn_est(ibt_cm_event_t *evp);
53 static ibt_cm_status_t iser_handle_cm_conn_closed(ibt_cm_event_t *evp);
54 static ibt_cm_status_t iser_handle_cm_event_failure(ibt_cm_event_t *evp);
55 
56 /*
57  * iser_ib_cm_handler()
58  */
59 ibt_cm_status_t
60 iser_ib_cm_handler(void *cm_private, ibt_cm_event_t *eventp,
61     ibt_cm_return_args_t *ret_args, void *ret_priv_data,
62     ibt_priv_data_len_t ret_len_max)
63 {
64 	ibt_cm_status_t	ret = IBT_CM_REJECT;
65 
66 	switch (eventp->cm_type) {
67 
68 	case IBT_CM_EVENT_REQ_RCV:
69 		ISER_LOG(CE_NOTE, "iser_ib_cm_handler: IBT_CM_EVENT_REQ_RCV");
70 		ret = iser_ib_handle_cm_req((idm_svc_t *)cm_private, eventp,
71 		    ret_args, ret_priv_data, ret_len_max);
72 		break;
73 
74 	case IBT_CM_EVENT_REP_RCV:
75 		ISER_LOG(CE_NOTE, "iser_ib_cm_handler: IBT_CM_EVENT_REP_RCV");
76 		ret = iser_ib_handle_cm_rep((iser_state_t *)cm_private,
77 		    eventp, ret_args, ret_priv_data, ret_len_max);
78 		break;
79 
80 	case IBT_CM_EVENT_CONN_EST:
81 		ISER_LOG(CE_NOTE, "iser_ib_cm_handler: IBT_CM_EVENT_CONN_EST");
82 		ret = iser_handle_cm_conn_est(eventp);
83 		break;
84 
85 	case IBT_CM_EVENT_CONN_CLOSED:
86 		ISER_LOG(CE_NOTE, "iser_ib_cm_handler: "
87 		    "IBT_CM_EVENT_CONN_CLOSED");
88 		ret = iser_handle_cm_conn_closed(eventp);
89 		break;
90 
91 	case IBT_CM_EVENT_FAILURE:
92 		ISER_LOG(CE_NOTE, "iser_ib_cm_handler:  Event failure");
93 		ret = iser_handle_cm_event_failure(eventp);
94 		break;
95 
96 	case IBT_CM_EVENT_MRA_RCV:
97 		/* Not supported */
98 		ISER_LOG(CE_NOTE, "iser_ib_cm_handler:  MRA message received");
99 		break;
100 
101 	case IBT_CM_EVENT_LAP_RCV:
102 		/* Not supported */
103 		ISER_LOG(CE_NOTE, "iser_ib_cm_handler: LAP message received");
104 		break;
105 
106 	case IBT_CM_EVENT_APR_RCV:
107 		/* Not supported */
108 		ISER_LOG(CE_NOTE, "iser_ib_cm_handler: APR message received");
109 		break;
110 
111 	default:
112 		ISER_LOG(CE_NOTE, "iser_ib_cm_handler: unknown event (0x%x)",
113 		    eventp->cm_type);
114 		break;
115 	}
116 
117 	return (ret);
118 }
119 
120 /* ARGSUSED */
121 static ibt_cm_status_t
122 iser_ib_handle_cm_req(idm_svc_t *svc_hdl, ibt_cm_event_t *evp,
123     ibt_cm_return_args_t *rargsp, void *rcmp, ibt_priv_data_len_t rcmp_len)
124 {
125 
126 	iser_private_data_t	iser_priv_data;
127 	ibt_ip_cm_info_t	ipcm_info;
128 	iser_chan_t		*chan;
129 	iser_conn_t		*iser_conn;
130 	int			status;
131 
132 	/*
133 	 * CM private data brings IP information
134 	 * Private data received is a stream of bytes and may not be properly
135 	 * aligned. So, bcopy the data onto the stack before accessing it.
136 	 */
137 	bcopy((uint8_t *)evp->cm_priv_data, &iser_priv_data,
138 	    sizeof (iser_private_data_t));
139 
140 	/* extract the CM IP info */
141 	status = ibt_get_ip_data(evp->cm_priv_data_len, evp->cm_priv_data,
142 	    &ipcm_info);
143 	if (status != IBT_SUCCESS) {
144 		return (IBT_CM_REJECT);
145 	}
146 
147 	ISER_LOG(CE_NOTE, "iser_ib_handle_cm_req: ipcm_info (0x%p): src IP "
148 	    "(0x%08x) src port (0x%04x) dst IP: (0x%08x)", (void *)&ipcm_info,
149 	    ipcm_info.src_addr.un.ip4addr, ipcm_info.src_port,
150 	    ipcm_info.dst_addr.un.ip4addr);
151 
152 	/* Allocate a channel to establish the new connection */
153 	chan = iser_ib_alloc_channel_nopathlookup(
154 	    evp->cm_event.req.req_hca_guid,
155 	    evp->cm_event.req.req_prim_hca_port);
156 	if (chan == NULL) {
157 		ISER_LOG(CE_NOTE, "iser_ib_handle_cm_req: failed to allocate "
158 		    "a channel from src IP (0x%08x) src port (0x%04x) "
159 		    "to dst IP: (0x%08x) on hca(%llx %d)",
160 		    ipcm_info.src_addr.un.ip4addr, ipcm_info.src_port,
161 		    ipcm_info.dst_addr.un.ip4addr,
162 		    (longlong_t)evp->cm_event.req.req_hca_guid,
163 		    evp->cm_event.req.req_prim_hca_port);
164 		return (IBT_CM_REJECT);
165 	}
166 
167 	/* Set the local and remote ip */
168 	chan->ic_localip = ipcm_info.dst_addr;
169 	chan->ic_remoteip = ipcm_info.src_addr;
170 
171 	/* Set the local and remote port numbers on the channel handle */
172 	chan->ic_lport = svc_hdl->is_svc_req.sr_port;
173 	chan->ic_rport = ipcm_info.src_port;
174 
175 	/* Allocate the iser_conn_t for the IDM svc binding */
176 	iser_conn = kmem_zalloc(sizeof (iser_conn_t), KM_SLEEP);
177 
178 	/* Set up the iser_conn attributes */
179 	mutex_init(&iser_conn->ic_lock, NULL, MUTEX_DRIVER, NULL);
180 	cv_init(&iser_conn->ic_stage_cv, NULL, CV_DEFAULT, NULL);
181 	iser_conn->ic_type = ISER_CONN_TYPE_TGT;
182 	iser_conn->ic_chan = chan;
183 	iser_conn->ic_stage = ISER_CONN_STAGE_ALLOCATED;
184 
185 	/* Hold a reference to the iSER service handle */
186 	iser_tgt_svc_hold((iser_svc_t *)svc_hdl->is_iser_svc);
187 
188 	iser_conn->ic_idms = svc_hdl;
189 
190 	/*
191 	 * Now set a pointer to the iser_conn in the iser_chan for
192 	 * access during CM event handling
193 	 */
194 	chan->ic_conn = iser_conn;
195 
196 	rargsp->cm_ret.rep.cm_channel = chan->ic_chanhdl;
197 
198 	return (IBT_CM_ACCEPT);
199 }
200 
201 /* ARGSUSED */
202 static ibt_cm_status_t
203 iser_ib_handle_cm_rep(iser_state_t *statep, ibt_cm_event_t *evp,
204     ibt_cm_return_args_t *rargsp, void *rcmp, ibt_priv_data_len_t rcmp_len)
205 {
206 	/* pre-post work requests into the receive queue */
207 	iser_ib_post_recv(evp->cm_channel);
208 
209 	/* It looks like the RTU need not be send specifically */
210 	return (IBT_CM_ACCEPT);
211 }
212 
213 static ibt_cm_status_t
214 iser_handle_cm_conn_est(ibt_cm_event_t *evp)
215 {
216 	iser_chan_t	*iser_chan;
217 	iser_conn_t	*iser_conn;
218 	iser_svc_t	*iser_svc;
219 	idm_status_t	status;
220 	idm_conn_t	*ic;
221 
222 	iser_chan = (iser_chan_t *)ibt_get_chan_private(evp->cm_channel);
223 
224 	/*
225 	 * An ibt_open_rc_channel() comes in as a IBT_CM_EVENT_REQ_RCV on the
226 	 * iSER-IB target, upon which the target sends a Response, accepting
227 	 * the request. This comes in as a IBT_CM_EVENT_REP_RCV on the iSER-IB
228 	 * initiator, which then sends an RTU. Upon getting this RTU from the
229 	 * iSER-IB initiator, the IBT_CM_EVENT_CONN_EST event is generated on
230 	 * the target. Then subsequently an IBT_CM_EVENT_CONN_EST event is
231 	 * generated on the initiator.
232 	 *
233 	 * Our new connection has been established on the target. If we are
234 	 * receiving this event on the target side, the iser_channel can be
235 	 * used as it is already populated. On the target side, an IDM
236 	 * connection is then allocated and the IDM layer is notified.
237 	 * If we are on the initiator we needn't do anything, since we
238 	 * already have the IDM linkage in place for this connection.
239 	 */
240 	if (iser_chan->ic_conn->ic_type == ISER_CONN_TYPE_TGT) {
241 
242 		iser_conn = iser_chan->ic_conn;
243 		iser_svc  = (iser_svc_t *)iser_conn->ic_idms->is_iser_svc;
244 
245 		mutex_enter(&iser_conn->ic_lock);
246 
247 		status = idm_svc_conn_create(iser_conn->ic_idms,
248 		    IDM_TRANSPORT_TYPE_ISER, &ic);
249 		if (status != IDM_STATUS_SUCCESS) {
250 			/*
251 			 * No IDM rsrcs or something equally Bad.
252 			 * Return non-SUCCESS to IBCM. It'll give
253 			 * us a CONN_CLOSED, which we'll handle
254 			 * below.
255 			 */
256 			ISER_LOG(CE_NOTE, "iser_handle_cm_conn_est: "
257 			    "idm_svc_conn_create_failed");
258 			mutex_exit(&iser_conn->ic_lock);
259 			return (IBT_CM_NO_RESOURCE);
260 		}
261 
262 		/* We no longer need the hold on the iSER service handle */
263 		iser_tgt_svc_rele(iser_svc);
264 
265 		/* Hold a reference on the IDM connection handle */
266 		idm_conn_hold(ic);
267 
268 		/* Set the transport ops and conn on the idm_conn handle */
269 		ic->ic_transport_ops = &iser_transport_ops;
270 		ic->ic_transport_private = (void *)iser_conn;
271 		ic->ic_transport_hdrlen = ISER_HEADER_LENGTH;
272 		iser_conn->ic_idmc = ic;
273 
274 		/*
275 		 * Set the local and remote addresses in the idm conn handle.
276 		 */
277 		iser_ib_conv_ibtaddr2sockaddr(&ic->ic_laddr,
278 		    &iser_conn->ic_chan->ic_localip, iser_chan->ic_lport);
279 		iser_ib_conv_ibtaddr2sockaddr(&ic->ic_raddr,
280 		    &iser_conn->ic_chan->ic_remoteip, iser_chan->ic_rport);
281 
282 		/*
283 		 * Kick the state machine.  At CS_S3_XPT_UP the state machine
284 		 * will notify the client (target) about the new connection.
285 		 */
286 		idm_conn_event(ic, CE_CONNECT_ACCEPT, NULL);
287 		iser_conn->ic_stage = ISER_CONN_STAGE_IC_CONNECTED;
288 		mutex_exit(&iser_conn->ic_lock);
289 
290 		/*
291 		 * Post work requests on the receive queue
292 		 */
293 		iser_ib_post_recv(iser_chan->ic_chanhdl);
294 
295 	}
296 
297 	return (IBT_CM_ACCEPT);
298 }
299 
300 static ibt_cm_status_t
301 iser_handle_cm_conn_closed(ibt_cm_event_t *evp)
302 {
303 
304 	iser_chan_t	*chan;
305 
306 	chan = (iser_chan_t *)ibt_get_chan_private(evp->cm_channel);
307 
308 	ISER_LOG(CE_NOTE, "iser_handle_cm_conn_closed: chan (0x%p) "
309 	    "reason (0x%x)", (void *)chan, evp->cm_event.closed);
310 
311 	switch (evp->cm_event.closed) {
312 	case IBT_CM_CLOSED_DREP_RCVD:	/* we requested a disconnect */
313 	case IBT_CM_CLOSED_ALREADY:	/* duplicate close */
314 		/* ignore these */
315 		return (IBT_CM_ACCEPT);
316 
317 	case IBT_CM_CLOSED_DREQ_RCVD:	/* request to close the channel */
318 	case IBT_CM_CLOSED_REJ_RCVD:	/* reject after conn establishment */
319 	case IBT_CM_CLOSED_DREQ_TIMEOUT: /* our close request timed out */
320 	case IBT_CM_CLOSED_DUP:		/* duplicate close request */
321 	case IBT_CM_CLOSED_ABORT:	/* aborted connection establishment */
322 	case IBT_CM_CLOSED_STALE:	/* stale / unref connection */
323 		/* handle these depending upon our connection state */
324 		mutex_enter(&chan->ic_conn->ic_lock);
325 		switch (chan->ic_conn->ic_stage) {
326 		case ISER_CONN_STAGE_UNDEFINED:
327 		case ISER_CONN_STAGE_CLOSED:
328 			/* do nothing, just drop the lock */
329 			mutex_exit(&chan->ic_conn->ic_lock);
330 			break;
331 
332 		case ISER_CONN_STAGE_ALLOCATED:
333 			/*
334 			 * We blew up or were offlined during connection
335 			 * establishment. Teardown the iSER conn and chan
336 			 * handles.
337 			 */
338 			mutex_exit(&chan->ic_conn->ic_lock);
339 			iser_internal_conn_destroy(chan->ic_conn);
340 			break;
341 
342 		case ISER_CONN_STAGE_IC_DISCONNECTED:
343 		case ISER_CONN_STAGE_IC_FREED:
344 		case ISER_CONN_STAGE_CLOSING:
345 			/* we're down, set CLOSED */
346 			chan->ic_conn->ic_stage = ISER_CONN_STAGE_CLOSED;
347 			mutex_exit(&chan->ic_conn->ic_lock);
348 			break;
349 
350 		case ISER_CONN_STAGE_IC_CONNECTED:
351 		case ISER_CONN_STAGE_HELLO_SENT:
352 		case ISER_CONN_STAGE_HELLO_SENT_FAIL:
353 		case ISER_CONN_STAGE_HELLO_WAIT:
354 		case ISER_CONN_STAGE_HELLO_RCV:
355 		case ISER_CONN_STAGE_HELLO_RCV_FAIL:
356 		case ISER_CONN_STAGE_HELLOREPLY_SENT:
357 		case ISER_CONN_STAGE_HELLOREPLY_SENT_FAIL:
358 		case ISER_CONN_STAGE_HELLOREPLY_RCV:
359 		case ISER_CONN_STAGE_HELLOREPLY_RCV_FAIL:
360 		case ISER_CONN_STAGE_LOGGED_IN:
361 			/* for all other stages, fail the transport */
362 			idm_conn_event(chan->ic_conn->ic_idmc,
363 			    CE_TRANSPORT_FAIL, IDM_STATUS_FAIL);
364 			chan->ic_conn->ic_stage = ISER_CONN_STAGE_CLOSING;
365 			mutex_exit(&chan->ic_conn->ic_lock);
366 			break;
367 
368 		default:
369 			mutex_exit(&chan->ic_conn->ic_lock);
370 			ASSERT(0);
371 
372 		}
373 
374 		/* accept the event */
375 		return (IBT_CM_ACCEPT);
376 
377 	default:
378 		/* unknown event */
379 		ISER_LOG(CE_NOTE, "iser_handle_cm_conn_closed: unknown closed "
380 		    "event: (0x%x)", evp->cm_event.closed);
381 		return (IBT_CM_REJECT);
382 	}
383 }
384 
385 /*
386  * Handle EVENT FAILURE
387  */
388 static ibt_cm_status_t
389 iser_handle_cm_event_failure(ibt_cm_event_t *evp)
390 {
391 	iser_chan_t	*chan;
392 
393 	chan = (iser_chan_t *)ibt_get_chan_private(evp->cm_channel);
394 
395 	ISER_LOG(CE_NOTE, "iser_handle_cm_event_failure: chan (0x%p): "
396 	    "code: %d msg: %d reason: %d", (void *)chan,
397 	    evp->cm_event.failed.cf_code, evp->cm_event.failed.cf_msg,
398 	    evp->cm_event.failed.cf_reason);
399 
400 	if ((evp->cm_channel == NULL) || (chan == NULL)) {
401 		/* channel not established yet */
402 		return (IBT_CM_ACCEPT);
403 	}
404 
405 	if ((evp->cm_event.failed.cf_code != IBT_CM_FAILURE_STALE) &&
406 	    (evp->cm_event.failed.cf_msg == IBT_CM_FAILURE_REQ)) {
407 		/*
408 		 * This end is active, just ignore, ibt_open_rc_channel()
409 		 * caller will take care of cleanup.
410 		 */
411 		return (IBT_CM_ACCEPT);
412 	}
413 
414 	/* handle depending upon our connection state */
415 	mutex_enter(&chan->ic_conn->ic_lock);
416 	switch (chan->ic_conn->ic_stage) {
417 	case ISER_CONN_STAGE_UNDEFINED:
418 	case ISER_CONN_STAGE_CLOSED:
419 		/* do nothing, just drop the lock */
420 		mutex_exit(&chan->ic_conn->ic_lock);
421 		break;
422 
423 	case ISER_CONN_STAGE_ALLOCATED:
424 		/*
425 		 * We blew up or were offlined during connection
426 		 * establishment. Teardown the iSER conn and chan
427 		 * handles.
428 		 */
429 		mutex_exit(&chan->ic_conn->ic_lock);
430 		iser_internal_conn_destroy(chan->ic_conn);
431 		break;
432 
433 	case ISER_CONN_STAGE_IC_DISCONNECTED:
434 	case ISER_CONN_STAGE_IC_FREED:
435 	case ISER_CONN_STAGE_CLOSING:
436 		/* update to CLOSED, then drop the lock */
437 		chan->ic_conn->ic_stage = ISER_CONN_STAGE_CLOSED;
438 		mutex_exit(&chan->ic_conn->ic_lock);
439 		break;
440 
441 	case ISER_CONN_STAGE_IC_CONNECTED:
442 	case ISER_CONN_STAGE_HELLO_SENT:
443 	case ISER_CONN_STAGE_HELLO_SENT_FAIL:
444 	case ISER_CONN_STAGE_HELLO_WAIT:
445 	case ISER_CONN_STAGE_HELLO_RCV:
446 	case ISER_CONN_STAGE_HELLO_RCV_FAIL:
447 	case ISER_CONN_STAGE_HELLOREPLY_SENT:
448 	case ISER_CONN_STAGE_HELLOREPLY_SENT_FAIL:
449 	case ISER_CONN_STAGE_HELLOREPLY_RCV:
450 	case ISER_CONN_STAGE_HELLOREPLY_RCV_FAIL:
451 	case ISER_CONN_STAGE_LOGGED_IN:
452 		/* fail the transport and move the conn to CLOSING */
453 		idm_conn_event(chan->ic_conn->ic_idmc, CE_TRANSPORT_FAIL,
454 		    IDM_STATUS_FAIL);
455 		chan->ic_conn->ic_stage = ISER_CONN_STAGE_CLOSING;
456 		mutex_exit(&chan->ic_conn->ic_lock);
457 		break;
458 
459 	default:
460 		mutex_exit(&chan->ic_conn->ic_lock);
461 		ASSERT(0);
462 	}
463 
464 	/* accept the event */
465 	return (IBT_CM_ACCEPT);
466 }
467