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
iser_ib_cm_handler(void * cm_private,ibt_cm_event_t * eventp,ibt_cm_return_args_t * ret_args,void * ret_priv_data,ibt_priv_data_len_t ret_len_max)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
iser_ib_handle_cm_req(idm_svc_t * svc_hdl,ibt_cm_event_t * evp,ibt_cm_return_args_t * rargsp,void * rcmp,ibt_priv_data_len_t rcmp_len)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
iser_ib_handle_cm_rep(iser_state_t * statep,ibt_cm_event_t * evp,ibt_cm_return_args_t * rargsp,void * rcmp,ibt_priv_data_len_t rcmp_len)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
iser_handle_cm_conn_est(ibt_cm_event_t * evp)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, (uintptr_t)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
iser_handle_cm_conn_closed(ibt_cm_event_t * evp)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
iser_handle_cm_event_failure(ibt_cm_event_t * evp)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