xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_conn.c (revision 9fe633fd812f2df2354dc88fd3f7f50e94bd8eb3)
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