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 session interfaces
25 */
26
27 #include <sys/bootprops.h>
28 #include "iscsi.h"
29 #include "persistent.h"
30 #include "iscsi_targetparam.h"
31
32 #define ISCSI_SESS_ENUM_TIMEOUT_DEFAULT 60
33 #define SCSI_INQUIRY_PQUAL_MASK 0xE0
34
35 boolean_t iscsi_sess_logging = B_FALSE;
36 /*
37 * used to store report lun information found
38 *
39 * lun_valid: if TRUE means the entry contains a valid entry
40 * lun_found: if TRUE means the lun has been found in the sess_lun_list
41 * lun_num: contains the lun_number
42 * lun_addr_type: indicates lun's type of addressing
43 */
44 typedef struct replun_data {
45 boolean_t lun_valid;
46 boolean_t lun_found;
47 uint16_t lun_num;
48 uint8_t lun_addr_type;
49 } replun_data_t;
50
51 int iscsi_sess_enum_timeout = ISCSI_SESS_ENUM_TIMEOUT_DEFAULT;
52
53 /*
54 * The following private tunable, settable via
55 * set iscsi:iscsi_sess_max_delay = 64
56 * in /etc/system, provides customer relief for configurations max interval in
57 * seconds of retry for a unreachable target during the login.
58 */
59 int iscsi_sess_max_delay = ISCSI_DEFAULT_MAX_STORM_DELAY;
60
61 /*
62 * Warning messages for the session scsi enumeration
63 */
64 static const char *iscsi_sess_enum_warn_msgs[] = {
65 "completed",
66 "partially successful",
67 "IO failures",
68 "submitted",
69 "unable to submit the enumeration",
70 "session is gone",
71 "test unit ready failed"
72 };
73
74 /* internal interfaces */
75 /* LINTED E_STATIC_UNUSED */
76 static iscsi_sess_t *iscsi_sess_alloc(iscsi_hba_t *ihp, iscsi_sess_type_t type);
77 static char *iscsi_sess_event_str(iscsi_sess_event_t event);
78 static iscsi_status_t iscsi_sess_threads_create(iscsi_sess_t *isp);
79 static void iscsi_sess_flush(iscsi_sess_t *isp);
80 static void iscsi_sess_offline_luns(iscsi_sess_t *isp);
81 static iscsi_status_t retrieve_lundata(uint32_t lun_count, unsigned char *buf,
82 iscsi_sess_t *isp, uint16_t *lun_data, uint8_t *lun_addr_type);
83
84 /* internal state machine interfaces */
85 static void iscsi_sess_state_free(iscsi_sess_t *isp,
86 iscsi_sess_event_t event, uint32_t event_count);
87 static void iscsi_sess_state_logged_in(iscsi_sess_t *isp,
88 iscsi_sess_event_t event, uint32_t event_count);
89 static void iscsi_sess_state_failed(iscsi_sess_t *isp,
90 iscsi_sess_event_t event, uint32_t event_count);
91 static void iscsi_sess_state_in_flush(iscsi_sess_t *isp,
92 iscsi_sess_event_t event, uint32_t event_count);
93 static void iscsi_sess_state_flushed(iscsi_sess_t *isp,
94 iscsi_sess_event_t event, uint32_t event_count);
95
96 /* internal enumeration interfaces */
97 static void iscsi_sess_enumeration(void *arg);
98 static iscsi_status_t iscsi_sess_testunitready(iscsi_sess_t *isp,
99 uint32_t event_count);
100 static iscsi_status_t iscsi_sess_reportluns(iscsi_sess_t *isp,
101 uint32_t event_count);
102 static void iscsi_sess_inquiry(iscsi_sess_t *isp, uint16_t lun_num,
103 uint8_t lun_addr_type, uint32_t event_count, iscsi_lun_t *ilp);
104 static void iscsi_sess_update_busy_luns(iscsi_sess_t *isp, boolean_t clear);
105 static void iscsi_sess_enum_warn(iscsi_sess_t *isp, iscsi_enum_result_t r);
106
107 /*
108 * +--------------------------------------------------------------------+
109 * | External Session Interfaces |
110 * +--------------------------------------------------------------------+
111 */
112 iscsi_sess_t *
iscsi_sess_create(iscsi_hba_t * ihp,iSCSIDiscoveryMethod_t method,struct sockaddr * addr_dsc,char * target_name,int tpgt,uchar_t isid_lsb,iscsi_sess_type_t type,uint32_t * oid)113 iscsi_sess_create(iscsi_hba_t *ihp, iSCSIDiscoveryMethod_t method,
114 struct sockaddr *addr_dsc, char *target_name, int tpgt, uchar_t isid_lsb,
115 iscsi_sess_type_t type, uint32_t *oid)
116 {
117 iscsi_sess_t *isp = NULL;
118 int len = 0;
119 char *tq_name;
120 char *th_name;
121 iscsi_status_t status;
122
123 len = strlen(target_name);
124
125 clean_failed_sess:
126 if (isp != NULL) {
127 (void) iscsi_sess_destroy(isp);
128 }
129
130 for (isp = ihp->hba_sess_list; isp; isp = isp->sess_next) {
131 /* Match target name and LSB ISID */
132 if ((strcmp((char *)isp->sess_name, target_name) == 0) &&
133 (isp->sess_isid[5] == isid_lsb)) {
134
135 /* Match TPGT */
136 if (isp->sess_tpgt_conf == tpgt) {
137 /* Found mathing session, return oid/ptr */
138 *oid = isp->sess_oid;
139 if (isp->sess_wd_thread != NULL &&
140 isp->sess_ic_thread != NULL) {
141 return (isp);
142 }
143
144 if (isp->sess_wd_thread == NULL) {
145 /*
146 * Under rare cases wd thread is already
147 * freed, create it if so.
148 */
149 th_name = kmem_zalloc(
150 ISCSI_TH_MAX_NAME_LEN, KM_SLEEP);
151 if (snprintf(th_name,
152 (ISCSI_TH_MAX_NAME_LEN - 1),
153 ISCSI_SESS_WD_NAME_FORMAT,
154 ihp->hba_oid, isp->sess_oid) <
155 ISCSI_TH_MAX_NAME_LEN) {
156 isp->sess_wd_thread =
157 iscsi_thread_create(
158 ihp->hba_dip,
159 th_name,
160 iscsi_wd_thread,
161 isp);
162 (void) iscsi_thread_start(
163 isp->sess_wd_thread);
164 }
165 kmem_free(th_name,
166 ISCSI_TH_MAX_NAME_LEN);
167 if (isp->sess_wd_thread == NULL) {
168 /* No way to save it */
169 goto clean_failed_sess;
170 }
171 }
172
173 if (isp->sess_ic_thread == NULL) {
174 status = iscsi_sess_threads_create(isp);
175 if (status != ISCSI_STATUS_SUCCESS) {
176 goto clean_failed_sess;
177 }
178 }
179 return (isp);
180 }
181
182 /*
183 * Also protect against creating duplicate
184 * sessions with different configured tpgt
185 * values. default vs. defined.
186 */
187 if ((((isp->sess_tpgt_conf == ISCSI_DEFAULT_TPGT) &&
188 (tpgt != ISCSI_DEFAULT_TPGT)) ||
189 ((isp->sess_tpgt_conf != ISCSI_DEFAULT_TPGT) &&
190 (tpgt == ISCSI_DEFAULT_TPGT)))) {
191 /* Dangerous configuration. Fail Request */
192 return (NULL);
193 }
194 }
195 }
196
197 isp = (iscsi_sess_t *)kmem_zalloc(sizeof (iscsi_sess_t), KM_SLEEP);
198 /*
199 * If this session is not a Send Targets session, set the target
200 * that this session is associated with.
201 */
202 if (strncmp(target_name, SENDTARGETS_DISCOVERY,
203 strlen(SENDTARGETS_DISCOVERY))) {
204 isp->sess_target_oid = iscsi_targetparam_get_oid(
205 (uchar_t *)target_name);
206 }
207
208 if (method & iSCSIDiscoveryMethodBoot) {
209 /* This is boot session. */
210 isp->sess_boot = B_TRUE;
211 } else {
212 isp->sess_boot = B_FALSE;
213 }
214
215 /* Associate session with this discovery method */
216 method = method & ~(iSCSIDiscoveryMethodBoot);
217
218 isp->sess_discovered_by = method;
219 if (addr_dsc == NULL) {
220 bzero(&isp->sess_discovered_addr,
221 sizeof (isp->sess_discovered_addr));
222 } else {
223 bcopy(addr_dsc, &isp->sess_discovered_addr,
224 SIZEOF_SOCKADDR(addr_dsc));
225 }
226
227 /* assign unique key for the session */
228 mutex_enter(&iscsi_oid_mutex);
229 isp->sess_oid = iscsi_oid++;
230 *oid = isp->sess_oid;
231 mutex_exit(&iscsi_oid_mutex);
232
233 /* setup session parameters */
234 isp->sess_name_length = 0;
235 isp->sess_sig = ISCSI_SIG_SESS;
236 isp->sess_state = ISCSI_SESS_STATE_FREE;
237 rw_init(&isp->sess_state_rwlock, NULL, RW_DRIVER, NULL);
238 mutex_init(&isp->sess_reset_mutex, NULL, MUTEX_DRIVER, NULL);
239 isp->sess_hba = ihp;
240
241 isp->sess_isid[0] = ISCSI_SUN_ISID_0;
242 isp->sess_isid[1] = ISCSI_SUN_ISID_1;
243 isp->sess_isid[2] = ISCSI_SUN_ISID_2;
244 isp->sess_isid[3] = ISCSI_SUN_ISID_3;
245 isp->sess_isid[4] = 0;
246 isp->sess_isid[5] = isid_lsb;
247
248 isp->sess_cmdsn = 1;
249 isp->sess_expcmdsn = 1;
250 isp->sess_maxcmdsn = 1;
251 isp->sess_last_err = NoError;
252 isp->sess_tsid = 0;
253 isp->sess_type = type;
254 isp->sess_reset_in_progress = B_FALSE;
255 isp->sess_boot_nic_reset = B_FALSE;
256 idm_sm_audit_init(&isp->sess_state_audit);
257
258 /* copy default driver login parameters */
259 bcopy(&ihp->hba_params, &isp->sess_params,
260 sizeof (iscsi_login_params_t));
261
262 /* copy target name into session */
263 bcopy((char *)target_name, isp->sess_name, len);
264 isp->sess_name_length = len;
265 isp->sess_tpgt_conf = tpgt;
266 isp->sess_tpgt_nego = ISCSI_DEFAULT_TPGT;
267
268 /* initialize pending and completion queues */
269 iscsi_init_queue(&isp->sess_queue_pending);
270 iscsi_init_queue(&isp->sess_queue_completion);
271
272 /* setup sessions lun list */
273 isp->sess_lun_list = NULL;
274 rw_init(&isp->sess_lun_list_rwlock, NULL, RW_DRIVER, NULL);
275
276 /* setup sessions connection list */
277 isp->sess_conn_act = NULL;
278 isp->sess_conn_list = NULL;
279 rw_init(&isp->sess_conn_list_rwlock, NULL, RW_DRIVER, NULL);
280
281 mutex_init(&isp->sess_cmdsn_mutex, NULL, MUTEX_DRIVER, NULL);
282
283 /* create the session task queue */
284 tq_name = kmem_zalloc(ISCSI_TH_MAX_NAME_LEN, KM_SLEEP);
285 if (snprintf(tq_name, (ISCSI_TH_MAX_NAME_LEN - 1),
286 ISCSI_SESS_LOGIN_TASKQ_NAME_FORMAT, ihp->hba_oid, isp->sess_oid) <
287 ISCSI_TH_MAX_NAME_LEN) {
288 isp->sess_login_taskq = ddi_taskq_create(ihp->hba_dip,
289 tq_name, 1, TASKQ_DEFAULTPRI, 0);
290 }
291 if (isp->sess_login_taskq == NULL) {
292 kmem_free(tq_name, ISCSI_TH_MAX_NAME_LEN);
293 goto iscsi_sess_cleanup2;
294 }
295
296 if (snprintf(tq_name, (ISCSI_TH_MAX_NAME_LEN - 1),
297 ISCSI_SESS_ENUM_TASKQ_NAME_FORMAT, ihp->hba_oid, isp->sess_oid) <
298 ISCSI_TH_MAX_NAME_LEN) {
299 isp->sess_enum_taskq = ddi_taskq_create(ihp->hba_dip,
300 tq_name, 1, TASKQ_DEFAULTPRI, 0);
301 }
302 kmem_free(tq_name, ISCSI_TH_MAX_NAME_LEN);
303 if (isp->sess_enum_taskq == NULL) {
304 goto iscsi_sess_cleanup1;
305 }
306 /* startup watchdog */
307 th_name = kmem_zalloc(ISCSI_TH_MAX_NAME_LEN, KM_SLEEP);
308 if (snprintf(th_name, (ISCSI_TH_MAX_NAME_LEN - 1),
309 ISCSI_SESS_WD_NAME_FORMAT, ihp->hba_oid, isp->sess_oid) <
310 ISCSI_TH_MAX_NAME_LEN) {
311 isp->sess_wd_thread = iscsi_thread_create(ihp->hba_dip,
312 th_name, iscsi_wd_thread, isp);
313 (void) iscsi_thread_start(isp->sess_wd_thread);
314 }
315
316 kmem_free(th_name, ISCSI_TH_MAX_NAME_LEN);
317 if (isp->sess_wd_thread == NULL) {
318 goto iscsi_sess_cleanup0;
319 }
320
321 status = iscsi_sess_threads_create(isp);
322 if (status != ISCSI_STATUS_SUCCESS) {
323 goto iscsi_sess_cleanup1;
324 }
325
326 /* Add new target to the hba target list */
327 if (ihp->hba_sess_list == NULL) {
328 ihp->hba_sess_list = isp;
329 } else {
330 isp->sess_next = ihp->hba_sess_list;
331 ihp->hba_sess_list = isp;
332 }
333 KSTAT_INC_HBA_CNTR_SESS(ihp);
334
335 (void) iscsi_sess_kstat_init(isp);
336
337 if (type == ISCSI_SESS_TYPE_NORMAL) {
338 isp->sess_enum_status = ISCSI_SESS_ENUM_FREE;
339 isp->sess_enum_result = ISCSI_SESS_ENUM_COMPLETE;
340 isp->sess_enum_result_count = 0;
341 mutex_init(&isp->sess_enum_lock, NULL, MUTEX_DRIVER, NULL);
342 cv_init(&isp->sess_enum_cv, NULL, CV_DRIVER, NULL);
343 }
344
345 mutex_init(&isp->sess_state_wmutex, NULL, MUTEX_DRIVER, NULL);
346 cv_init(&isp->sess_state_wcv, NULL, CV_DRIVER, NULL);
347 isp->sess_state_hasw = B_FALSE;
348
349 isp->sess_state_event_count = 0;
350
351 return (isp);
352 iscsi_sess_cleanup0:
353 ddi_taskq_destroy(isp->sess_enum_taskq);
354 iscsi_sess_cleanup1:
355 ddi_taskq_destroy(isp->sess_login_taskq);
356 iscsi_sess_cleanup2:
357 if (isp->sess_wd_thread != NULL) {
358 iscsi_thread_destroy(isp->sess_wd_thread);
359 isp->sess_wd_thread = NULL;
360 }
361 if (isp->sess_ic_thread != NULL) {
362 iscsi_thread_destroy(isp->sess_ic_thread);
363 isp->sess_ic_thread = NULL;
364 }
365 mutex_destroy(&isp->sess_cmdsn_mutex);
366 rw_destroy(&isp->sess_conn_list_rwlock);
367 rw_destroy(&isp->sess_lun_list_rwlock);
368 iscsi_destroy_queue(&isp->sess_queue_completion);
369 iscsi_destroy_queue(&isp->sess_queue_pending);
370 rw_destroy(&isp->sess_state_rwlock);
371 mutex_destroy(&isp->sess_reset_mutex);
372 kmem_free(isp, sizeof (iscsi_sess_t));
373
374 return (NULL);
375 }
376
377 /*
378 * iscsi_sess_get - return the session structure for based on a
379 * passed in oid and hba instance.
380 */
381 int
iscsi_sess_get(uint32_t oid,iscsi_hba_t * ihp,iscsi_sess_t ** ispp)382 iscsi_sess_get(uint32_t oid, iscsi_hba_t *ihp, iscsi_sess_t **ispp)
383 {
384 int rval = 0;
385 iscsi_sess_t *isp = NULL;
386
387 ASSERT(ihp != NULL);
388 ASSERT(ispp != NULL);
389
390 /* See if we already created this session */
391 for (isp = ihp->hba_sess_list; isp; isp = isp->sess_next) {
392 /* compare target name as the unique identifier */
393 if (isp->sess_oid == oid) {
394 /* Found matching session */
395 break;
396 }
397 }
398
399 /* If not null this session is already available */
400 if (isp != NULL) {
401 /* Existing session, return it */
402 *ispp = isp;
403 } else {
404 rval = EFAULT;
405 }
406 return (rval);
407 }
408
409 /*
410 * iscsi_sess_online - initiate online of sessions connections
411 */
412 void
iscsi_sess_online(void * arg)413 iscsi_sess_online(void *arg)
414 {
415 iscsi_sess_t *isp;
416 iscsi_hba_t *ihp;
417 iscsi_conn_t *icp;
418 int idx;
419 uint32_t event_count;
420
421 isp = (iscsi_sess_t *)arg;
422
423 ASSERT(isp != NULL);
424 ihp = isp->sess_hba;
425 ASSERT(ihp != NULL);
426
427 /*
428 * Stale /dev links can cause us to get floods
429 * of config requests. To prevent these repeated
430 * requests from causing unneeded login to the
431 * unreachable target, we won't try it during
432 * the delay.
433 */
434 if (ddi_get_lbolt() < isp->sess_failure_lbolt +
435 SEC_TO_TICK(isp->sess_storm_delay)) {
436 return;
437 }
438
439 /*
440 * Perform a crude version of round robin to
441 * determine which connection to use for
442 * this session. Since byte 5 in session ID
443 * is overridden for full feature session,
444 * the connection to be selected depends on
445 * the result of sess_isid[5] devided by the
446 * next connection ID.
447 * If MS/T is enabled and there are multiple
448 * IPs are available on the target, we can
449 * select different IPs to connect in this
450 * way.
451 */
452 icp = isp->sess_conn_act;
453 if (icp == NULL) {
454 icp = isp->sess_conn_list;
455 for (idx = 0; idx < (isp->sess_isid[5] %
456 isp->sess_conn_next_cid); idx++) {
457 ASSERT(icp->conn_next != NULL);
458 icp = icp->conn_next;
459 }
460 isp->sess_conn_act = icp;
461 }
462
463 if (icp == NULL) {
464 cmn_err(CE_NOTE, "iscsi session(%d) - "
465 "no connection assigned", isp->sess_oid);
466 return;
467 }
468
469 /*
470 * If connection is in free state, start
471 * login. If already logged in, try to
472 * re-enumerate LUs on the session.
473 */
474 mutex_enter(&icp->conn_state_mutex);
475 if (icp->conn_state == ISCSI_CONN_STATE_FREE) {
476 /*
477 * attempt to login into the first connection in our connection
478 * list. If this fails, we will try the next connection
479 * in our list until end of the list.
480 */
481 while (icp != NULL) {
482 if (iscsi_conn_online(icp) == ISCSI_STATUS_SUCCESS) {
483 mutex_exit(&icp->conn_state_mutex);
484 break;
485 } else {
486 mutex_exit(&icp->conn_state_mutex);
487 icp = icp->conn_next;
488 if (icp != NULL) {
489 mutex_enter(&icp->conn_state_mutex);
490 }
491 }
492 }
493 isp->sess_conn_act = icp;
494 if (icp == NULL) {
495 /* the target for this session is unreachable */
496 isp->sess_failure_lbolt = ddi_get_lbolt();
497 if (isp->sess_storm_delay == 0) {
498 isp->sess_storm_delay++;
499 } else {
500
501 if ((isp->sess_storm_delay * 2) <
502 iscsi_sess_max_delay) {
503 isp->sess_storm_delay =
504 isp->sess_storm_delay * 2;
505 } else {
506 isp->sess_storm_delay =
507 iscsi_sess_max_delay;
508 }
509 }
510
511 } else {
512 isp->sess_storm_delay = 0;
513 isp->sess_failure_lbolt = 0;
514 }
515 } else if (icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN) {
516 mutex_exit(&icp->conn_state_mutex);
517 event_count = atomic_inc_32_nv(&isp->sess_state_event_count);
518 iscsi_sess_enter_state_zone(isp);
519 iscsi_sess_state_machine(isp,
520 ISCSI_SESS_EVENT_N1, event_count);
521 iscsi_sess_exit_state_zone(isp);
522 } else {
523 mutex_exit(&icp->conn_state_mutex);
524 }
525 }
526
527 /*
528 * iscsi_sess_destroy - Destroys a iscsi session structure
529 * and de-associates it from the hba.
530 */
531 iscsi_status_t
iscsi_sess_destroy(iscsi_sess_t * isp)532 iscsi_sess_destroy(iscsi_sess_t *isp)
533 {
534 iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
535 iscsi_status_t tmprval = ISCSI_STATUS_SUCCESS;
536 iscsi_hba_t *ihp;
537 iscsi_sess_t *t_isp;
538 iscsi_lun_t *ilp;
539 iscsi_conn_t *icp;
540
541 ASSERT(isp != NULL);
542 ihp = isp->sess_hba;
543 ASSERT(ihp != NULL);
544
545 /*
546 * The first step in tearing down the session
547 * has to be offlining all the LUNs. This will
548 * ensure there is no outstanding IO by upper
549 * level drivers. If this fails then we are
550 * unable to destroy the session.
551 *
552 * Try all luns and continue upon failure
553 * to remove what is removable before returning
554 * the last error.
555 */
556 rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
557 ilp = isp->sess_lun_list;
558 while (ilp != NULL) {
559 iscsi_lun_t *ilp_next = ilp->lun_next;
560
561 tmprval = iscsi_lun_destroy(ihp, ilp);
562 if (!ISCSI_SUCCESS(tmprval)) {
563 rval = tmprval;
564 }
565 ilp = ilp_next;
566 }
567 rw_exit(&isp->sess_lun_list_rwlock);
568
569 if (!ISCSI_SUCCESS(rval)) {
570 return (rval);
571 }
572
573 /* The next step is to logout of the connections. */
574 rw_enter(&isp->sess_conn_list_rwlock, RW_WRITER);
575 icp = isp->sess_conn_list;
576 while (icp != NULL) {
577 rval = iscsi_conn_offline(icp);
578 if (ISCSI_SUCCESS(rval)) {
579 /* Succes, Continue processing... */
580 icp = icp->conn_next;
581 } else {
582 /* Failure, Stop processing... */
583 rw_exit(&isp->sess_conn_list_rwlock);
584 return (rval);
585 }
586 }
587 rw_exit(&isp->sess_conn_list_rwlock);
588
589 /*
590 * At this point all connections should be in
591 * a FREE state which will have pushed the session
592 * to a FREE state.
593 */
594 ASSERT(isp->sess_state == ISCSI_SESS_STATE_FREE ||
595 isp->sess_state == ISCSI_SESS_STATE_FAILED);
596
597 /* Stop watchdog before destroying connections */
598 if (isp->sess_wd_thread) {
599 iscsi_thread_destroy(isp->sess_wd_thread);
600 isp->sess_wd_thread = NULL;
601 }
602
603 /* Destroy connections */
604 rw_enter(&isp->sess_conn_list_rwlock, RW_WRITER);
605 icp = isp->sess_conn_list;
606 while (icp != NULL) {
607 rval = iscsi_conn_destroy(icp);
608 if (!ISCSI_SUCCESS(rval)) {
609 rw_exit(&isp->sess_conn_list_rwlock);
610 return (rval);
611 }
612 icp = isp->sess_conn_list;
613 }
614 rw_exit(&isp->sess_conn_list_rwlock);
615
616 /* Destroy Session ic thread */
617 if (isp->sess_ic_thread != NULL) {
618 iscsi_thread_destroy(isp->sess_ic_thread);
619 isp->sess_ic_thread = NULL;
620 }
621
622 /* Destroy session task queue */
623 ddi_taskq_destroy(isp->sess_enum_taskq);
624 ddi_taskq_destroy(isp->sess_login_taskq);
625
626 /* destroy pending and completion queues */
627 iscsi_destroy_queue(&isp->sess_queue_pending);
628 iscsi_destroy_queue(&isp->sess_queue_completion);
629
630 /* Remove session from ihp */
631 if (ihp->hba_sess_list == isp) {
632 /* session first item in list */
633 ihp->hba_sess_list = isp->sess_next;
634 } else {
635 /*
636 * search hba list for isp pointing
637 * to session being removed. Then
638 * update that sessions next pointer.
639 */
640 t_isp = ihp->hba_sess_list;
641 while (t_isp->sess_next != NULL) {
642 if (t_isp->sess_next == isp) {
643 break;
644 }
645 t_isp = t_isp->sess_next;
646 }
647 if (t_isp->sess_next == isp) {
648 t_isp->sess_next = isp->sess_next;
649 } else {
650 /* couldn't find session */
651 ASSERT(FALSE);
652 }
653 }
654
655 if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
656 /* Wait for all enum requests complete */
657 mutex_enter(&isp->sess_enum_lock);
658 while (isp->sess_enum_result_count > 0) {
659 cv_wait(&isp->sess_enum_cv, &isp->sess_enum_lock);
660 }
661 mutex_exit(&isp->sess_enum_lock);
662 }
663
664 /* Destroy this Sessions Data */
665 (void) iscsi_sess_kstat_term(isp);
666 rw_destroy(&isp->sess_lun_list_rwlock);
667 rw_destroy(&isp->sess_conn_list_rwlock);
668 mutex_destroy(&isp->sess_cmdsn_mutex);
669 rw_destroy(&isp->sess_state_rwlock);
670 mutex_destroy(&isp->sess_reset_mutex);
671 if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
672 mutex_destroy(&isp->sess_enum_lock);
673 cv_destroy(&isp->sess_enum_cv);
674 }
675 mutex_destroy(&isp->sess_state_wmutex);
676 cv_destroy(&isp->sess_state_wcv);
677 kmem_free(isp, sizeof (iscsi_sess_t));
678 return (rval);
679 }
680
681 extern ib_boot_prop_t *iscsiboot_prop;
682 /*
683 * static iscsi_sess_set_auth -
684 *
685 */
686 boolean_t
iscsi_sess_set_auth(iscsi_sess_t * isp)687 iscsi_sess_set_auth(iscsi_sess_t *isp)
688 {
689 char *init_name;
690 iscsi_chap_props_t *chap = NULL;
691 iscsi_auth_props_t *auth = NULL;
692 uchar_t *tmp = NULL;
693
694 if (isp == (iscsi_sess_t *)NULL) {
695 return (B_FALSE);
696 }
697
698 /* Obtain initiator's name */
699 if (isp->sess_hba == (iscsi_hba_t *)NULL) {
700 return (B_FALSE);
701 }
702
703 init_name = (char *)isp->sess_hba->hba_name;
704
705 /* Zero out the session authentication structure */
706 bzero(&isp->sess_auth, sizeof (iscsi_auth_t));
707
708 if (isp->sess_boot == B_FALSE) {
709
710 auth = (iscsi_auth_props_t *)kmem_zalloc
711 (sizeof (iscsi_auth_props_t), KM_SLEEP);
712 /* Obtain target's authentication settings. */
713 if (persistent_auth_get((char *)isp->sess_name, auth)
714 != B_TRUE) {
715 /*
716 * If no target authentication settings found,
717 * try to obtain system wide configuration
718 * (from the initiator).
719 */
720 bzero(auth, sizeof (*auth));
721 if (persistent_auth_get(init_name, auth) != B_TRUE) {
722 bzero(auth, sizeof (*auth));
723 auth->a_auth_method = authMethodNone;
724 }
725
726 /*
727 * We do not support system wide bi-directional
728 * auth flag.
729 */
730 auth->a_bi_auth = B_FALSE;
731 }
732
733 chap = (iscsi_chap_props_t *)kmem_zalloc
734 (sizeof (iscsi_chap_props_t), KM_SLEEP);
735
736 /*
737 * Initialize the target-side chap name to the session name
738 * if no chap settings have been saved for the current session.
739 */
740 if (persistent_chap_get((char *)isp->sess_name, chap)
741 == B_FALSE) {
742 int name_len = strlen((char *)isp->sess_name);
743 bcopy((char *)isp->sess_name, chap->c_user, name_len);
744 chap->c_user_len = name_len;
745 (void) (persistent_chap_set((char *)isp->sess_name,
746 chap));
747 bzero(chap, sizeof (*chap));
748 }
749
750 if (auth->a_auth_method & authMethodCHAP) {
751 /* Obtain initiator's CHAP settings. */
752 if (persistent_chap_get(init_name, chap) == B_FALSE) {
753 /* No initiator secret defined. */
754 kmem_free(chap, sizeof (iscsi_chap_props_t));
755 /* Set authentication method to NONE */
756 isp->sess_auth.password_length = 0;
757 kmem_free(auth, sizeof (iscsi_auth_props_t));
758 return (B_FALSE);
759 }
760
761 bcopy(chap->c_user, isp->sess_auth.username,
762 sizeof (chap->c_user));
763 bcopy(chap->c_secret, isp->sess_auth.password,
764 sizeof (chap->c_secret));
765 isp->sess_auth.password_length = chap->c_secret_len;
766 } else {
767 /* Set authentication method to NONE */
768 isp->sess_auth.password_length = 0;
769 }
770
771 /*
772 * Consider enabling bidirectional authentication only if
773 * authentication method is not NONE.
774 */
775 if (auth->a_auth_method & authMethodCHAP &&
776 auth->a_bi_auth == B_TRUE) {
777 /* Enable bi-directional authentication. */
778 isp->sess_auth.bidirectional_auth = 1;
779
780 bzero(chap, sizeof (*chap));
781 /* Obtain target's CHAP settings. */
782 if (persistent_chap_get((char *)isp->sess_name, chap)
783 == B_TRUE) {
784 bcopy(chap->c_secret,
785 isp->sess_auth.password_in,
786 sizeof (chap->c_secret));
787 bcopy(chap->c_user, isp->sess_auth.username_in,
788 strlen((char *)chap->c_user));
789 isp->sess_auth.password_length_in =
790 chap->c_secret_len;
791 } else {
792 /*
793 * No target secret defined.
794 * RADIUS server should have been enabled.
795 */
796 /* EMPTY */
797 }
798 } else {
799 /* Disable bi-directional authentication */
800 isp->sess_auth.bidirectional_auth = 0;
801 }
802
803 if (auth != NULL) {
804 kmem_free(auth, sizeof (iscsi_auth_props_t));
805 }
806 if (chap != NULL) {
807 kmem_free(chap, sizeof (iscsi_chap_props_t));
808 }
809 } else {
810 /*
811 * This session is boot session. We will use the CHAP and
812 * the user name got from the boot property structure instead
813 * of persistent sotre.
814 */
815 if (iscsiboot_prop == NULL) {
816 return (B_FALSE);
817 }
818
819 if (iscsiboot_prop->boot_init.ini_chap_sec == NULL) {
820 return (B_FALSE);
821 }
822
823 /* CHAP secret */
824 (void) bcopy(iscsiboot_prop->boot_init.ini_chap_sec,
825 isp->sess_auth.password,
826 strlen((char *)iscsiboot_prop->boot_init.ini_chap_sec));
827
828 /*
829 * If chap name is not set,
830 * we will use initiator name instead.
831 */
832 if (iscsiboot_prop->boot_init.ini_chap_name == NULL) {
833 (void) bcopy(init_name, isp->sess_auth.username,
834 strlen(init_name));
835 } else {
836 tmp = iscsiboot_prop->boot_init.ini_chap_name;
837 (void) bcopy(tmp,
838 isp->sess_auth.username, strlen((char *)tmp));
839 }
840
841 isp->sess_auth.password_length =
842 strlen((char *)iscsiboot_prop->boot_init.ini_chap_sec);
843
844 if (iscsiboot_prop->boot_tgt.tgt_chap_sec != NULL) {
845 /*
846 * Bidirectional authentication is required.
847 */
848 tmp = iscsiboot_prop->boot_tgt.tgt_chap_sec;
849 (void) bcopy(tmp,
850 isp->sess_auth.password_in, strlen((char *)tmp));
851
852 /*
853 * If the target's chap name is not set, we will use
854 * session name instead.
855 */
856 if (iscsiboot_prop->boot_tgt.tgt_chap_name == NULL) {
857 (void) bcopy(isp->sess_name,
858 isp->sess_auth.username_in,
859 isp->sess_name_length);
860 } else {
861 tmp = iscsiboot_prop->boot_tgt.tgt_chap_name;
862 (void) bcopy(tmp,
863 isp->sess_auth.username_in,
864 strlen((char *)tmp));
865 }
866 tmp = iscsiboot_prop->boot_tgt.tgt_chap_sec;
867 isp->sess_auth.password_length_in =
868 strlen((char *)tmp);
869 isp->sess_auth.bidirectional_auth = 1;
870 }
871 }
872
873 /* Set up authentication buffers only if configured */
874 if ((isp->sess_auth.password_length != 0) ||
875 (isp->sess_auth.password_length_in != 0)) {
876 isp->sess_auth.num_auth_buffers = 5;
877 isp->sess_auth.auth_buffers[0].address =
878 &(isp->sess_auth.auth_client_block);
879 isp->sess_auth.auth_buffers[0].length =
880 sizeof (isp->sess_auth.auth_client_block);
881 isp->sess_auth.auth_buffers[1].address =
882 &(isp->sess_auth.auth_recv_string_block);
883 isp->sess_auth.auth_buffers[1].length =
884 sizeof (isp->sess_auth.auth_recv_string_block);
885 isp->sess_auth.auth_buffers[2].address =
886 &(isp->sess_auth.auth_send_string_block);
887 isp->sess_auth.auth_buffers[2].length =
888 sizeof (isp->sess_auth.auth_send_string_block);
889 isp->sess_auth.auth_buffers[3].address =
890 &(isp->sess_auth.auth_recv_binary_block);
891 isp->sess_auth.auth_buffers[3].length =
892 sizeof (isp->sess_auth.auth_recv_binary_block);
893 isp->sess_auth.auth_buffers[4].address =
894 &(isp->sess_auth.auth_send_binary_block);
895 isp->sess_auth.auth_buffers[4].length =
896 sizeof (isp->sess_auth.auth_send_binary_block);
897 }
898
899 return (B_TRUE);
900 }
901
902 /*
903 * iscsi_sess_reserve_itt - Used to reserve an ITT hash slot
904 */
905 iscsi_status_t
iscsi_sess_reserve_scsi_itt(iscsi_cmd_t * icmdp)906 iscsi_sess_reserve_scsi_itt(iscsi_cmd_t *icmdp)
907 {
908 idm_task_t *itp;
909 iscsi_conn_t *icp = icmdp->cmd_conn;
910 itp = idm_task_alloc(icp->conn_ic);
911 if (itp == NULL)
912 return (ISCSI_STATUS_INTERNAL_ERROR);
913 itp->idt_private = icmdp;
914 icmdp->cmd_itp = itp;
915 icmdp->cmd_itt = itp->idt_tt;
916 return (ISCSI_STATUS_SUCCESS);
917 }
918
919 /*
920 * iscsi_sess_release_scsi_itt - Used to release ITT hash slot
921 */
922 void
iscsi_sess_release_scsi_itt(iscsi_cmd_t * icmdp)923 iscsi_sess_release_scsi_itt(iscsi_cmd_t *icmdp)
924 {
925 idm_task_free(icmdp->cmd_itp);
926 }
927
928 /*
929 * iscsi_sess_reserve_itt - Used to reserve an ITT hash slot
930 */
931 iscsi_status_t
iscsi_sess_reserve_itt(iscsi_sess_t * isp,iscsi_cmd_t * icmdp)932 iscsi_sess_reserve_itt(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
933 {
934 /* If no more slots are open fail reservation */
935 if (isp->sess_cmd_table_count >= ISCSI_CMD_TABLE_SIZE) {
936 return (ISCSI_STATUS_ITT_TABLE_FULL);
937 }
938
939 /*
940 * Keep itt values out of the range used by IDM
941 */
942 if (isp->sess_itt < IDM_TASKIDS_MAX)
943 isp->sess_itt = IDM_TASKIDS_MAX;
944
945 /*
946 * Find the next available slot. Normally its the
947 * slot pointed to by the session's sess_itt value.
948 * If this is not true the table has become fragmented.
949 * Fragmentation can occur during max loads and IOs
950 * are completed out of order. Defragmentation will
951 * occur when IO slows down and ITT slots are released.
952 */
953 while (isp->sess_cmd_table[isp->sess_itt %
954 ISCSI_CMD_TABLE_SIZE] != NULL) {
955 isp->sess_itt++;
956 }
957
958 /* reserve slot and update counters */
959 icmdp->cmd_itt = isp->sess_itt;
960 isp->sess_cmd_table[isp->sess_itt %
961 ISCSI_CMD_TABLE_SIZE] = icmdp;
962 isp->sess_cmd_table_count++;
963 isp->sess_itt++;
964
965 return (ISCSI_STATUS_SUCCESS);
966 }
967
968 /*
969 * iscsi_sess_release_itt - Used to release ITT hash slot
970 */
971 void
iscsi_sess_release_itt(iscsi_sess_t * isp,iscsi_cmd_t * icmdp)972 iscsi_sess_release_itt(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
973 {
974 int hash_index = (icmdp->cmd_itt % ISCSI_CMD_TABLE_SIZE);
975
976 ASSERT(isp->sess_cmd_table[hash_index] != NULL);
977
978 /* release slot and update counters */
979 isp->sess_cmd_table[hash_index] = NULL;
980 isp->sess_cmd_table_count--;
981 }
982
983 /*
984 * iscsi_sess_redrive_io - Used to redrive IO on connections in
985 * a full feature state.
986 */
987 void
iscsi_sess_redrive_io(iscsi_sess_t * isp)988 iscsi_sess_redrive_io(iscsi_sess_t *isp)
989 {
990 iscsi_conn_t *icp;
991
992 ASSERT(isp != NULL);
993
994 icp = isp->sess_conn_list;
995 while (icp != NULL) {
996 if (ISCSI_CONN_STATE_FULL_FEATURE(
997 icp->conn_state)) {
998 (void) iscsi_thread_send_wakeup(
999 icp->conn_tx_thread);
1000 }
1001 icp = icp->conn_next;
1002 }
1003 }
1004
1005 /*
1006 * iscsi_sess_state_machine -
1007 *
1008 * 7.3.1 Session State Diagram for an Initiator
1009 *
1010 * Symbolic Names for States:
1011 * Q1: FREE - State on instantiation of after cleanup
1012 * Q3: LOGGED_IN - Waiting for all session events.
1013 * Q4: FAILED - Waiting for session recovery or session cont.
1014 * Q5: IN_FLUSH - A login parameter has changed. We are in the
1015 * process of flushing active, aborting, and
1016 * completed queues. Once flushed the iscsi_ic_thread()
1017 * will drop of drop connections (T14) and reconnect
1018 * to the target with new values.
1019 * Q6: FLUSHED - Active, Aborting and Completed Queues flushed.
1020 * Awaiting reconnect or failure. iscsi_tx/ic_threads
1021 * are still running and might be timing-out IOs.
1022 * State Q3/4 represent the Full Feature Phase operation of the session.
1023 *
1024 * The state diagram is as follows:
1025 *
1026 * ------ (N5/6/7 == NOOP)
1027 * / Q1 \
1028 * +------------------------->\ /<-------------+
1029 * | ---+--- |
1030 * | N5 |N1 |
1031 * | +------+ +-------------+ | |
1032 * | | V V | V |
1033 * | | ----+-- -----+ |
1034 * |N6|N5/7 / Q4 \ / Q3 \(N6 == NOOP) |
1035 * +--+-----\ /----+--->\ /-----+---------+
1036 * | ------- /N1 -+---- | N3|
1037 * | (N7 == NOOP) / N7| ^ N1/3/5| |
1038 * | / | +-------+ |
1039 * | +-------+ / | |
1040 * | | V / v |
1041 * | | ------- -+---- |
1042 * |N6|N6 / Q6 \ N5 / Q5 \ |
1043 * +--+-----\ /<--------\ /-----+---------+
1044 * ------- ------ | N3
1045 * (N7 == NOOP) ^ N1/3/5|
1046 * +-------+
1047 *
1048 * The state transition table is as follows:
1049 *
1050 * +------+------+----+--------+----+
1051 * |Q1 |Q3 |Q4 |Q5 |Q6 |
1052 * -----+------+------+----+--------+----+
1053 * Q1 |N5/6/7|N1 | - | | |
1054 * -----+------+------+----+--------+----+
1055 * Q3 |N3 |N1/3/5|N5 |N7 | |
1056 * -----+------+------+----+--------+----+
1057 * Q4 |N6 |N1 |N5/7| | |
1058 * -----+------+------+----+--------+----+
1059 * Q5 |N3 | | |N1/3/5/7|N6 |
1060 * -----+------+------+----+--------+----+
1061 * Q6 |N6 |N1 |N6/7| | |
1062 * -----+------+------+----+--------+----+
1063 *
1064 * Event definitions:
1065 *
1066 * -N1: A connection logged in
1067 * -N3: A connection logged out
1068 * -N5: A connection failed
1069 * -N6: Session state timeout occurred, or a session
1070 * reinstatement cleared this session instance. This results in
1071 * the freeing of all associated resources and the session state
1072 * is discarded.
1073 * -N7: Login parameters for session have changed.
1074 * Re-negeotation required.
1075 *
1076 * Any caller to the state machine (and so as a state writer) must
1077 * enter the state zone before calling this function, and vice versa
1078 * any caller that doesn't change the state machine shouldn't enter
1079 * the zone, and should act as a reader for a better performance.
1080 *
1081 * The handler of state transition shouldn't try to enter the state
1082 * zone in the same thread or dead lock will occur.
1083 */
1084 void
iscsi_sess_state_machine(iscsi_sess_t * isp,iscsi_sess_event_t event,uint32_t event_count)1085 iscsi_sess_state_machine(iscsi_sess_t *isp, iscsi_sess_event_t event,
1086 uint32_t event_count)
1087 {
1088 ASSERT(isp != NULL);
1089 ASSERT(rw_read_locked(&isp->sess_state_rwlock) == 0);
1090
1091 DTRACE_PROBE3(event, iscsi_sess_t *, isp,
1092 char *, iscsi_sess_state_str(isp->sess_state),
1093 char *, iscsi_sess_event_str(event));
1094
1095 /* Audit event */
1096 idm_sm_audit_event(&isp->sess_state_audit,
1097 SAS_ISCSI_SESS, isp->sess_state, event, 0);
1098
1099 isp->sess_prev_state = isp->sess_state;
1100 isp->sess_state_lbolt = ddi_get_lbolt();
1101
1102 ISCSI_SESS_LOG(CE_NOTE,
1103 "DEBUG: sess_state: isp: %p state: %d event: %d event count: %d",
1104 (void *)isp, isp->sess_state, event, event_count);
1105 switch (isp->sess_state) {
1106 case ISCSI_SESS_STATE_FREE:
1107 iscsi_sess_state_free(isp, event, event_count);
1108 break;
1109 case ISCSI_SESS_STATE_LOGGED_IN:
1110 iscsi_sess_state_logged_in(isp, event, event_count);
1111 break;
1112 case ISCSI_SESS_STATE_FAILED:
1113 iscsi_sess_state_failed(isp, event, event_count);
1114 break;
1115 case ISCSI_SESS_STATE_IN_FLUSH:
1116 iscsi_sess_state_in_flush(isp, event, event_count);
1117 break;
1118 case ISCSI_SESS_STATE_FLUSHED:
1119 iscsi_sess_state_flushed(isp, event, event_count);
1120 break;
1121 default:
1122 ASSERT(FALSE);
1123 }
1124
1125 /* Audit state change */
1126 if (isp->sess_prev_state != isp->sess_state) {
1127 idm_sm_audit_state_change(&isp->sess_state_audit,
1128 SAS_ISCSI_SESS, isp->sess_prev_state, isp->sess_state);
1129 }
1130 }
1131
1132
1133 /*
1134 * iscsi_sess_state_str -
1135 *
1136 */
1137 char *
iscsi_sess_state_str(iscsi_sess_state_t state)1138 iscsi_sess_state_str(iscsi_sess_state_t state)
1139 {
1140 switch (state) {
1141 case ISCSI_SESS_STATE_FREE:
1142 return ("free");
1143 case ISCSI_SESS_STATE_LOGGED_IN:
1144 return ("logged_in");
1145 case ISCSI_SESS_STATE_FAILED:
1146 return ("failed");
1147 case ISCSI_SESS_STATE_IN_FLUSH:
1148 return ("in_flush");
1149 case ISCSI_SESS_STATE_FLUSHED:
1150 return ("flushed");
1151 default:
1152 return ("unknown");
1153 }
1154 }
1155
1156
1157 /*
1158 * +--------------------------------------------------------------------+
1159 * | Internal Session Interfaces |
1160 * +--------------------------------------------------------------------+
1161 */
1162
1163
1164 /*
1165 * iscsi_sess_state_free -
1166 *
1167 */
1168 static void
iscsi_sess_state_free(iscsi_sess_t * isp,iscsi_sess_event_t event,uint32_t event_count)1169 iscsi_sess_state_free(iscsi_sess_t *isp, iscsi_sess_event_t event,
1170 uint32_t event_count)
1171 {
1172 iscsi_hba_t *ihp;
1173 iscsi_enum_result_t enum_result;
1174
1175 ASSERT(isp != NULL);
1176 ihp = isp->sess_hba;
1177 ASSERT(ihp != NULL);
1178 ASSERT(isp->sess_state == ISCSI_SESS_STATE_FREE);
1179
1180 /* switch on event change */
1181 switch (event) {
1182 /*
1183 * -N1: A connection logged in
1184 */
1185 case ISCSI_SESS_EVENT_N1:
1186 isp->sess_state = ISCSI_SESS_STATE_LOGGED_IN;
1187 rw_downgrade(&isp->sess_state_rwlock);
1188 if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
1189 cmn_err(CE_NOTE,
1190 "!iscsi session(%u) %s online\n",
1191 isp->sess_oid, isp->sess_name);
1192 enum_result =
1193 iscsi_sess_enum_request(isp, B_TRUE,
1194 event_count);
1195 if (enum_result == ISCSI_SESS_ENUM_SUBMITTED) {
1196 enum_result =
1197 iscsi_sess_enum_query(isp);
1198 }
1199 if (enum_result != ISCSI_SESS_ENUM_COMPLETE) {
1200 iscsi_sess_enum_warn(isp, enum_result);
1201 }
1202 }
1203 break;
1204
1205 /*
1206 * -N5: A connection failed
1207 */
1208 case ISCSI_SESS_EVENT_N5:
1209 /* NOOP - not connected */
1210 break;
1211
1212 /*
1213 * -N6: Session state timeout occurred, or a session
1214 * reinstatement cleared this session instance. This results in
1215 * the freeing of all associated resources and the session state
1216 * is discarded.
1217 */
1218 case ISCSI_SESS_EVENT_N6:
1219 /* FALLTHRU */
1220
1221 /*
1222 * -N7: Login parameters for session have changed.
1223 * Re-negeotation required.
1224 */
1225 case ISCSI_SESS_EVENT_N7:
1226 /* NOOP - not connected */
1227 break;
1228
1229 /* All other events are invalid for this state */
1230 default:
1231 ASSERT(FALSE);
1232 }
1233 }
1234
1235
1236 /*
1237 * iscsi_sess_logged_in -
1238 *
1239 */
1240 static void
iscsi_sess_state_logged_in(iscsi_sess_t * isp,iscsi_sess_event_t event,uint32_t event_count)1241 iscsi_sess_state_logged_in(iscsi_sess_t *isp, iscsi_sess_event_t event,
1242 uint32_t event_count)
1243 {
1244 iscsi_enum_result_t enum_result;
1245
1246 ASSERT(isp != NULL);
1247 ASSERT(isp->sess_state == ISCSI_SESS_STATE_LOGGED_IN);
1248
1249 /* switch on event change */
1250 switch (event) {
1251 /*
1252 * -N1: At least one transport connection reached the
1253 * LOGGED_IN state
1254 */
1255 case ISCSI_SESS_EVENT_N1:
1256 /*
1257 * A different connection already logged in. If the
1258 * session is NORMAL, just re-enumerate the session.
1259 */
1260 if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
1261 rw_downgrade(&isp->sess_state_rwlock);
1262 enum_result =
1263 iscsi_sess_enum_request(isp, B_TRUE, event_count);
1264 if (enum_result == ISCSI_SESS_ENUM_SUBMITTED) {
1265 enum_result = iscsi_sess_enum_query(isp);
1266 }
1267 if (enum_result != ISCSI_SESS_ENUM_COMPLETE) {
1268 iscsi_sess_enum_warn(isp, enum_result);
1269 }
1270 }
1271 break;
1272
1273 /*
1274 * -N3: A connection logged out.
1275 */
1276 case ISCSI_SESS_EVENT_N3:
1277 /* FALLTHRU */
1278
1279 /*
1280 * -N5: A connection failed
1281 */
1282 case ISCSI_SESS_EVENT_N5:
1283 /*
1284 * MC/S: If this is the last connection to
1285 * fail then move the the failed state.
1286 */
1287 if (event == ISCSI_SESS_EVENT_N3) {
1288 isp->sess_state = ISCSI_SESS_STATE_FREE;
1289 } else {
1290 isp->sess_state = ISCSI_SESS_STATE_FAILED;
1291 }
1292 rw_downgrade(&isp->sess_state_rwlock);
1293
1294 /* no longer connected reset nego tpgt */
1295 isp->sess_tpgt_nego = ISCSI_DEFAULT_TPGT;
1296
1297 iscsi_sess_flush(isp);
1298
1299 if (event == ISCSI_SESS_EVENT_N3) {
1300 if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
1301 cmn_err(CE_NOTE,
1302 "!iscsi session(%u) %s offline\n",
1303 isp->sess_oid, isp->sess_name);
1304 }
1305 /*
1306 * During the process of offlining the LUNs
1307 * our ic thread might be calling back into
1308 * the driver via a target driver failure
1309 * path to do a reset or something
1310 * we need to release the sess_state_mutex
1311 * while we are killing these threads so
1312 * they don't get deadlocked.
1313 */
1314 iscsi_sess_offline_luns(isp);
1315 }
1316
1317 mutex_enter(&isp->sess_reset_mutex);
1318 isp->sess_reset_in_progress = B_FALSE;
1319 mutex_exit(&isp->sess_reset_mutex);
1320 /* update busy luns if needed */
1321 iscsi_sess_update_busy_luns(isp, B_TRUE);
1322
1323 break;
1324
1325 /*
1326 * -N6: Session state timeout occurred, or a session
1327 * reinstatement cleared this session instance. This results in
1328 * the freeing of all associated resources and the session state
1329 * is discarded.
1330 */
1331 case ISCSI_SESS_EVENT_N6:
1332 /* NOOP - Not last connection */
1333 break;
1334
1335 /*
1336 * -N7: Login parameters for session have changed.
1337 * Re-negeotation required.
1338 */
1339 case ISCSI_SESS_EVENT_N7:
1340 isp->sess_state = ISCSI_SESS_STATE_IN_FLUSH;
1341 break;
1342
1343 /* All other events are invalid for this state */
1344 default:
1345 ASSERT(FALSE);
1346 }
1347 }
1348
1349
1350 /*
1351 * iscsi_sess_state_failed -
1352 *
1353 */
1354 static void
iscsi_sess_state_failed(iscsi_sess_t * isp,iscsi_sess_event_t event,uint32_t event_count)1355 iscsi_sess_state_failed(iscsi_sess_t *isp, iscsi_sess_event_t event,
1356 uint32_t event_count)
1357 {
1358 iscsi_hba_t *ihp;
1359 iscsi_enum_result_t enum_result;
1360
1361 ASSERT(isp != NULL);
1362 ihp = isp->sess_hba;
1363 ASSERT(ihp != NULL);
1364 ASSERT(isp->sess_state == ISCSI_SESS_STATE_FAILED);
1365
1366 /* switch on event change */
1367 switch (event) {
1368 /* -N1: A session continuation attempt succeeded */
1369 case ISCSI_SESS_EVENT_N1:
1370 isp->sess_state = ISCSI_SESS_STATE_LOGGED_IN;
1371 rw_downgrade(&isp->sess_state_rwlock);
1372 if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
1373 enum_result =
1374 iscsi_sess_enum_request(isp, B_TRUE,
1375 event_count);
1376 if (enum_result == ISCSI_SESS_ENUM_SUBMITTED) {
1377 enum_result =
1378 iscsi_sess_enum_query(isp);
1379 }
1380 if (enum_result != ISCSI_SESS_ENUM_COMPLETE) {
1381 iscsi_sess_enum_warn(isp, enum_result);
1382 }
1383 }
1384 break;
1385
1386 /*
1387 * -N5: A connection failed
1388 */
1389 case ISCSI_SESS_EVENT_N5:
1390 /* NOOP - not connected */
1391 break;
1392
1393 /*
1394 * -N6: Session state timeout occurred, or a session
1395 * reinstatement cleared this session instance. This results in
1396 * the freeing of all associated resources and the session state
1397 * is discarded.
1398 */
1399 case ISCSI_SESS_EVENT_N6:
1400 isp->sess_state = ISCSI_SESS_STATE_FREE;
1401
1402 if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
1403 cmn_err(CE_NOTE, "!iscsi session(%u) %s offline\n",
1404 isp->sess_oid, isp->sess_name);
1405 }
1406
1407 rw_downgrade(&isp->sess_state_rwlock);
1408 iscsi_sess_offline_luns(isp);
1409 break;
1410
1411 /*
1412 * -N7: Login parameters for session have changed.
1413 * Re-negeotation required.
1414 */
1415 case ISCSI_SESS_EVENT_N7:
1416 /* NOOP - not connected */
1417 break;
1418
1419 /* All other events are invalid for this state */
1420 default:
1421 ASSERT(FALSE);
1422 }
1423 }
1424
1425 /*
1426 * iscsi_sess_state_in_flush -
1427 *
1428 */
1429 /* ARGSUSED */
1430 static void
iscsi_sess_state_in_flush(iscsi_sess_t * isp,iscsi_sess_event_t event,uint32_t event_count)1431 iscsi_sess_state_in_flush(iscsi_sess_t *isp, iscsi_sess_event_t event,
1432 uint32_t event_count)
1433 {
1434 ASSERT(isp != NULL);
1435 ASSERT(isp->sess_state == ISCSI_SESS_STATE_IN_FLUSH);
1436
1437 /* switch on event change */
1438 switch (event) {
1439 /* -N1: A session continuation attempt succeeded */
1440 case ISCSI_SESS_EVENT_N1:
1441 /* NOOP - connections already online */
1442 break;
1443
1444 /*
1445 * -N3: A connection logged out.
1446 */
1447 case ISCSI_SESS_EVENT_N3:
1448 /* FALLTHRU */
1449
1450 /*
1451 * -N5: A connection failed
1452 */
1453 case ISCSI_SESS_EVENT_N5:
1454 /*
1455 * MC/S: If this is the last connection to
1456 * fail then move the the failed state.
1457 */
1458 if (event == ISCSI_SESS_EVENT_N3) {
1459 isp->sess_state = ISCSI_SESS_STATE_FREE;
1460 } else {
1461 isp->sess_state = ISCSI_SESS_STATE_FLUSHED;
1462 }
1463 rw_downgrade(&isp->sess_state_rwlock);
1464
1465 /* no longer connected reset nego tpgt */
1466 isp->sess_tpgt_nego = ISCSI_DEFAULT_TPGT;
1467 iscsi_sess_flush(isp);
1468
1469 if (event == ISCSI_SESS_EVENT_N3) {
1470 if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
1471 cmn_err(CE_NOTE,
1472 "!iscsi session(%u) %s offline\n",
1473 isp->sess_oid, isp->sess_name);
1474 }
1475 /*
1476 * During the process of offlining the LUNs
1477 * our ic thread might be calling back into
1478 * the driver via a target driver failure
1479 * path to do a reset or something
1480 * we need to release the sess_state_mutex
1481 * while we are killing these threads so
1482 * they don't get deadlocked.
1483 */
1484 iscsi_sess_offline_luns(isp);
1485 }
1486
1487 mutex_enter(&isp->sess_reset_mutex);
1488 isp->sess_reset_in_progress = B_FALSE;
1489 mutex_exit(&isp->sess_reset_mutex);
1490 /* update busy luns if needed */
1491 iscsi_sess_update_busy_luns(isp, B_TRUE);
1492
1493 break;
1494
1495 /*
1496 * -N6: Session state timeout occurred, or a session
1497 * reinstatement cleared this session instance. This results in
1498 * the freeing of all associated resources and the session state
1499 * is discarded.
1500 */
1501 case ISCSI_SESS_EVENT_N6:
1502 /* NOOP - Not last connection */
1503 break;
1504
1505 /*
1506 * -N7: Login parameters for session have changed.
1507 * Re-negeotation required.
1508 */
1509 case ISCSI_SESS_EVENT_N7:
1510 /* NOOP - Already attempting to update */
1511 break;
1512
1513 /* All other events are invalid for this state */
1514 default:
1515 ASSERT(FALSE);
1516 }
1517 }
1518
1519
1520 /*
1521 * iscsi_sess_state_flushed -
1522 *
1523 */
1524 static void
iscsi_sess_state_flushed(iscsi_sess_t * isp,iscsi_sess_event_t event,uint32_t event_count)1525 iscsi_sess_state_flushed(iscsi_sess_t *isp, iscsi_sess_event_t event,
1526 uint32_t event_count)
1527 {
1528 iscsi_hba_t *ihp;
1529 iscsi_enum_result_t enum_result;
1530
1531 ASSERT(isp != NULL);
1532 ASSERT(isp->sess_state == ISCSI_SESS_STATE_FLUSHED);
1533 ihp = isp->sess_hba;
1534 ASSERT(ihp != NULL);
1535
1536 /* switch on event change */
1537 switch (event) {
1538 /* -N1: A session continuation attempt succeeded */
1539 case ISCSI_SESS_EVENT_N1:
1540 isp->sess_state = ISCSI_SESS_STATE_LOGGED_IN;
1541 rw_downgrade(&isp->sess_state_rwlock);
1542 if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
1543 enum_result =
1544 iscsi_sess_enum_request(isp, B_TRUE,
1545 event_count);
1546 if (enum_result == ISCSI_SESS_ENUM_SUBMITTED) {
1547 enum_result =
1548 iscsi_sess_enum_query(isp);
1549 }
1550 if (enum_result != ISCSI_SESS_ENUM_COMPLETE) {
1551 iscsi_sess_enum_warn(isp, enum_result);
1552 }
1553 }
1554 break;
1555
1556 /*
1557 * -N6: Session state timeout occurred, or a session
1558 * reinstatement cleared this session instance. This results in
1559 * the freeing of all associated resources and the session state
1560 * is discarded.
1561 */
1562 case ISCSI_SESS_EVENT_N6:
1563 isp->sess_state = ISCSI_SESS_STATE_FREE;
1564 rw_downgrade(&isp->sess_state_rwlock);
1565
1566 if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
1567 cmn_err(CE_NOTE, "!iscsi session(%u) %s offline\n",
1568 isp->sess_oid, isp->sess_name);
1569 }
1570
1571 iscsi_sess_offline_luns(isp);
1572 break;
1573
1574 /*
1575 * -N7: Login parameters for session have changed.
1576 * Re-negeotation required.
1577 */
1578 case ISCSI_SESS_EVENT_N7:
1579 /* NOOP - not connected */
1580 break;
1581
1582 /* All other events are invalid for this state */
1583 default:
1584 ASSERT(FALSE);
1585 }
1586 }
1587
1588 /*
1589 * iscsi_sess_event_str -
1590 *
1591 */
1592 static char *
iscsi_sess_event_str(iscsi_sess_event_t event)1593 iscsi_sess_event_str(iscsi_sess_event_t event)
1594 {
1595 switch (event) {
1596 case ISCSI_SESS_EVENT_N1:
1597 return ("N1");
1598 case ISCSI_SESS_EVENT_N3:
1599 return ("N3");
1600 case ISCSI_SESS_EVENT_N5:
1601 return ("N5");
1602 case ISCSI_SESS_EVENT_N6:
1603 return ("N6");
1604 case ISCSI_SESS_EVENT_N7:
1605 return ("N7");
1606 default:
1607 return ("unknown");
1608 }
1609 }
1610
1611 /*
1612 * iscsi_sess_thread_create -
1613 *
1614 */
1615 static iscsi_status_t
iscsi_sess_threads_create(iscsi_sess_t * isp)1616 iscsi_sess_threads_create(iscsi_sess_t *isp)
1617 {
1618 iscsi_hba_t *ihp;
1619 char th_name[ISCSI_TH_MAX_NAME_LEN];
1620
1621 ASSERT(isp != NULL);
1622 ihp = isp->sess_hba;
1623 ASSERT(ihp != NULL);
1624
1625 /* Completion thread creation. */
1626 if (snprintf(th_name, sizeof (th_name) - 1,
1627 ISCSI_SESS_IOTH_NAME_FORMAT, ihp->hba_oid,
1628 isp->sess_oid) >= sizeof (th_name)) {
1629 return (ISCSI_STATUS_INTERNAL_ERROR);
1630 }
1631
1632 isp->sess_ic_thread = iscsi_thread_create(ihp->hba_dip,
1633 th_name, iscsi_ic_thread, isp);
1634
1635 if (isp->sess_ic_thread == NULL) {
1636 return (ISCSI_STATUS_INTERNAL_ERROR);
1637 }
1638
1639 (void) iscsi_thread_start(isp->sess_ic_thread);
1640
1641 return (ISCSI_STATUS_SUCCESS);
1642 }
1643
1644 /*
1645 * iscsi_sess_enumeration - This function is used to drive the enumeration
1646 * of LUs on a session. It will first prepare the target by sending test
1647 * unit ready commands, then it will issue a report luns. If the report
1648 * luns is successful then it will process all the luns in the report.
1649 * If report luns is not successful we will do a stepping enumeration
1650 * of luns until no more luns are found.
1651 */
1652 static void
iscsi_sess_enumeration(void * arg)1653 iscsi_sess_enumeration(void *arg)
1654 {
1655 iscsi_task_t *itp = (iscsi_task_t *)arg;
1656 iscsi_sess_t *isp;
1657 iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
1658 iscsi_enum_result_t enum_result = ISCSI_SESS_ENUM_COMPLETE;
1659 uint32_t event_count = itp->t_event_count;
1660
1661 ASSERT(itp != NULL);
1662 isp = (iscsi_sess_t *)itp->t_arg;
1663 ASSERT(isp != NULL);
1664
1665 /*
1666 * Send initial TEST_UNIT_READY to target. If it fails this we
1667 * stop our enumeration as the target is not responding properly.
1668 */
1669 rval = iscsi_sess_testunitready(isp, event_count);
1670 if (ISCSI_SUCCESS(rval)) {
1671 /*
1672 * Now we know the target is ready start our enumeration with
1673 * REPORT LUNs, If this fails we will have to fall back to
1674 * stepping
1675 */
1676 rval = iscsi_sess_reportluns(isp, event_count);
1677 if (!ISCSI_SUCCESS(rval)) {
1678 /*
1679 * report luns failed so lets just check for LUN 0.
1680 * This will match fcp's enumeration support and
1681 * avoid issues with older devices like the A5K that
1682 * respond poorly.
1683 */
1684 if (isp->sess_lun_list == NULL) {
1685 iscsi_sess_inquiry(isp, 0, 0, event_count,
1686 NULL);
1687 }
1688 }
1689 } else {
1690 enum_result = ISCSI_SESS_ENUM_TUR_FAIL;
1691 }
1692
1693 kmem_free(itp, sizeof (iscsi_task_t));
1694 mutex_enter(&isp->sess_enum_lock);
1695 if (isp->sess_enum_result_count != 0) {
1696 isp->sess_enum_status = ISCSI_SESS_ENUM_DONE;
1697 } else {
1698 isp->sess_enum_status = ISCSI_SESS_ENUM_FREE;
1699 }
1700 isp->sess_enum_result = enum_result;
1701 cv_broadcast(&isp->sess_enum_cv);
1702 mutex_exit(&isp->sess_enum_lock);
1703 }
1704
1705 /*
1706 * iscsi_sess_testunitready - This is used during enumeration to
1707 * ensure an array is ready to be enumerated.
1708 */
1709 static iscsi_status_t
iscsi_sess_testunitready(iscsi_sess_t * isp,uint32_t event_count)1710 iscsi_sess_testunitready(iscsi_sess_t *isp, uint32_t event_count)
1711 {
1712 iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
1713 int retries = 0;
1714 struct uscsi_cmd ucmd;
1715 char cdb[CDB_GROUP0];
1716
1717 ASSERT(isp != NULL);
1718
1719 /* loop until successful sending test unit ready or retries out */
1720 while ((retries++ < 3) &&
1721 (isp->sess_state_event_count == event_count)) {
1722 /* cdb is all zeros */
1723 bzero(&cdb[0], CDB_GROUP0);
1724
1725 /* setup uscsi cmd */
1726 bzero(&ucmd, sizeof (struct uscsi_cmd));
1727 ucmd.uscsi_timeout = iscsi_sess_enum_timeout;
1728 ucmd.uscsi_cdb = &cdb[0];
1729 ucmd.uscsi_cdblen = CDB_GROUP0;
1730
1731 /* send test unit ready to lun zero on this session */
1732 rval = iscsi_handle_passthru(isp, 0, &ucmd);
1733
1734 /*
1735 * If passthru was successful then we were able to
1736 * communicate with the target, continue enumeration.
1737 */
1738 if (ISCSI_SUCCESS(rval)) {
1739 break;
1740 }
1741 }
1742
1743 return (rval);
1744 }
1745
1746 #define SCSI_REPORTLUNS_ADDRESS_SIZE 8
1747 #define SCSI_REPORTLUNS_ADDRESS_MASK 0xC0
1748 #define SCSI_REPORTLUNS_ADDRESS_PERIPHERAL 0x00
1749 #define SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE 0x40
1750 #define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT 0x80
1751 #define SCSI_REPORTLUNS_ADDRESS_EXTENDED_UNIT 0xC0
1752 #define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT_2B 0x00
1753 #define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT_4B 0x01
1754 #define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT_6B 0x10
1755 #define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT_8B 0x20
1756 #define SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT_SIZE 0x30
1757
1758 /*
1759 * iscsi_sess_reportluns - This is used during enumeration to
1760 * ensure an array is ready to be enumerated.
1761 */
1762 static iscsi_status_t
iscsi_sess_reportluns(iscsi_sess_t * isp,uint32_t event_count)1763 iscsi_sess_reportluns(iscsi_sess_t *isp, uint32_t event_count)
1764 {
1765 iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
1766 iscsi_hba_t *ihp;
1767 struct uscsi_cmd ucmd;
1768 unsigned char cdb[CDB_GROUP5];
1769 unsigned char *buf = NULL;
1770 int buf_len = sizeof (struct scsi_inquiry);
1771 uint32_t lun_list_length = 0;
1772 uint16_t lun_num = 0;
1773 uint8_t lun_addr_type = 0;
1774 uint32_t lun_count = 0;
1775 uint32_t lun_start = 0;
1776 uint32_t lun_total = 0;
1777 int retries = 0;
1778 iscsi_lun_t *ilp_next;
1779 iscsi_lun_t *ilp = NULL;
1780 replun_data_t *saved_replun_ptr = NULL;
1781
1782 ASSERT(isp != NULL);
1783 ihp = isp->sess_hba;
1784 ASSERT(ihp != NULL);
1785
1786 /*
1787 * Attempt to send report luns until we successfully
1788 * get all the data or the retries run out.
1789 */
1790 while ((retries++ < 3) &&
1791 (isp->sess_state_event_count == event_count)) {
1792 /*
1793 * Allocate our buffer based on current buf_len.
1794 * buf_len may change after we received a response
1795 * from the target.
1796 */
1797 if (buf == NULL) {
1798 buf = kmem_zalloc(buf_len, KM_SLEEP);
1799 }
1800
1801 /* setup cdb */
1802 bzero(&cdb, CDB_GROUP5);
1803 cdb[0] = SCMD_REPORT_LUNS;
1804 cdb[6] = (buf_len & 0xff000000) >> 24;
1805 cdb[7] = (buf_len & 0x00ff0000) >> 16;
1806 cdb[8] = (buf_len & 0x0000ff00) >> 8;
1807 cdb[9] = (buf_len & 0x000000ff);
1808
1809 /* setup uscsi cmd */
1810 bzero(&ucmd, sizeof (struct uscsi_cmd));
1811 ucmd.uscsi_flags = USCSI_READ;
1812 ucmd.uscsi_timeout = iscsi_sess_enum_timeout;
1813 ucmd.uscsi_cdb = (char *)&cdb[0];
1814 ucmd.uscsi_cdblen = CDB_GROUP5;
1815 ucmd.uscsi_bufaddr = (char *)buf;
1816 ucmd.uscsi_buflen = buf_len;
1817
1818 /* send uscsi cmd to lun 0 on session */
1819 rval = iscsi_handle_passthru(isp, 0, &ucmd);
1820
1821 /* If passthru successful but not scsi status update istatus */
1822 if (ISCSI_SUCCESS(rval) &&
1823 (ucmd.uscsi_status != STATUS_GOOD)) {
1824 rval = ISCSI_STATUS_USCSI_FAILED;
1825 }
1826
1827 /* If successful, check if we have all the data */
1828 if (ISCSI_SUCCESS(rval)) {
1829 /* total data - header (SCSI_REPORTLUNS_ADDRESS_SIZE) */
1830 lun_list_length = htonl(*(uint32_t *)buf);
1831
1832 if (buf_len >= lun_list_length +
1833 SCSI_REPORTLUNS_ADDRESS_SIZE) {
1834 /* we have all the data, were done */
1835 break;
1836 }
1837
1838 /*
1839 * We don't have all the data. free up the
1840 * memory for the next pass and update the
1841 * buf_len
1842 */
1843 kmem_free(buf, buf_len);
1844 buf = NULL;
1845 buf_len = lun_list_length +
1846 SCSI_REPORTLUNS_ADDRESS_SIZE;
1847 } else {
1848 retries++;
1849 }
1850 }
1851
1852 if (isp->sess_state_event_count != event_count) {
1853 if (buf != NULL) {
1854 kmem_free(buf, buf_len);
1855 buf = NULL;
1856 }
1857 return (rval);
1858 }
1859
1860 /* If not successful go no further */
1861 if (!ISCSI_SUCCESS(rval)) {
1862 kmem_free(buf, buf_len);
1863 return (rval);
1864 }
1865
1866 /*
1867 * find out the number of luns returned by the SCSI ReportLun call
1868 * and allocate buffer space
1869 */
1870 lun_total = lun_list_length / SCSI_REPORTLUNS_ADDRESS_SIZE;
1871 saved_replun_ptr = kmem_zalloc(lun_total * sizeof (replun_data_t),
1872 KM_SLEEP);
1873
1874 /*
1875 * walk the isp->sess_lun_list
1876 * for each lun in this list
1877 * look to see if this lun is in the SCSI ReportLun list we
1878 * just retrieved
1879 * if it is in the SCSI ReportLun list and it is already ONLINE or
1880 * if it is in the SCSI ReportLun list and it is OFFLINE or
1881 * if it isn't in the SCSI ReportLunlist or then
1882 * issue the iscsi_sess_inquiry() to handle
1883 *
1884 * as we walk the SCSI ReportLun list, we save this lun information
1885 * into the buffer we just allocated. This will save us from
1886 * having to figure out this information later
1887 */
1888 lun_start = 0;
1889 rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
1890 for (ilp = isp->sess_lun_list; ilp; ilp = ilp_next) {
1891 if (isp->sess_state_event_count != event_count)
1892 break;
1893
1894 ilp_next = ilp->lun_next;
1895
1896 for (lun_count = lun_start; lun_count < lun_total;
1897 lun_count++) {
1898 /*
1899 * if the first lun in saved_replun_ptr buffer has
1900 * already been found we can move on and do not
1901 * have to check this lun in the future
1902 */
1903 if (lun_count == lun_start &&
1904 saved_replun_ptr[lun_start].lun_found) {
1905 lun_start++;
1906 continue;
1907 }
1908 /*
1909 * check to see if the lun we are looking for is in the
1910 * saved_replun_ptr buffer
1911 * if it is, process the lun
1912 * if it isn't, then we must go to SCSI
1913 * Report Lun buffer
1914 * we retrieved to get lun info
1915 */
1916 if ((saved_replun_ptr[lun_count].lun_valid
1917 == B_TRUE) &&
1918 (saved_replun_ptr[lun_count].lun_num
1919 == ilp->lun_num)) {
1920 /*
1921 * the lun we are looking for is found,
1922 * give it to iscsi_sess_inquiry()
1923 */
1924 rw_exit(&isp->sess_lun_list_rwlock);
1925 iscsi_sess_inquiry(isp, ilp->lun_num,
1926 saved_replun_ptr[lun_count].lun_addr_type,
1927 event_count, ilp);
1928 rw_enter(&isp->sess_lun_list_rwlock,
1929 RW_WRITER);
1930 saved_replun_ptr[lun_count].lun_found
1931 = B_TRUE;
1932 break;
1933 } else {
1934 /*
1935 * lun information is not found in the
1936 * saved_replun buffer, retrieve lun
1937 * information from the SCSI Report Lun buffer
1938 * and store this information in the
1939 * saved_replun buffer
1940 */
1941 if (retrieve_lundata(lun_count, buf, isp,
1942 &lun_num, &lun_addr_type) !=
1943 ISCSI_STATUS_SUCCESS) {
1944 continue;
1945 }
1946 saved_replun_ptr[lun_count].lun_valid = B_TRUE;
1947 saved_replun_ptr[lun_count].lun_num = lun_num;
1948 saved_replun_ptr[lun_count].lun_addr_type =
1949 lun_addr_type;
1950 if (ilp->lun_num == lun_num) {
1951 /*
1952 * lun is found in the SCSI Report Lun
1953 * buffer, give it to inquiry
1954 */
1955 rw_exit(&isp->sess_lun_list_rwlock);
1956 iscsi_sess_inquiry(isp, lun_num,
1957 lun_addr_type, event_count, ilp);
1958 rw_enter(&isp->sess_lun_list_rwlock,
1959 RW_WRITER);
1960 saved_replun_ptr[lun_count].lun_found
1961 = B_TRUE;
1962 break;
1963 }
1964 }
1965 }
1966
1967 if (lun_count == lun_total) {
1968 /*
1969 * this lun we found in the sess->lun_list does
1970 * not exist anymore, need to offline this lun
1971 */
1972
1973 DTRACE_PROBE2(
1974 sess_reportluns_lun_no_longer_exists,
1975 int, ilp->lun_num, int, ilp->lun_state);
1976
1977 (void) iscsi_lun_destroy(ihp, ilp);
1978 }
1979 }
1980 rw_exit(&isp->sess_lun_list_rwlock);
1981 /*
1982 * look for new luns that we found in the SCSI Report Lun buffer that
1983 * we did not have in the sess->lun_list and add them into the list
1984 */
1985 for (lun_count = lun_start; lun_count < lun_total; lun_count++) {
1986 if (saved_replun_ptr[lun_count].lun_valid == B_FALSE) {
1987 /*
1988 * lun information is not in the
1989 * saved_replun buffer, retrieve
1990 * it from the SCSI Report Lun buffer
1991 */
1992 if (retrieve_lundata(lun_count, buf, isp,
1993 &lun_num, &lun_addr_type) != ISCSI_STATUS_SUCCESS) {
1994 continue;
1995 }
1996 } else {
1997 /*
1998 * lun information is in the saved_replun buffer
1999 * if this lun has been found already,
2000 * then we can move on
2001 */
2002 if (saved_replun_ptr[lun_count].lun_found == B_TRUE) {
2003 continue;
2004 }
2005 lun_num = saved_replun_ptr[lun_count].lun_num;
2006 lun_addr_type =
2007 saved_replun_ptr[lun_count].lun_addr_type;
2008 }
2009
2010
2011 /* New luns found should not conflict with existing luns */
2012 rw_enter(&isp->sess_lun_list_rwlock, RW_READER);
2013 for (ilp = isp->sess_lun_list; ilp; ilp = ilp->lun_next) {
2014 if (ilp->lun_num == lun_num) {
2015 break;
2016 }
2017 }
2018 rw_exit(&isp->sess_lun_list_rwlock);
2019
2020 if (ilp == NULL) {
2021 /* new lun found, add this lun */
2022 iscsi_sess_inquiry(isp, lun_num, lun_addr_type,
2023 event_count, NULL);
2024 } else {
2025 cmn_err(CE_NOTE,
2026 "!Duplicate Lun Number(%d) recieved from "
2027 "Target(%s)", lun_num, isp->sess_name);
2028 }
2029 }
2030 if (buf != NULL) {
2031 kmem_free(buf, buf_len);
2032 }
2033 kmem_free(saved_replun_ptr, lun_total * sizeof (replun_data_t));
2034
2035 return (rval);
2036 }
2037
2038 #define ISCSI_MAX_INQUIRY_BUF_SIZE 0xFF
2039 #define ISCSI_MAX_INQUIRY_RETRIES 3
2040
2041 /*
2042 * iscsi_sess_inquiry - Final processing of a LUN before we create a tgt
2043 * mapping, if necessary the old lun will be deleted.
2044 *
2045 * We need to collect the stardard inquiry page and the
2046 * vendor identification page for this LUN. If both of these are
2047 * successful and the identification page contains a NAA or EUI type
2048 * we will continue. Otherwise we fail the creation of a tgt for
2049 * this LUN.
2050 *
2051 * Keep the old lun unchanged if it is online and following things are
2052 * match, lun_addr_type, lun_type, and lun_guid.
2053 *
2054 * Online the old lun if it is offline/invalid and those three things
2055 * are match.
2056 *
2057 * Online a new lun if the old lun is offline and any of those three things
2058 * is not match, and needs to destroy the old first.
2059 *
2060 * Destroy the old lun and online the new lun if the old is online/invalid
2061 * and any of those three things is not match, and then online the new lun
2062 */
2063 static void
iscsi_sess_inquiry(iscsi_sess_t * isp,uint16_t lun_num,uint8_t lun_addr_type,uint32_t event_count,iscsi_lun_t * ilp)2064 iscsi_sess_inquiry(iscsi_sess_t *isp, uint16_t lun_num, uint8_t lun_addr_type,
2065 uint32_t event_count, iscsi_lun_t *ilp)
2066 {
2067 iscsi_status_t rval;
2068 struct uscsi_cmd ucmd;
2069 uchar_t cdb[CDB_GROUP0];
2070 uchar_t *inq;
2071 size_t inq_len;
2072 uchar_t *inq83;
2073 size_t inq83_len;
2074 int retries;
2075 ddi_devid_t devid;
2076 char *guid = NULL;
2077 iscsi_hba_t *ihp;
2078 iscsi_status_t status = ISCSI_STATUS_SUCCESS;
2079 boolean_t inq_ready = B_FALSE;
2080 boolean_t inq83_ready = B_FALSE;
2081 boolean_t nochange = B_FALSE;
2082 uchar_t lun_type;
2083
2084 ASSERT(isp != NULL);
2085 ihp = isp->sess_hba;
2086 ASSERT(ihp != NULL);
2087
2088 inq = kmem_zalloc(ISCSI_MAX_INQUIRY_BUF_SIZE, KM_SLEEP);
2089 inq83 = kmem_zalloc(ISCSI_MAX_INQUIRY_BUF_SIZE, KM_SLEEP);
2090
2091 if (ilp == NULL) {
2092 /* easy case, just to create the new lun */
2093 goto sess_inq;
2094 }
2095
2096 if (ilp->lun_addr_type != lun_addr_type) {
2097 goto offline_old;
2098 }
2099
2100 goto sess_inq;
2101
2102 offline_old:
2103 if (isp->sess_state_event_count != event_count) {
2104 goto inq_done;
2105 }
2106
2107 status = iscsi_lun_destroy(ihp, ilp);
2108 if (status != ISCSI_STATUS_SUCCESS) {
2109 /* have to abort the process */
2110 cmn_err(CE_WARN, "iscsi session(%u) is unable to offline"
2111 " obsolete logical unit %d", isp->sess_oid, lun_num);
2112 goto inq_done;
2113 }
2114 ilp = NULL;
2115
2116 sess_inq:
2117 if (inq_ready == B_TRUE) {
2118 goto sess_inq83;
2119 }
2120 /*
2121 * STANDARD INQUIRY - We need the standard inquiry information
2122 * to feed into the scsi_hba_nodename_compatible_get function.
2123 * This function is used to detemine which driver will bind
2124 * on top of us, via the compatible id.
2125 */
2126 bzero(&cdb, CDB_GROUP0);
2127 cdb[0] = SCMD_INQUIRY;
2128 cdb[4] = ISCSI_MAX_INQUIRY_BUF_SIZE;
2129
2130 bzero(&ucmd, sizeof (struct uscsi_cmd));
2131 ucmd.uscsi_flags = USCSI_READ;
2132 ucmd.uscsi_timeout = iscsi_sess_enum_timeout;
2133 ucmd.uscsi_cdb = (char *)&cdb[0];
2134 ucmd.uscsi_cdblen = CDB_GROUP0;
2135 ucmd.uscsi_bufaddr = (char *)inq;
2136 ucmd.uscsi_buflen = ISCSI_MAX_INQUIRY_BUF_SIZE;
2137
2138 /* Attempt to get inquiry information until successful or retries */
2139 retries = 0;
2140 while ((retries++ < ISCSI_MAX_INQUIRY_RETRIES) &&
2141 (isp->sess_state_event_count == event_count)) {
2142 /* issue passthru */
2143 rval = iscsi_handle_passthru(isp, lun_num, &ucmd);
2144
2145 /* If we were successful but scsi stat failed update istatus */
2146 if (ISCSI_SUCCESS(rval) &&
2147 (ucmd.uscsi_status != STATUS_GOOD)) {
2148 rval = ISCSI_STATUS_USCSI_FAILED;
2149 }
2150
2151 /* If successful break */
2152 if (ISCSI_SUCCESS(rval)) {
2153 inq_len = ISCSI_MAX_INQUIRY_BUF_SIZE - ucmd.uscsi_resid;
2154 break;
2155 }
2156
2157 /* loop until we are successful or retries run out */
2158 }
2159
2160 /* If failed don't continue */
2161 if (!ISCSI_SUCCESS(rval)) {
2162 cmn_err(CE_NOTE, "iscsi session(%u) unable to enumerate "
2163 "logical unit - inquiry failed lun %d",
2164 isp->sess_oid, lun_num);
2165
2166 goto inq_done;
2167 }
2168 inq_ready = B_TRUE;
2169
2170 sess_inq83:
2171 /*
2172 * T-10 SPC Section 6.4.2. Standard INQUIRY Peripheral
2173 * qualifier of 000b is the only type we should attempt
2174 * to plumb under the IO stack.
2175 */
2176 if ((inq[0] & SCSI_INQUIRY_PQUAL_MASK) != 0x00) {
2177 /* shouldn't enumerate, destroy the old one if exists */
2178 if (ilp != NULL) {
2179 goto offline_old;
2180 }
2181 goto inq_done;
2182 }
2183
2184 /*
2185 * If lun type has changed
2186 */
2187 lun_type = ((struct scsi_inquiry *)inq)->inq_dtype & DTYPE_MASK;
2188 if ((ilp != NULL) && (ilp->lun_type != lun_type)) {
2189 goto offline_old;
2190 }
2191
2192 if (inq83_ready == B_TRUE) {
2193 goto guid_ready;
2194 }
2195
2196 /*
2197 * VENDOR IDENTIFICATION INQUIRY - This will be used to identify
2198 * a unique lunId. This Id is passed to the mdi alloc calls so
2199 * we can properly plumb into scsi_vhci/mpxio.
2200 */
2201
2202 bzero(&cdb, CDB_GROUP0);
2203 cdb[0] = SCMD_INQUIRY;
2204 cdb[1] = 0x01; /* EVP bit */
2205 cdb[2] = 0x83;
2206 cdb[4] = ISCSI_MAX_INQUIRY_BUF_SIZE;
2207
2208 ucmd.uscsi_flags = USCSI_READ;
2209 ucmd.uscsi_timeout = iscsi_sess_enum_timeout;
2210 ucmd.uscsi_cdb = (char *)&cdb[0];
2211 ucmd.uscsi_cdblen = CDB_GROUP0;
2212 ucmd.uscsi_bufaddr = (char *)inq83;
2213 ucmd.uscsi_buflen = ISCSI_MAX_INQUIRY_BUF_SIZE;
2214
2215 /* Attempt to get inquiry information until successful or retries */
2216 retries = 0;
2217 while ((retries++ < ISCSI_MAX_INQUIRY_RETRIES) &&
2218 (isp->sess_state_event_count == event_count)) {
2219 /* issue passthru command */
2220 rval = iscsi_handle_passthru(isp, lun_num, &ucmd);
2221
2222 /* If we were successful but scsi stat failed update istatus */
2223 if (ISCSI_SUCCESS(rval) &&
2224 (ucmd.uscsi_status != STATUS_GOOD)) {
2225 rval = ISCSI_STATUS_USCSI_FAILED;
2226 }
2227
2228 /* Break if successful */
2229 if (ISCSI_SUCCESS(rval)) {
2230 inq83_len = ISCSI_MAX_INQUIRY_BUF_SIZE -
2231 ucmd.uscsi_resid;
2232 break;
2233 }
2234 }
2235
2236 /*
2237 * If we were successful collecting page 83 data attempt
2238 * to generate a GUID. If no GUID can be generated then
2239 * the logical unit will skip attempt to plumb under
2240 * scsi_vhci/mpxio.
2241 */
2242 if (ISCSI_SUCCESS(rval)) {
2243 /* create DEVID from inquiry data */
2244 if (ddi_devid_scsi_encode(
2245 DEVID_SCSI_ENCODE_VERSION_LATEST, NULL,
2246 inq, inq_len, NULL, 0, inq83, inq83_len, &devid) ==
2247 DDI_SUCCESS) {
2248
2249 /* extract GUID from DEVID */
2250 guid = ddi_devid_to_guid(devid);
2251
2252 /* devid no longer needed */
2253 ddi_devid_free(devid);
2254 }
2255 }
2256 inq83_ready = B_TRUE;
2257
2258 guid_ready:
2259
2260 if (ilp != NULL) {
2261 if ((guid == NULL) && (ilp->lun_guid == NULL)) {
2262 nochange = B_TRUE;
2263 }
2264
2265 if ((guid != NULL) && (ilp->lun_guid != NULL) &&
2266 ((strlen(guid) + 1) == ilp->lun_guid_size) &&
2267 (bcmp(guid, ilp->lun_guid, ilp->lun_guid_size) == 0)) {
2268 nochange = B_TRUE;
2269 }
2270
2271 if (nochange != B_TRUE) {
2272 goto offline_old;
2273 }
2274
2275 if (ilp->lun_state & (ISCSI_LUN_STATE_OFFLINE |
2276 ISCSI_LUN_STATE_INVALID)) {
2277 if (isp->sess_state_event_count == event_count) {
2278 (void) iscsi_lun_online(ihp, ilp);
2279 }
2280 }
2281 } else {
2282 if (isp->sess_state_event_count == event_count) {
2283 (void) iscsi_lun_create(isp, lun_num, lun_addr_type,
2284 (struct scsi_inquiry *)inq, guid);
2285 }
2286 }
2287
2288 inq_done:
2289 if (guid != NULL) {
2290 /* guid is no longer needed */
2291 ddi_devid_free_guid(guid);
2292 }
2293
2294 /* free up memory now that we are done */
2295 kmem_free(inq, ISCSI_MAX_INQUIRY_BUF_SIZE);
2296 kmem_free(inq83, ISCSI_MAX_INQUIRY_BUF_SIZE);
2297 }
2298
2299 static iscsi_status_t
retrieve_lundata(uint32_t lun_count,unsigned char * buf,iscsi_sess_t * isp,uint16_t * lun_num,uint8_t * lun_addr_type)2300 retrieve_lundata(uint32_t lun_count, unsigned char *buf, iscsi_sess_t *isp,
2301 uint16_t *lun_num, uint8_t *lun_addr_type)
2302 {
2303 uint32_t lun_idx = 0;
2304
2305 ASSERT(lun_num != NULL);
2306 ASSERT(lun_addr_type != NULL);
2307
2308 lun_idx = (lun_count + 1) * SCSI_REPORTLUNS_ADDRESS_SIZE;
2309 /* determine report luns addressing type */
2310 switch (buf[lun_idx] & SCSI_REPORTLUNS_ADDRESS_MASK) {
2311 /*
2312 * Vendors in the field have been found to be concatenating
2313 * bus/target/lun to equal the complete lun value instead
2314 * of switching to flat space addressing
2315 */
2316 /* 00b - peripheral device addressing method */
2317 case SCSI_REPORTLUNS_ADDRESS_PERIPHERAL:
2318 /* FALLTHRU */
2319 /* 10b - logical unit addressing method */
2320 case SCSI_REPORTLUNS_ADDRESS_LOGICAL_UNIT:
2321 /* FALLTHRU */
2322 /* 01b - flat space addressing method */
2323 case SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE:
2324 /* byte0 bit0-5=msb lun byte1 bit0-7=lsb lun */
2325 *lun_addr_type = (buf[lun_idx] &
2326 SCSI_REPORTLUNS_ADDRESS_MASK) >> 6;
2327 *lun_num = (buf[lun_idx] & 0x3F) << 8;
2328 *lun_num |= buf[lun_idx + 1];
2329 return (ISCSI_STATUS_SUCCESS);
2330 default: /* protocol error */
2331 cmn_err(CE_NOTE, "iscsi session(%u) unable "
2332 "to enumerate logical units - report "
2333 "luns returned an unsupported format",
2334 isp->sess_oid);
2335 break;
2336 }
2337 return (ISCSI_STATUS_INTERNAL_ERROR);
2338 }
2339
2340 /*
2341 * iscsi_sess_flush - flushes remaining pending io on the session
2342 */
2343 static void
iscsi_sess_flush(iscsi_sess_t * isp)2344 iscsi_sess_flush(iscsi_sess_t *isp)
2345 {
2346 iscsi_cmd_t *icmdp;
2347
2348 ASSERT(isp != NULL);
2349 ASSERT(isp->sess_state != ISCSI_SESS_STATE_LOGGED_IN);
2350
2351 /*
2352 * Flush out any remaining commands in the pending
2353 * queue.
2354 */
2355 mutex_enter(&isp->sess_queue_pending.mutex);
2356 icmdp = isp->sess_queue_pending.head;
2357 while (icmdp != NULL) {
2358 if (isp->sess_state == ISCSI_SESS_STATE_FAILED) {
2359 mutex_enter(&icmdp->cmd_mutex);
2360 if (icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) {
2361 icmdp->cmd_un.scsi.pkt_stat |= STAT_ABORTED;
2362 }
2363 mutex_exit(&icmdp->cmd_mutex);
2364 }
2365
2366 iscsi_cmd_state_machine(icmdp,
2367 ISCSI_CMD_EVENT_E7, isp);
2368 icmdp = isp->sess_queue_pending.head;
2369 }
2370 mutex_exit(&isp->sess_queue_pending.mutex);
2371 }
2372
2373 /*
2374 * iscsi_sess_offline_luns - offline all this sessions luns
2375 */
2376 static void
iscsi_sess_offline_luns(iscsi_sess_t * isp)2377 iscsi_sess_offline_luns(iscsi_sess_t *isp)
2378 {
2379 iscsi_lun_t *ilp;
2380 iscsi_hba_t *ihp;
2381
2382 ASSERT(isp != NULL);
2383 ASSERT(isp->sess_state != ISCSI_SESS_STATE_LOGGED_IN);
2384 ihp = isp->sess_hba;
2385 ASSERT(ihp != NULL);
2386
2387 rw_enter(&isp->sess_lun_list_rwlock, RW_READER);
2388 ilp = isp->sess_lun_list;
2389 while (ilp != NULL) {
2390 (void) iscsi_lun_offline(ihp, ilp, B_FALSE);
2391 ilp = ilp->lun_next;
2392 }
2393 rw_exit(&isp->sess_lun_list_rwlock);
2394 }
2395
2396 /*
2397 * iscsi_sess_get_by_target - return the session structure for based on a
2398 * passed in target oid and hba instance. NOTE: There may be
2399 * multiple sessions associated with any given target. In this case,
2400 * we will return the first matching session. This function
2401 * is intended to be used in retrieving target info that is constant
2402 * across sessions (target name, alias, etc.).
2403 */
2404 int
iscsi_sess_get_by_target(uint32_t target_oid,iscsi_hba_t * ihp,iscsi_sess_t ** ispp)2405 iscsi_sess_get_by_target(uint32_t target_oid, iscsi_hba_t *ihp,
2406 iscsi_sess_t **ispp)
2407 {
2408 int rval = 0;
2409 iscsi_sess_t *isp = NULL;
2410
2411 ASSERT(ihp != NULL);
2412 ASSERT(ispp != NULL);
2413
2414 /* See if we already created this session */
2415 for (isp = ihp->hba_sess_list; isp; isp = isp->sess_next) {
2416 /*
2417 * Look for a session associated to the given target.
2418 * Return the first one found.
2419 */
2420 if (isp->sess_target_oid == target_oid) {
2421 /* Found matching session */
2422 break;
2423 }
2424 }
2425
2426 /* If not null this session is already available */
2427 if (isp != NULL) {
2428 /* Existing session, return it */
2429 *ispp = isp;
2430 } else {
2431 rval = EFAULT;
2432 }
2433 return (rval);
2434 }
2435
2436 static void
iscsi_sess_update_busy_luns(iscsi_sess_t * isp,boolean_t clear)2437 iscsi_sess_update_busy_luns(iscsi_sess_t *isp, boolean_t clear)
2438 {
2439 iscsi_lun_t *ilp;
2440 iscsi_hba_t *ihp;
2441
2442 ASSERT(isp != NULL);
2443 ihp = isp->sess_hba;
2444 ASSERT(ihp != NULL);
2445
2446 rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
2447 ilp = isp->sess_lun_list;
2448 while (ilp != NULL) {
2449 if (clear == B_TRUE) {
2450 ilp->lun_state &= ~ISCSI_LUN_STATE_BUSY;
2451 } else {
2452 ilp->lun_state |= ISCSI_LUN_STATE_BUSY;
2453 }
2454 ilp = ilp->lun_next;
2455 }
2456 rw_exit(&isp->sess_lun_list_rwlock);
2457 }
2458
2459 /*
2460 * Submits the scsi enumeration request. Returns
2461 * ISCSI_SESS_ENUM_SUBMITTED upon success, or others if failures are met.
2462 * If the request is submitted and the wait is set to B_TRUE, the caller
2463 * must call iscsi_sess_enum_query at a later time to unblock next enum
2464 */
2465 iscsi_enum_result_t
iscsi_sess_enum_request(iscsi_sess_t * isp,boolean_t wait,uint32_t event_count)2466 iscsi_sess_enum_request(iscsi_sess_t *isp, boolean_t wait, uint32_t event_count)
2467 {
2468 iscsi_task_t *itp;
2469
2470 itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
2471 itp->t_arg = isp;
2472 itp->t_event_count = event_count;
2473
2474 mutex_enter(&isp->sess_enum_lock);
2475 while ((isp->sess_enum_status != ISCSI_SESS_ENUM_FREE) &&
2476 (isp->sess_enum_status != ISCSI_SESS_ENUM_INPROG)) {
2477 cv_wait(&isp->sess_enum_cv, &isp->sess_enum_lock);
2478 }
2479 if (isp->sess_enum_status == ISCSI_SESS_ENUM_INPROG) {
2480 /* easy case */
2481 if (wait == B_TRUE) {
2482 isp->sess_enum_result_count ++;
2483 }
2484 mutex_exit(&isp->sess_enum_lock);
2485 kmem_free(itp, sizeof (iscsi_task_t));
2486 return (ISCSI_SESS_ENUM_SUBMITTED);
2487 }
2488
2489 ASSERT(isp->sess_enum_status == ISCSI_SESS_ENUM_FREE);
2490 ASSERT(isp->sess_enum_result_count == 0);
2491
2492 isp->sess_enum_status = ISCSI_SESS_ENUM_INPROG;
2493 if (ddi_taskq_dispatch(isp->sess_enum_taskq,
2494 iscsi_sess_enumeration, itp, DDI_SLEEP) != DDI_SUCCESS) {
2495 isp->sess_enum_status = ISCSI_SESS_ENUM_FREE;
2496 mutex_exit(&isp->sess_enum_lock);
2497 kmem_free(itp, sizeof (iscsi_task_t));
2498 return (ISCSI_SESS_ENUM_SUBFAIL);
2499 }
2500 if (wait == B_TRUE) {
2501 isp->sess_enum_result_count ++;
2502 }
2503 mutex_exit(&isp->sess_enum_lock);
2504 return (ISCSI_SESS_ENUM_SUBMITTED);
2505 }
2506
2507 /*
2508 * Wait and query the result of the enumeration.
2509 * The last caller is responsible for kicking off the DONE status
2510 */
2511 iscsi_enum_result_t
iscsi_sess_enum_query(iscsi_sess_t * isp)2512 iscsi_sess_enum_query(iscsi_sess_t *isp)
2513 {
2514 iscsi_enum_result_t ret = ISCSI_SESS_ENUM_IOFAIL;
2515
2516 mutex_enter(&isp->sess_enum_lock);
2517 while (isp->sess_enum_status != ISCSI_SESS_ENUM_DONE) {
2518 cv_wait(&isp->sess_enum_cv, &isp->sess_enum_lock);
2519 }
2520 ret = isp->sess_enum_result;
2521 isp->sess_enum_result_count --;
2522 if (isp->sess_enum_result_count == 0) {
2523 isp->sess_enum_status = ISCSI_SESS_ENUM_FREE;
2524 cv_broadcast(&isp->sess_enum_cv);
2525 }
2526 mutex_exit(&isp->sess_enum_lock);
2527
2528 return (ret);
2529 }
2530
2531 static void
iscsi_sess_enum_warn(iscsi_sess_t * isp,iscsi_enum_result_t r)2532 iscsi_sess_enum_warn(iscsi_sess_t *isp, iscsi_enum_result_t r)
2533 {
2534 cmn_err(CE_WARN, "iscsi session (%u) enumeration fails - %s",
2535 isp->sess_oid, iscsi_sess_enum_warn_msgs[r]);
2536 }
2537
2538 void
iscsi_sess_enter_state_zone(iscsi_sess_t * isp)2539 iscsi_sess_enter_state_zone(iscsi_sess_t *isp)
2540 {
2541 mutex_enter(&isp->sess_state_wmutex);
2542 while (isp->sess_state_hasw == B_TRUE) {
2543 cv_wait(&isp->sess_state_wcv, &isp->sess_state_wmutex);
2544 }
2545 isp->sess_state_hasw = B_TRUE;
2546 mutex_exit(&isp->sess_state_wmutex);
2547
2548 rw_enter(&isp->sess_state_rwlock, RW_WRITER);
2549 }
2550
2551 void
iscsi_sess_exit_state_zone(iscsi_sess_t * isp)2552 iscsi_sess_exit_state_zone(iscsi_sess_t *isp)
2553 {
2554 rw_exit(&isp->sess_state_rwlock);
2555
2556 mutex_enter(&isp->sess_state_wmutex);
2557 isp->sess_state_hasw = B_FALSE;
2558 cv_signal(&isp->sess_state_wcv);
2559 mutex_exit(&isp->sess_state_wmutex);
2560 }
2561