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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23 *
24 * iSCSI connection interfaces
25 */
26
27 #define ISCSI_ICS_NAMES
28 #include "iscsi.h"
29 #include "persistent.h"
30 #include <sys/bootprops.h>
31
32 extern ib_boot_prop_t *iscsiboot_prop;
33
34 static void iscsi_client_notify_task(void *cn_task_void);
35
36 static void iscsi_conn_flush_active_cmds(iscsi_conn_t *icp);
37
38 #define SHUTDOWN_TIMEOUT 180 /* seconds */
39
40 extern int modrootloaded;
41
42 boolean_t iscsi_conn_logging = B_FALSE;
43
44 #define ISCSI_LOGIN_TPGT_NEGO_ERROR(icp) \
45 (((icp)->conn_login_state == LOGIN_ERROR) && \
46 ((icp)->conn_login_status == ISCSI_STATUS_LOGIN_TPGT_NEGO_FAIL))
47
48 /*
49 * +--------------------------------------------------------------------+
50 * | External Connection Interfaces |
51 * +--------------------------------------------------------------------+
52 */
53
54 /*
55 * iscsi_conn_create - This creates an iscsi connection structure and
56 * associates it with a session structure. The session's sess_conn_list_rwlock
57 * should be held as a writer before calling this function.
58 */
59 iscsi_status_t
iscsi_conn_create(struct sockaddr * addr,iscsi_sess_t * isp,iscsi_conn_t ** icpp)60 iscsi_conn_create(struct sockaddr *addr, iscsi_sess_t *isp, iscsi_conn_t **icpp)
61 {
62 iscsi_conn_t *icp = NULL;
63 char th_name[ISCSI_TH_MAX_NAME_LEN];
64
65 /* See if this connection already exists */
66 for (icp = isp->sess_conn_list; icp; icp = icp->conn_next) {
67
68 /*
69 * Compare the ioctl information to see if
70 * its a match for this connection. (This
71 * is done by making sure the IPs are of
72 * the same size and then they are the
73 * same value.
74 */
75 if (bcmp(&icp->conn_base_addr, addr,
76 SIZEOF_SOCKADDR(addr)) == 0) {
77 /* It's a match, record this connection */
78 break;
79 }
80 }
81
82 /* If icp is found return it */
83 if (icp != NULL) {
84 *icpp = icp;
85 return (ISCSI_STATUS_SUCCESS);
86 }
87
88 /* We are creating the connection, allocate, and setup */
89 icp = (iscsi_conn_t *)kmem_zalloc(sizeof (iscsi_conn_t), KM_SLEEP);
90
91 /*
92 * Setup connection
93 */
94 icp->conn_sig = ISCSI_SIG_CONN;
95 icp->conn_state = ISCSI_CONN_STATE_FREE;
96 mutex_init(&icp->conn_state_mutex, NULL, MUTEX_DRIVER, NULL);
97 cv_init(&icp->conn_state_change, NULL, CV_DRIVER, NULL);
98 mutex_init(&icp->conn_login_mutex, NULL, MUTEX_DRIVER, NULL);
99 cv_init(&icp->conn_login_cv, NULL, CV_DRIVER, NULL);
100 icp->conn_state_destroy = B_FALSE;
101 idm_sm_audit_init(&icp->conn_state_audit);
102 icp->conn_sess = isp;
103
104 mutex_enter(&iscsi_oid_mutex);
105 icp->conn_oid = iscsi_oid++;
106 mutex_exit(&iscsi_oid_mutex);
107
108 /*
109 * IDM CN taskq
110 */
111
112 if (snprintf(th_name, sizeof (th_name) - 1,
113 ISCSI_CONN_CN_TASKQ_NAME_FORMAT,
114 icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid,
115 icp->conn_oid) >= sizeof (th_name)) {
116 cv_destroy(&icp->conn_state_change);
117 mutex_destroy(&icp->conn_state_mutex);
118 kmem_free(icp, sizeof (iscsi_conn_t));
119 *icpp = NULL;
120 return (ISCSI_STATUS_INTERNAL_ERROR);
121 }
122
123 icp->conn_cn_taskq =
124 ddi_taskq_create(icp->conn_sess->sess_hba->hba_dip, th_name, 1,
125 TASKQ_DEFAULTPRI, 0);
126 if (icp->conn_cn_taskq == NULL) {
127 cv_destroy(&icp->conn_state_change);
128 mutex_destroy(&icp->conn_state_mutex);
129 kmem_free(icp, sizeof (iscsi_conn_t));
130 *icpp = NULL;
131 return (ISCSI_STATUS_INTERNAL_ERROR);
132 }
133
134 /* Creation of the transfer thread */
135 if (snprintf(th_name, sizeof (th_name) - 1, ISCSI_CONN_TXTH_NAME_FORMAT,
136 icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid,
137 icp->conn_oid) >= sizeof (th_name)) {
138 cv_destroy(&icp->conn_state_change);
139 mutex_destroy(&icp->conn_state_mutex);
140 kmem_free(icp, sizeof (iscsi_conn_t));
141 ddi_taskq_destroy(icp->conn_cn_taskq);
142 *icpp = NULL;
143 return (ISCSI_STATUS_INTERNAL_ERROR);
144 }
145
146 icp->conn_tx_thread = iscsi_thread_create(isp->sess_hba->hba_dip,
147 th_name, iscsi_tx_thread, icp);
148
149 /* setup connection queues */
150 iscsi_init_queue(&icp->conn_queue_active);
151 iscsi_init_queue(&icp->conn_queue_idm_aborting);
152
153 bcopy(addr, &icp->conn_base_addr, sizeof (icp->conn_base_addr));
154
155 /* Add new connection to the session connection list */
156 icp->conn_cid = isp->sess_conn_next_cid++;
157 if (isp->sess_conn_list == NULL) {
158 isp->sess_conn_list = isp->sess_conn_list_last_ptr = icp;
159 } else {
160 isp->sess_conn_list_last_ptr->conn_next = icp;
161 isp->sess_conn_list_last_ptr = icp;
162 }
163
164 KSTAT_INC_SESS_CNTR_CONN(isp);
165 (void) iscsi_conn_kstat_init(icp);
166
167 *icpp = icp;
168
169 return (ISCSI_STATUS_SUCCESS);
170 }
171
172 /*
173 * iscsi_conn_online - This attempts to take a connection from
174 * ISCSI_CONN_STATE_FREE to ISCSI_CONN_STATE_LOGGED_IN.
175 */
176 iscsi_status_t
iscsi_conn_online(iscsi_conn_t * icp)177 iscsi_conn_online(iscsi_conn_t *icp)
178 {
179 iscsi_task_t *itp;
180 iscsi_status_t rval;
181
182 ASSERT(icp != NULL);
183 ASSERT(mutex_owned(&icp->conn_state_mutex));
184 ASSERT(icp->conn_state == ISCSI_CONN_STATE_FREE);
185
186 /*
187 * If we are attempting to connect then for the purposes of the
188 * other initiator code we are effectively in ISCSI_CONN_STATE_IN_LOGIN.
189 */
190 iscsi_conn_update_state_locked(icp, ISCSI_CONN_STATE_IN_LOGIN);
191 mutex_exit(&icp->conn_state_mutex);
192
193 /*
194 * Sync base connection information before login
195 * A login redirection might have shifted the
196 * current information from the base.
197 */
198 bcopy(&icp->conn_base_addr, &icp->conn_curr_addr,
199 sizeof (icp->conn_curr_addr));
200
201 itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
202 ASSERT(itp != NULL);
203
204 itp->t_arg = icp;
205 itp->t_blocking = B_TRUE;
206 rval = iscsi_login_start(itp);
207 kmem_free(itp, sizeof (iscsi_task_t));
208
209 mutex_enter(&icp->conn_state_mutex);
210
211 return (rval);
212 }
213
214 /*
215 * iscsi_conn_offline - This attempts to take a connection from
216 * any state to ISCSI_CONN_STATE_FREE.
217 */
218 iscsi_status_t
iscsi_conn_offline(iscsi_conn_t * icp)219 iscsi_conn_offline(iscsi_conn_t *icp)
220 {
221 clock_t delay;
222
223 ASSERT(icp != NULL);
224
225 /*
226 * We can only destroy a connection if its either in
227 * a state of FREE or LOGGED. The other states are
228 * transitionary and its unsafe to perform actions
229 * on the connection in those states. Set a flag
230 * on the connection to influence the transitions
231 * to quickly complete. Then wait for a state
232 * transition.
233 *
234 * ISCSI_CONN_STATE_LOGGED_IN is set immediately at the
235 * start of CN_NOTIFY_FFP processing. icp->conn_state_ffp
236 * is set to true at the end of ffp processing, at which
237 * point any session updates are complete. We don't
238 * want to start offlining the connection before we're
239 * done completing the FFP processing since this might
240 * interrupt the discovery process.
241 */
242 delay = ddi_get_lbolt() + SEC_TO_TICK(SHUTDOWN_TIMEOUT);
243 mutex_enter(&icp->conn_state_mutex);
244 icp->conn_state_destroy = B_TRUE;
245 while ((((icp->conn_state != ISCSI_CONN_STATE_FREE) &&
246 (icp->conn_state != ISCSI_CONN_STATE_LOGGED_IN)) ||
247 ((icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN) &&
248 !icp->conn_state_ffp)) &&
249 (ddi_get_lbolt() < delay)) {
250 /* wait for transition */
251 (void) cv_timedwait(&icp->conn_state_change,
252 &icp->conn_state_mutex, delay);
253 }
254
255 switch (icp->conn_state) {
256 case ISCSI_CONN_STATE_FREE:
257 break;
258 case ISCSI_CONN_STATE_LOGGED_IN:
259 if (icp->conn_state_ffp) {
260 /* Hold is released in iscsi_handle_logout */
261 idm_conn_hold(icp->conn_ic);
262 (void) iscsi_handle_logout(icp);
263 } else {
264 icp->conn_state_destroy = B_FALSE;
265 mutex_exit(&icp->conn_state_mutex);
266 return (ISCSI_STATUS_INTERNAL_ERROR);
267 }
268 break;
269 case ISCSI_CONN_STATE_IN_LOGIN:
270 case ISCSI_CONN_STATE_IN_LOGOUT:
271 case ISCSI_CONN_STATE_FAILED:
272 case ISCSI_CONN_STATE_POLLING:
273 default:
274 icp->conn_state_destroy = B_FALSE;
275 mutex_exit(&icp->conn_state_mutex);
276 return (ISCSI_STATUS_INTERNAL_ERROR);
277 }
278 mutex_exit(&icp->conn_state_mutex);
279
280 return (ISCSI_STATUS_SUCCESS);
281 }
282
283 /*
284 * iscsi_conn_destroy - This destroys an iscsi connection structure
285 * and de-associates it with the session. The connection should
286 * already been in the ISCSI_CONN_STATE_FREE when attempting this
287 * operation.
288 */
289 iscsi_status_t
iscsi_conn_destroy(iscsi_conn_t * icp)290 iscsi_conn_destroy(iscsi_conn_t *icp)
291 {
292 iscsi_sess_t *isp;
293 iscsi_conn_t *t_icp;
294
295 ASSERT(icp != NULL);
296 isp = icp->conn_sess;
297 ASSERT(isp != NULL);
298
299 if (icp->conn_state != ISCSI_CONN_STATE_FREE) {
300 return (ISCSI_STATUS_INTERNAL_ERROR);
301 }
302
303 /* Destroy transfer thread */
304 iscsi_thread_destroy(icp->conn_tx_thread);
305 ddi_taskq_destroy(icp->conn_cn_taskq);
306
307 /* Terminate connection queues */
308 iscsi_destroy_queue(&icp->conn_queue_idm_aborting);
309 iscsi_destroy_queue(&icp->conn_queue_active);
310
311 cv_destroy(&icp->conn_login_cv);
312 mutex_destroy(&icp->conn_login_mutex);
313 cv_destroy(&icp->conn_state_change);
314 mutex_destroy(&icp->conn_state_mutex);
315
316 /*
317 * Remove connection from sessions linked list.
318 */
319 if (isp->sess_conn_list == icp) {
320 /* connection first item in list */
321 isp->sess_conn_list = icp->conn_next;
322 /*
323 * check if this is also the last item in the list
324 */
325 if (isp->sess_conn_list_last_ptr == icp) {
326 isp->sess_conn_list_last_ptr = NULL;
327 }
328 } else {
329 /*
330 * search session list for icp pointing
331 * to connection being removed. Then
332 * update that connections next pointer.
333 */
334 t_icp = isp->sess_conn_list;
335 while (t_icp->conn_next != NULL) {
336 if (t_icp->conn_next == icp) {
337 break;
338 }
339 t_icp = t_icp->conn_next;
340 }
341 if (t_icp->conn_next == icp) {
342 t_icp->conn_next = icp->conn_next;
343 /*
344 * if this is the last connection in the list
345 * update the last_ptr to point to t_icp
346 */
347 if (isp->sess_conn_list_last_ptr == icp) {
348 isp->sess_conn_list_last_ptr = t_icp;
349 }
350 } else {
351 /* couldn't find session */
352 ASSERT(FALSE);
353 }
354 }
355
356 /* Free this Connections Data */
357 iscsi_conn_kstat_term(icp);
358 kmem_free(icp, sizeof (iscsi_conn_t));
359
360 return (ISCSI_STATUS_SUCCESS);
361 }
362
363
364 /*
365 * iscsi_conn_set_login_min_max - set min/max login window
366 *
367 * Used to set the min and max login window. Input values
368 * are in seconds.
369 */
370 void
iscsi_conn_set_login_min_max(iscsi_conn_t * icp,int min,int max)371 iscsi_conn_set_login_min_max(iscsi_conn_t *icp, int min, int max)
372 {
373 ASSERT(icp != NULL);
374
375 icp->conn_login_min = ddi_get_lbolt() + SEC_TO_TICK(min);
376 icp->conn_login_max = ddi_get_lbolt() + SEC_TO_TICK(max);
377 }
378
379
380 /*
381 * Process the idm notifications
382 */
383 idm_status_t
iscsi_client_notify(idm_conn_t * ic,idm_client_notify_t icn,uintptr_t data)384 iscsi_client_notify(idm_conn_t *ic, idm_client_notify_t icn, uintptr_t data)
385 {
386 iscsi_cn_task_t *cn;
387 iscsi_conn_t *icp = ic->ic_handle;
388 iscsi_sess_t *isp;
389 uint32_t event_count;
390
391 /*
392 * Don't access icp if the notification is CN_CONNECT_DESTROY
393 * since icp may have already been freed.
394 *
395 * In particular, we cannot audit the CN_CONNECT_DESTROY event.
396 *
397 * Handle a few cases immediately, the rest in a task queue.
398 */
399 switch (icn) {
400 case CN_CONNECT_FAIL:
401 case CN_LOGIN_FAIL:
402 /*
403 * Wakeup any thread waiting for login stuff to happen.
404 */
405 ASSERT(icp != NULL);
406
407 mutex_enter(&icp->conn_state_mutex);
408 idm_sm_audit_event(&icp->conn_state_audit,
409 SAS_ISCSI_CONN, icp->conn_state, icn, data);
410 mutex_exit(&icp->conn_state_mutex);
411 iscsi_login_update_state(icp, LOGIN_ERROR);
412 return (IDM_STATUS_SUCCESS);
413
414 case CN_READY_FOR_LOGIN:
415 idm_conn_hold(ic); /* Released in CN_CONNECT_LOST */
416 ASSERT(icp != NULL);
417
418 mutex_enter(&icp->conn_state_mutex);
419 idm_sm_audit_event(&icp->conn_state_audit,
420 SAS_ISCSI_CONN, icp->conn_state, icn, data);
421 icp->conn_state_idm_connected = B_TRUE;
422 cv_broadcast(&icp->conn_state_change);
423 mutex_exit(&icp->conn_state_mutex);
424
425 iscsi_login_update_state(icp, LOGIN_READY);
426 return (IDM_STATUS_SUCCESS);
427
428 case CN_CONNECT_DESTROY:
429 /*
430 * We released any dependecies we had on this object in
431 * either CN_LOGIN_FAIL or CN_CONNECT_LOST so we just need
432 * to destroy the IDM connection now.
433 */
434 idm_ini_conn_destroy(ic);
435 return (IDM_STATUS_SUCCESS);
436 }
437
438 ASSERT(icp != NULL);
439 mutex_enter(&icp->conn_state_mutex);
440 idm_sm_audit_event(&icp->conn_state_audit,
441 SAS_ISCSI_CONN, icp->conn_state, icn, data);
442 mutex_exit(&icp->conn_state_mutex);
443 isp = icp->conn_sess;
444
445 /*
446 * Dispatch notifications to the taskq since they often require
447 * long blocking operations. In the case of CN_CONNECT_DESTROY
448 * we actually just want to destroy the connection which we
449 * can't do in the IDM taskq context.
450 */
451 cn = kmem_alloc(sizeof (*cn), KM_SLEEP);
452
453 cn->ct_ic = ic;
454 cn->ct_icn = icn;
455 cn->ct_data = data;
456
457 idm_conn_hold(ic);
458
459 if (ddi_taskq_dispatch(icp->conn_cn_taskq,
460 iscsi_client_notify_task, cn, DDI_SLEEP) != DDI_SUCCESS) {
461 idm_conn_rele(ic);
462 cmn_err(CE_WARN, "iscsi connection(%u) failure - "
463 "unable to schedule notify task", icp->conn_oid);
464 iscsi_conn_update_state(icp, ISCSI_CONN_STATE_FREE);
465 event_count = atomic_inc_32_nv(&isp->sess_state_event_count);
466 iscsi_sess_enter_state_zone(isp);
467
468 iscsi_sess_state_machine(isp,
469 ISCSI_SESS_EVENT_N6, event_count);
470
471 iscsi_sess_exit_state_zone(isp);
472 }
473
474 return (IDM_STATUS_SUCCESS);
475 }
476
477 static void
iscsi_client_notify_task(void * cn_task_void)478 iscsi_client_notify_task(void *cn_task_void)
479 {
480 iscsi_cn_task_t *cn_task = cn_task_void;
481 iscsi_conn_t *icp;
482 iscsi_sess_t *isp;
483 idm_conn_t *ic;
484 idm_client_notify_t icn;
485 uintptr_t data;
486 idm_ffp_disable_t disable_type;
487 boolean_t in_login;
488 uint32_t event_count;
489
490 ic = cn_task->ct_ic;
491 icn = cn_task->ct_icn;
492 data = cn_task->ct_data;
493
494 icp = ic->ic_handle;
495 ASSERT(icp != NULL);
496 isp = icp->conn_sess;
497
498 switch (icn) {
499 case CN_FFP_ENABLED:
500 mutex_enter(&icp->conn_state_mutex);
501 icp->conn_async_logout = B_FALSE;
502 icp->conn_state_ffp = B_TRUE;
503 cv_broadcast(&icp->conn_state_change);
504 mutex_exit(&icp->conn_state_mutex);
505
506 /*
507 * This logic assumes that the IDM login-snooping code
508 * and the initiator login code will agree to go when
509 * the connection is in FFP or final error received.
510 * The reason we do this is that we don't want to process
511 * CN_FFP_DISABLED until CN_FFP_ENABLED has been full handled.
512 */
513 mutex_enter(&icp->conn_login_mutex);
514 while ((icp->conn_login_state != LOGIN_FFP) &&
515 (icp->conn_login_state != LOGIN_ERROR)) {
516 cv_wait(&icp->conn_login_cv, &icp->conn_login_mutex);
517 }
518 mutex_exit(&icp->conn_login_mutex);
519 break;
520 case CN_FFP_DISABLED:
521 disable_type = (idm_ffp_disable_t)data;
522
523 mutex_enter(&icp->conn_state_mutex);
524 switch (disable_type) {
525 case FD_SESS_LOGOUT:
526 case FD_CONN_LOGOUT:
527 if (icp->conn_async_logout) {
528 /*
529 * Our logout was in response to an
530 * async logout request so treat this
531 * like a connection failure (we will
532 * try to re-establish the connection)
533 */
534 iscsi_conn_update_state_locked(icp,
535 ISCSI_CONN_STATE_FAILED);
536 } else {
537 /*
538 * Logout due to to user config change,
539 * we will not try to re-establish
540 * the connection.
541 */
542 iscsi_conn_update_state_locked(icp,
543 ISCSI_CONN_STATE_IN_LOGOUT);
544 /*
545 * Hold off generating the ISCSI_SESS_EVENT_N3
546 * event until we get the CN_CONNECT_LOST
547 * notification. This matches the pre-IDM
548 * implementation better.
549 */
550 }
551 break;
552
553 case FD_CONN_FAIL:
554 default:
555 if (icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN) {
556 iscsi_conn_update_state_locked(icp,
557 ISCSI_CONN_STATE_FREE);
558 } else {
559 iscsi_conn_update_state_locked(icp,
560 ISCSI_CONN_STATE_FAILED);
561 }
562 break;
563 }
564
565 icp->conn_state_ffp = B_FALSE;
566 cv_broadcast(&icp->conn_state_change);
567 mutex_exit(&icp->conn_state_mutex);
568
569 break;
570 case CN_CONNECT_LOST:
571 /*
572 * We only care about CN_CONNECT_LOST if we've logged in. IDM
573 * sends a flag as the data payload to indicate whether we
574 * were trying to login. The CN_LOGIN_FAIL notification
575 * gives us what we need to know for login failures and
576 * otherwise we will need to keep a bunch of state to know
577 * what CN_CONNECT_LOST means to us.
578 */
579 in_login = (boolean_t)data;
580 if (in_login ||
581 (icp->conn_prev_state == ISCSI_CONN_STATE_IN_LOGIN)) {
582 mutex_enter(&icp->conn_state_mutex);
583
584 icp->conn_state_idm_connected = B_FALSE;
585 cv_broadcast(&icp->conn_state_change);
586 mutex_exit(&icp->conn_state_mutex);
587
588 /* Release connect hold from CN_READY_FOR_LOGIN */
589 idm_conn_rele(ic);
590 break;
591 }
592
593 /* Any remaining commands are never going to finish */
594 iscsi_conn_flush_active_cmds(icp);
595
596 /*
597 * The connection is no longer active so cleanup any
598 * references to the connection and release any holds so
599 * that IDM can finish cleanup.
600 */
601 mutex_enter(&icp->conn_state_mutex);
602 if (icp->conn_state != ISCSI_CONN_STATE_FAILED) {
603 mutex_exit(&icp->conn_state_mutex);
604 event_count = atomic_inc_32_nv(
605 &isp->sess_state_event_count);
606 iscsi_sess_enter_state_zone(isp);
607
608 iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N3,
609 event_count);
610
611 iscsi_sess_exit_state_zone(isp);
612
613 mutex_enter(&icp->conn_state_mutex);
614 iscsi_conn_update_state_locked(icp,
615 ISCSI_CONN_STATE_FREE);
616 } else {
617 mutex_exit(&icp->conn_state_mutex);
618 event_count = atomic_inc_32_nv(
619 &isp->sess_state_event_count);
620 iscsi_sess_enter_state_zone(isp);
621
622 iscsi_sess_state_machine(isp,
623 ISCSI_SESS_EVENT_N5, event_count);
624
625 iscsi_sess_exit_state_zone(isp);
626
627 /*
628 * If session type is NORMAL, try to reestablish the
629 * connection.
630 */
631 if ((isp->sess_type == ISCSI_SESS_TYPE_NORMAL) &&
632 !(ISCSI_LOGIN_TPGT_NEGO_ERROR(icp))) {
633 iscsi_conn_retry(isp, icp);
634 mutex_enter(&icp->conn_state_mutex);
635 } else {
636 event_count = atomic_inc_32_nv(
637 &isp->sess_state_event_count);
638 iscsi_sess_enter_state_zone(isp);
639
640 iscsi_sess_state_machine(isp,
641 ISCSI_SESS_EVENT_N6, event_count);
642
643 iscsi_sess_exit_state_zone(isp);
644
645 mutex_enter(&icp->conn_state_mutex);
646 iscsi_conn_update_state_locked(icp,
647 ISCSI_CONN_STATE_FREE);
648 }
649 }
650
651 (void) iscsi_thread_stop(icp->conn_tx_thread);
652
653 icp->conn_state_idm_connected = B_FALSE;
654 cv_broadcast(&icp->conn_state_change);
655 mutex_exit(&icp->conn_state_mutex);
656
657 /* Release connect hold from CN_READY_FOR_LOGIN */
658 idm_conn_rele(ic);
659 break;
660 default:
661 ISCSI_CONN_LOG(CE_WARN,
662 "iscsi_client_notify: unknown notification: "
663 "%x: NOT IMPLEMENTED YET: icp: %p ic: %p ",
664 icn, (void *)icp, (void *)ic);
665 break;
666 }
667 /* free the task notify structure we allocated in iscsi_client_notify */
668 kmem_free(cn_task, sizeof (*cn_task));
669
670 /* Release the hold we acquired in iscsi_client_notify */
671 idm_conn_rele(ic);
672 }
673
674 /*
675 * iscsi_conn_sync_params - used to update connection parameters
676 *
677 * Used to update connection parameters with current configured
678 * parameters in the persistent store. This should be called
679 * before starting to make a new iscsi connection in iscsi_login.
680 */
681 iscsi_status_t
iscsi_conn_sync_params(iscsi_conn_t * icp)682 iscsi_conn_sync_params(iscsi_conn_t *icp)
683 {
684 iscsi_sess_t *isp;
685 iscsi_hba_t *ihp;
686 int param_id;
687 persistent_param_t pp;
688 persistent_tunable_param_t ptp;
689 iscsi_config_sess_t *ics;
690 int idx, size;
691 char *name;
692
693 ASSERT(icp != NULL);
694 ASSERT((icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN) ||
695 (icp->conn_state == ISCSI_CONN_STATE_FAILED) ||
696 (icp->conn_state == ISCSI_CONN_STATE_POLLING));
697 isp = icp->conn_sess;
698 ASSERT(isp != NULL);
699 ihp = isp->sess_hba;
700 ASSERT(ihp != NULL);
701
702 /*
703 * Check if someone is trying to destroy this
704 * connection. If so fail the sync request,
705 * as a method of fast fail.
706 */
707 if (icp->conn_state_destroy == B_TRUE) {
708 return (ISCSI_STATUS_SHUTDOWN);
709 }
710
711 bzero(&pp, sizeof (pp));
712
713 /* First get a copy of the HBA params */
714 bcopy(&ihp->hba_params, &icp->conn_params,
715 sizeof (iscsi_login_params_t));
716 bcopy(&ihp->hba_tunable_params, &icp->conn_tunable_params,
717 sizeof (iscsi_tunable_params_t));
718
719 /*
720 * Now we need to get the session configured
721 * values from the persistent store and apply
722 * them to our connection.
723 */
724 (void) persistent_param_get((char *)isp->sess_name, &pp);
725 for (param_id = 0; param_id < ISCSI_NUM_LOGIN_PARAM;
726 param_id++) {
727 if (iscsiboot_prop && modrootloaded &&
728 !iscsi_chk_bootlun_mpxio(ihp) && isp->sess_boot) {
729 /*
730 * iscsi boot with mpxio disabled
731 * while iscsi booting target's parameter overriden
732 * do no update target's parameters.
733 */
734 if (pp.p_bitmap) {
735 cmn_err(CE_NOTE, "Adopting "
736 " default login parameters in"
737 " boot session as MPxIO is disabled");
738 }
739 break;
740 }
741 if (pp.p_bitmap & (1 << param_id)) {
742
743 switch (param_id) {
744 /*
745 * Boolean parameters
746 */
747 case ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER:
748 icp->conn_params.data_pdu_in_order =
749 pp.p_params.data_pdu_in_order;
750 break;
751 case ISCSI_LOGIN_PARAM_IMMEDIATE_DATA:
752 icp->conn_params.immediate_data =
753 pp.p_params.immediate_data;
754 break;
755 case ISCSI_LOGIN_PARAM_INITIAL_R2T:
756 icp->conn_params.initial_r2t =
757 pp.p_params.initial_r2t;
758 break;
759 case ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER:
760 icp->conn_params.data_pdu_in_order =
761 pp.p_params.data_pdu_in_order;
762 break;
763 /*
764 * Integer parameters
765 */
766 case ISCSI_LOGIN_PARAM_HEADER_DIGEST:
767 icp->conn_params.header_digest =
768 pp.p_params.header_digest;
769 break;
770 case ISCSI_LOGIN_PARAM_DATA_DIGEST:
771 icp->conn_params.data_digest =
772 pp.p_params.data_digest;
773 break;
774 case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN:
775 icp->conn_params.default_time_to_retain =
776 pp.p_params.default_time_to_retain;
777 break;
778 case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT:
779 icp->conn_params.default_time_to_wait =
780 pp.p_params.default_time_to_wait;
781 break;
782 case ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH:
783 icp->conn_params.max_recv_data_seg_len =
784 pp.p_params.max_recv_data_seg_len;
785 break;
786 case ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH:
787 icp->conn_params.first_burst_length =
788 pp.p_params.first_burst_length;
789 break;
790 case ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH:
791 icp->conn_params.max_burst_length =
792 pp.p_params.max_burst_length;
793 break;
794
795 /*
796 * Integer parameters which currently are unsettable
797 */
798 case ISCSI_LOGIN_PARAM_MAX_CONNECTIONS:
799 /* FALLTHRU */
800 case ISCSI_LOGIN_PARAM_OUTSTANDING_R2T:
801 /* FALLTHRU */
802 case ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL:
803 /* FALLTHRU */
804 default:
805 break;
806 }
807 }
808 }
809
810 if (persistent_get_tunable_param((char *)isp->sess_name, &ptp) ==
811 B_TRUE) {
812 if (ptp.p_bitmap & ISCSI_TUNABLE_PARAM_RX_TIMEOUT_VALUE) {
813 icp->conn_tunable_params.recv_login_rsp_timeout =
814 ptp.p_params.recv_login_rsp_timeout;
815 }
816 if (ptp.p_bitmap & ISCSI_TUNABLE_PARAM_CONN_LOGIN_MAX) {
817 icp->conn_tunable_params.conn_login_max =
818 ptp.p_params.conn_login_max;
819 }
820 if (ptp.p_bitmap & ISCSI_TUNABLE_PARAM_LOGIN_POLLING_DELAY) {
821 icp->conn_tunable_params.polling_login_delay =
822 ptp.p_params.polling_login_delay;
823 }
824 }
825
826 /* Skip binding checks on discovery sessions */
827 if (isp->sess_type == ISCSI_SESS_TYPE_DISCOVERY) {
828 return (ISCSI_STATUS_SUCCESS);
829 }
830
831 /*
832 * Now we need to get the current optional connection
833 * binding information.
834 */
835 /* setup initial buffer for configured session information */
836 size = sizeof (*ics);
837 ics = kmem_zalloc(size, KM_SLEEP);
838 ics->ics_in = 1;
839
840 /* get configured sessions information */
841 name = (char *)isp->sess_name;
842 if (persistent_get_config_session(name, ics) == B_FALSE) {
843 /*
844 * If we were unable to get target level information
845 * then check the initiator level information.
846 */
847 name = (char *)isp->sess_hba->hba_name;
848 if (persistent_get_config_session(name, ics) == B_FALSE) {
849 /*
850 * No hba information is found. So assume default
851 * one session unbound behavior.
852 */
853 ics->ics_out = 1;
854 ics->ics_bound = B_FALSE;
855 }
856 }
857
858 if (iscsiboot_prop && (ics->ics_out > 1) && isp->sess_boot &&
859 !iscsi_chk_bootlun_mpxio(ihp)) {
860 /*
861 * iscsi booting session with mpxio disabled,
862 * no need set multiple sessions for booting session
863 */
864 ics->ics_out = 1;
865 ics->ics_bound = B_FALSE;
866 cmn_err(CE_NOTE, "MPxIO is disabled,"
867 " no need to configure multiple boot sessions");
868 }
869
870 /*
871 * Check to make sure this session is still a configured
872 * session. The user might have decreased the session
873 * count. (NOTE: byte 5 of the sess_isid is the session
874 * count (via MS/T). This counter starts at 0.)
875 */
876
877
878 idx = isp->sess_isid[5];
879
880 if (iscsiboot_prop && (idx == ISCSI_MAX_CONFIG_SESSIONS)) {
881 /*
882 * This is temporary session for boot session propose
883 * no need to bound IP for this session
884 */
885 icp->conn_bound = B_FALSE;
886 kmem_free(ics, sizeof (iscsi_config_sess_t));
887 return (ISCSI_STATUS_SUCCESS);
888 }
889
890 if (ics->ics_out <= idx) {
891 /*
892 * No longer a configured session. Return a
893 * failure so we don't attempt to relogin.
894 */
895 return (ISCSI_STATUS_SHUTDOWN);
896 }
897
898 /*
899 * If sessions are unbound set this information on
900 * the connection and return success.
901 */
902 if (ics->ics_bound == B_FALSE) {
903 icp->conn_bound = B_FALSE;
904 kmem_free(ics, sizeof (iscsi_config_sess_t));
905 return (ISCSI_STATUS_SUCCESS);
906 }
907
908 /*
909 * Since the sessions are bound we need to find the matching
910 * binding information for the session's isid. If this
911 * session's isid is > 0 then we need to get more configured
912 * session information to find the binding info.
913 */
914 if (idx > 0) {
915 int ics_out;
916
917 ics_out = ics->ics_out;
918 /* record new size and free last buffer */
919 size = ISCSI_SESSION_CONFIG_SIZE(ics_out);
920 kmem_free(ics, sizeof (*ics));
921
922 /* allocate new buffer */
923 ics = kmem_zalloc(size, KM_SLEEP);
924 ics->ics_in = ics_out;
925
926 /* get configured sessions information */
927 if (persistent_get_config_session(name, ics) != B_TRUE) {
928 cmn_err(CE_NOTE, "iscsi session(%d) - "
929 "unable to get configured session information\n",
930 isp->sess_oid);
931 kmem_free(ics, size);
932 return (ISCSI_STATUS_SHUTDOWN);
933 }
934 }
935
936 /* Copy correct binding information to the connection */
937 icp->conn_bound = B_TRUE;
938 if (ics->ics_bindings[idx].i_insize == sizeof (struct in_addr)) {
939 bcopy(&ics->ics_bindings[idx].i_addr.in4,
940 &icp->conn_bound_addr.sin4.sin_addr.s_addr,
941 sizeof (struct in_addr));
942 icp->conn_bound_addr.sin4.sin_family = AF_INET;
943 } else {
944 bcopy(&ics->ics_bindings[idx].i_addr.in6,
945 &icp->conn_bound_addr.sin6.sin6_addr.s6_addr,
946 sizeof (struct in6_addr));
947 icp->conn_bound_addr.sin6.sin6_family = AF_INET6;
948 }
949
950 kmem_free(ics, size);
951
952 return (ISCSI_STATUS_SUCCESS);
953 }
954
955 /*
956 * +--------------------------------------------------------------------+
957 * | Internal Connection Interfaces |
958 * +--------------------------------------------------------------------+
959 */
960
961 /*
962 * iscsi_conn_flush_active_cmds - flush all active icmdps
963 * for a connection.
964 */
965 static void
iscsi_conn_flush_active_cmds(iscsi_conn_t * icp)966 iscsi_conn_flush_active_cmds(iscsi_conn_t *icp)
967 {
968 iscsi_cmd_t *icmdp;
969 iscsi_sess_t *isp;
970 boolean_t lock_held = B_FALSE;
971
972 ASSERT(icp != NULL);
973 isp = icp->conn_sess;
974 ASSERT(isp != NULL);
975
976 if (mutex_owned(&icp->conn_queue_active.mutex)) {
977 lock_held = B_TRUE;
978 } else {
979 mutex_enter(&icp->conn_queue_active.mutex);
980 }
981
982 /* Flush active queue */
983 icmdp = icp->conn_queue_active.head;
984 while (icmdp != NULL) {
985
986 mutex_enter(&icmdp->cmd_mutex);
987 if (icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) {
988 icmdp->cmd_un.scsi.pkt_stat |= STAT_ABORTED;
989 }
990 mutex_exit(&icmdp->cmd_mutex);
991
992 iscsi_cmd_state_machine(icmdp,
993 ISCSI_CMD_EVENT_E7, isp);
994 icmdp = icp->conn_queue_active.head;
995 }
996
997 /* Wait for active queue to drain */
998 while (icp->conn_queue_active.count) {
999 mutex_exit(&icp->conn_queue_active.mutex);
1000 delay(drv_usectohz(100000));
1001 mutex_enter(&icp->conn_queue_active.mutex);
1002 }
1003
1004 if (lock_held == B_FALSE) {
1005 mutex_exit(&icp->conn_queue_active.mutex);
1006 }
1007
1008 /* Wait for IDM abort queue to drain (if necessary) */
1009 mutex_enter(&icp->conn_queue_idm_aborting.mutex);
1010 while (icp->conn_queue_idm_aborting.count) {
1011 mutex_exit(&icp->conn_queue_idm_aborting.mutex);
1012 delay(drv_usectohz(100000));
1013 mutex_enter(&icp->conn_queue_idm_aborting.mutex);
1014 }
1015 mutex_exit(&icp->conn_queue_idm_aborting.mutex);
1016 }
1017
1018 /*
1019 * iscsi_conn_retry - retry connect/login
1020 */
1021 void
iscsi_conn_retry(iscsi_sess_t * isp,iscsi_conn_t * icp)1022 iscsi_conn_retry(iscsi_sess_t *isp, iscsi_conn_t *icp)
1023 {
1024 iscsi_task_t *itp;
1025 uint32_t event_count;
1026
1027 ASSERT(isp != NULL);
1028 ASSERT(icp != NULL);
1029
1030 /* set login min/max time values */
1031 iscsi_conn_set_login_min_max(icp,
1032 ISCSI_CONN_DEFAULT_LOGIN_MIN,
1033 icp->conn_tunable_params.conn_login_max);
1034
1035 ISCSI_CONN_LOG(CE_NOTE, "DEBUG: iscsi_conn_retry: icp: %p icp: %p ",
1036 (void *)icp,
1037 (void *)icp->conn_ic);
1038
1039 /*
1040 * Sync base connection information before login.
1041 * A login redirection might have shifted the
1042 * current information from the base.
1043 */
1044 bcopy(&icp->conn_base_addr, &icp->conn_curr_addr,
1045 sizeof (icp->conn_curr_addr));
1046
1047 /* schedule login task */
1048 itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
1049 itp->t_arg = icp;
1050 itp->t_blocking = B_FALSE;
1051 if (ddi_taskq_dispatch(isp->sess_login_taskq,
1052 iscsi_login_cb, itp, DDI_SLEEP) !=
1053 DDI_SUCCESS) {
1054 kmem_free(itp, sizeof (iscsi_task_t));
1055 cmn_err(CE_WARN, "iscsi connection(%u) failure - "
1056 "unable to schedule login task", icp->conn_oid);
1057
1058 iscsi_conn_update_state(icp, ISCSI_CONN_STATE_FREE);
1059 event_count = atomic_inc_32_nv(
1060 &isp->sess_state_event_count);
1061 iscsi_sess_enter_state_zone(isp);
1062
1063 iscsi_sess_state_machine(isp,
1064 ISCSI_SESS_EVENT_N6, event_count);
1065
1066 iscsi_sess_exit_state_zone(isp);
1067 }
1068 }
1069
1070 void
iscsi_conn_update_state(iscsi_conn_t * icp,iscsi_conn_state_t next_state)1071 iscsi_conn_update_state(iscsi_conn_t *icp, iscsi_conn_state_t next_state)
1072 {
1073 mutex_enter(&icp->conn_state_mutex);
1074 (void) iscsi_conn_update_state_locked(icp, next_state);
1075 mutex_exit(&icp->conn_state_mutex);
1076 }
1077
1078 void
iscsi_conn_update_state_locked(iscsi_conn_t * icp,iscsi_conn_state_t next_state)1079 iscsi_conn_update_state_locked(iscsi_conn_t *icp, iscsi_conn_state_t next_state)
1080 {
1081 ASSERT(mutex_owned(&icp->conn_state_mutex));
1082 next_state = (next_state > ISCSI_CONN_STATE_MAX) ?
1083 ISCSI_CONN_STATE_MAX : next_state;
1084 idm_sm_audit_state_change(&icp->conn_state_audit,
1085 SAS_ISCSI_CONN, icp->conn_state, next_state);
1086 switch (next_state) {
1087 case ISCSI_CONN_STATE_FREE:
1088 case ISCSI_CONN_STATE_IN_LOGIN:
1089 case ISCSI_CONN_STATE_LOGGED_IN:
1090 case ISCSI_CONN_STATE_IN_LOGOUT:
1091 case ISCSI_CONN_STATE_FAILED:
1092 case ISCSI_CONN_STATE_POLLING:
1093 ISCSI_CONN_LOG(CE_NOTE,
1094 "iscsi_conn_update_state conn %p %s(%d) -> %s(%d)",
1095 (void *)icp,
1096 iscsi_ics_name[icp->conn_state], icp->conn_state,
1097 iscsi_ics_name[next_state], next_state);
1098 icp->conn_prev_state = icp->conn_state;
1099 icp->conn_state = next_state;
1100 cv_broadcast(&icp->conn_state_change);
1101 break;
1102 default:
1103 cmn_err(CE_WARN, "Update state found illegal state: %x "
1104 "prev_state: %x", next_state, icp->conn_prev_state);
1105 ASSERT(0);
1106 }
1107 }
1108