xref: /titanic_51/usr/src/uts/common/io/comstar/port/iscsit/iscsit_tgt.c (revision ca3b8945247d4cfb82a351e1a293910f66fbde81)
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  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
24  */
25 
26 #include <sys/cpuvar.h>
27 #include <sys/types.h>
28 #include <sys/conf.h>
29 #include <sys/file.h>
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/modctl.h>
33 #include <sys/sysmacros.h>
34 #include <sys/sdt.h>
35 
36 #include <sys/socket.h>
37 #include <sys/strsubr.h>
38 
39 #include <sys/stmf.h>
40 #include <sys/stmf_ioctl.h>
41 #include <sys/portif.h>
42 #include <sys/idm/idm.h>
43 
44 #define	ISCSIT_TGT_SM_STRINGS
45 #include "iscsit.h"
46 #include "iscsit_isns.h"
47 
48 typedef struct {
49 	list_node_t		te_ctx_node;
50 	iscsit_tgt_event_t	te_ctx_event;
51 } tgt_event_ctx_t;
52 
53 static void
54 tgt_sm_event_dispatch(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx);
55 
56 static void
57 tgt_sm_created(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx);
58 
59 static void
60 tgt_sm_onlining(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx);
61 
62 static void
63 tgt_sm_online(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx);
64 
65 static void
66 tgt_sm_stmf_online(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx);
67 
68 static void
69 tgt_sm_deleting_need_offline(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx);
70 
71 static void
72 tgt_sm_offlining(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx);
73 
74 static void
75 tgt_sm_offline(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx);
76 
77 static void
78 tgt_sm_stmf_offline(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx);
79 
80 static void
81 tgt_sm_deleting_stmf_dereg(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx);
82 
83 static void
84 tgt_sm_deleting_stmf_dereg_fail(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx);
85 
86 static void
87 tgt_sm_deleting(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx);
88 
89 static void
90 iscsit_tgt_dereg_retry(void *arg);
91 
92 static void
93 iscsit_tgt_dereg_task(void *arg);
94 
95 static void
96 tgt_sm_new_state(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx,
97     iscsit_tgt_state_t new_state);
98 
99 
100 static iscsit_tgt_t *
101 iscsit_tgt_create(it_tgt_t *cfg_tgt);
102 
103 static void
104 iscsit_tgt_unref(void *tgt);
105 
106 static void
107 iscsit_tgt_async_wait_ref(iscsit_tgt_t *tgt, idm_refcnt_cb_t *cb_func);
108 
109 static void
110 iscsit_tgt_destroy(iscsit_tgt_t *tgt);
111 
112 static iscsit_tpgt_t *
113 iscsit_tgt_lookup_tpgt_locked(iscsit_tgt_t *tgt, uint16_t tag);
114 
115 static iscsit_tpg_t *
116 iscsit_tpg_lookup_locked(char *tpg_name);
117 
118 static iscsit_portal_t *
119 iscsit_tpg_portal_lookup_locked(iscsit_tpg_t *tpg,
120     struct sockaddr_storage *sa);
121 
122 static idm_status_t
123 iscsit_tgt_online(iscsit_tgt_t *tgt);
124 
125 static void
126 iscsit_tgt_offline(iscsit_tgt_t *tgt);
127 
128 static idm_status_t
129 iscsit_tgt_modify(iscsit_tgt_t *tgt, it_tgt_t *cfg_tgt);
130 
131 static idm_status_t
132 iscsit_tgt_merge_tpgt(iscsit_tgt_t *tgt, it_tgt_t *cfg_tgt,
133     list_t *tpgt_del_list);
134 
135 static iscsit_tpgt_t *
136 iscsit_tpgt_create(it_tpgt_t *cfg_tpgt);
137 
138 static iscsit_tpgt_t *
139 iscsit_tpgt_create_default();
140 
141 static void
142 iscsit_tpgt_destroy(iscsit_tpgt_t *tpgt);
143 
144 static iscsit_tpg_t *
145 iscsit_tpg_create(it_tpg_t *tpg);
146 
147 static void
148 iscsit_tpg_modify(iscsit_tpg_t *tpg, it_tpg_t *cfg_tpg);
149 
150 static void
151 iscsit_tpg_destroy(iscsit_tpg_t *tpg);
152 
153 static iscsit_portal_t *
154 iscsit_portal_create(iscsit_tpg_t *tpg, struct sockaddr_storage *sa);
155 
156 static void
157 iscsit_portal_delete(iscsit_portal_t *portal);
158 
159 static idm_status_t
160 iscsit_portal_online(iscsit_portal_t *portal);
161 
162 static void
163 iscsit_portal_offline(iscsit_portal_t *portal);
164 
165 
166 
167 /*
168  * Target state machine
169  */
170 
171 void
172 iscsit_tgt_sm_event(iscsit_tgt_t *tgt, iscsit_tgt_event_t event)
173 {
174 	mutex_enter(&tgt->target_mutex);
175 	tgt_sm_event_locked(tgt, event);
176 	mutex_exit(&tgt->target_mutex);
177 }
178 
179 void
180 tgt_sm_event_locked(iscsit_tgt_t *tgt, iscsit_tgt_event_t event)
181 {
182 	tgt_event_ctx_t *ctx;
183 
184 	iscsit_tgt_hold(tgt);
185 
186 	ctx = kmem_zalloc(sizeof (*ctx), KM_SLEEP);
187 
188 	ctx->te_ctx_event = event;
189 
190 	list_insert_tail(&tgt->target_events, ctx);
191 	/*
192 	 * Use the target_sm_busy flag to keep the state machine single
193 	 * threaded.  This also serves as recursion avoidance since this
194 	 * flag will always be set if we call iscsit_tgt_sm_event from
195 	 * within the state machine code.
196 	 */
197 	if (!tgt->target_sm_busy) {
198 		tgt->target_sm_busy = B_TRUE;
199 		while (!list_is_empty(&tgt->target_events)) {
200 			ctx = list_head(&tgt->target_events);
201 			list_remove(&tgt->target_events, ctx);
202 			idm_sm_audit_event(&tgt->target_state_audit,
203 			    SAS_ISCSIT_TGT, (int)tgt->target_state,
204 			    (int)ctx->te_ctx_event, 0);
205 			mutex_exit(&tgt->target_mutex);
206 			tgt_sm_event_dispatch(tgt, ctx);
207 			mutex_enter(&tgt->target_mutex);
208 		}
209 		tgt->target_sm_busy = B_FALSE;
210 
211 	}
212 
213 	iscsit_tgt_rele(tgt);
214 }
215 
216 static void
217 tgt_sm_event_dispatch(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx)
218 {
219 	DTRACE_PROBE2(tgt__event, iscsit_tgt_t *, tgt,
220 	    tgt_event_ctx_t *, ctx);
221 
222 	IDM_SM_LOG(CE_NOTE, "tgt_sm_event_dispatch: tgt %p event %s(%d)",
223 	    (void *)tgt, iscsit_te_name[ctx->te_ctx_event], ctx->te_ctx_event);
224 
225 	/* State independent actions */
226 	switch (ctx->te_ctx_event) {
227 	case TE_DELETE:
228 		tgt->target_deleting = B_TRUE;
229 		break;
230 	}
231 
232 	/* State dependent actions */
233 	switch (tgt->target_state) {
234 	case TS_CREATED:
235 		tgt_sm_created(tgt, ctx);
236 		break;
237 	case TS_ONLINING:
238 		tgt_sm_onlining(tgt, ctx);
239 		break;
240 	case TS_ONLINE:
241 		tgt_sm_online(tgt, ctx);
242 		break;
243 	case TS_STMF_ONLINE:
244 		tgt_sm_stmf_online(tgt, ctx);
245 		break;
246 	case TS_DELETING_NEED_OFFLINE:
247 		tgt_sm_deleting_need_offline(tgt, ctx);
248 		break;
249 	case TS_OFFLINING:
250 		tgt_sm_offlining(tgt, ctx);
251 		break;
252 	case TS_OFFLINE:
253 		tgt_sm_offline(tgt, ctx);
254 		break;
255 	case TS_STMF_OFFLINE:
256 		tgt_sm_stmf_offline(tgt, ctx);
257 		break;
258 	case TS_DELETING_STMF_DEREG:
259 		tgt_sm_deleting_stmf_dereg(tgt, ctx);
260 		break;
261 	case TS_DELETING_STMF_DEREG_FAIL:
262 		tgt_sm_deleting_stmf_dereg_fail(tgt, ctx);
263 		break;
264 	case TS_DELETING:
265 		tgt_sm_deleting(tgt, ctx);
266 		break;
267 	default:
268 		ASSERT(0);
269 	}
270 
271 	kmem_free(ctx, sizeof (*ctx));
272 }
273 
274 static void
275 tgt_sm_created(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx)
276 {
277 	stmf_change_status_t	scs;
278 
279 	switch (ctx->te_ctx_event) {
280 	case TE_STMF_ONLINE_REQ:
281 		tgt_sm_new_state(tgt, ctx, TS_ONLINING);
282 		break;
283 	case TE_DELETE:
284 		tgt_sm_new_state(tgt, ctx, TS_DELETING_STMF_DEREG);
285 		break;
286 	case TE_STMF_OFFLINE_REQ:
287 		/*
288 		 * We're already offline but update to an equivelant
289 		 * state just to note that STMF talked to us.
290 		 */
291 		scs.st_completion_status = STMF_SUCCESS;
292 		scs.st_additional_info = NULL;
293 		tgt_sm_new_state(tgt, ctx, TS_OFFLINE);
294 		(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE,
295 		    tgt->target_stmf_lport, &scs);
296 		break;
297 	case TE_STMF_ONLINE_COMPLETE_ACK:
298 	case TE_STMF_OFFLINE_COMPLETE_ACK:
299 		/* Ignore */
300 		break;
301 	default:
302 		ASSERT(0);
303 	}
304 }
305 
306 static void
307 tgt_sm_onlining(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx)
308 {
309 	stmf_change_status_t	scs;
310 
311 	switch (ctx->te_ctx_event) {
312 	case TE_ONLINE_SUCCESS:
313 		tgt_sm_new_state(tgt, ctx, TS_ONLINE);
314 		break;
315 	case TE_ONLINE_FAIL:
316 		tgt_sm_new_state(tgt, ctx, TS_STMF_OFFLINE);
317 		break;
318 	case TE_DELETE:
319 		/* TE_DELETE is handled in tgt_sm_event_dispatch() */
320 		break;
321 	case TE_STMF_ONLINE_REQ:
322 	case TE_STMF_OFFLINE_REQ:
323 		/*
324 		 * We can't complete STMF's request since we are busy going
325 		 * online.
326 		 */
327 		scs.st_completion_status = STMF_INVALID_ARG;
328 		scs.st_additional_info = NULL;
329 		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
330 		    STMF_CMD_LPORT_ONLINE_COMPLETE :
331 		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
332 		    tgt->target_stmf_lport, &scs);
333 		break;
334 	case TE_STMF_ONLINE_COMPLETE_ACK:
335 	case TE_STMF_OFFLINE_COMPLETE_ACK:
336 		/* Ignore */
337 		break;
338 	default:
339 		ASSERT(0);
340 	}
341 }
342 
343 static void
344 tgt_sm_online(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx)
345 {
346 	stmf_change_status_t	scs;
347 
348 	switch (ctx->te_ctx_event) {
349 	case TE_STMF_ONLINE_COMPLETE_ACK:
350 		if (tgt->target_deleting) {
351 			tgt_sm_new_state(tgt, ctx, TS_DELETING_NEED_OFFLINE);
352 		} else {
353 			tgt_sm_new_state(tgt, ctx, TS_STMF_ONLINE);
354 		}
355 		break;
356 	case TE_DELETE:
357 		/* TE_DELETE is handled in tgt_sm_event_dispatch() */
358 		break;
359 	case TE_STMF_ONLINE_REQ:
360 	case TE_STMF_OFFLINE_REQ:
361 		/*
362 		 * We can't complete STMF's request since we are busy going
363 		 * online (waiting for acknowlegement from STMF)
364 		 */
365 		scs.st_completion_status = STMF_INVALID_ARG;
366 		scs.st_additional_info = NULL;
367 		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
368 		    STMF_CMD_LPORT_ONLINE_COMPLETE :
369 		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
370 		    tgt->target_stmf_lport, &scs);
371 		break;
372 	case TE_STMF_OFFLINE_COMPLETE_ACK:
373 		/* Ignore */
374 		break;
375 	default:
376 		ASSERT(0);
377 	}
378 }
379 
380 
381 static void
382 tgt_sm_stmf_online(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx)
383 {
384 	stmf_change_status_t	scs;
385 
386 	/* Deregister target with iSNS whenever we leave this state */
387 
388 	switch (ctx->te_ctx_event) {
389 	case TE_DELETE:
390 		(void) iscsit_isns_deregister(tgt);
391 		tgt_sm_new_state(tgt, ctx, TS_DELETING_NEED_OFFLINE);
392 		break;
393 	case TE_STMF_OFFLINE_REQ:
394 		(void) iscsit_isns_deregister(tgt);
395 		tgt_sm_new_state(tgt, ctx, TS_OFFLINING);
396 		break;
397 	case TE_STMF_ONLINE_REQ:
398 		/* Already online */
399 		scs.st_completion_status = STMF_ALREADY;
400 		scs.st_additional_info = NULL;
401 		(void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE,
402 		    tgt->target_stmf_lport, &scs);
403 		break;
404 	case TE_STMF_ONLINE_COMPLETE_ACK:
405 	case TE_STMF_OFFLINE_COMPLETE_ACK:
406 		/* Ignore */
407 		break;
408 	default:
409 		ASSERT(0);
410 	}
411 }
412 
413 
414 static void
415 tgt_sm_deleting_need_offline(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx)
416 {
417 	stmf_change_status_t	scs;
418 
419 	switch (ctx->te_ctx_event) {
420 	case TE_STMF_OFFLINE_REQ:
421 		tgt_sm_new_state(tgt, ctx, TS_OFFLINING);
422 		break;
423 	case TE_DELETE:
424 		/* TE_DELETE is handled in tgt_sm_event_dispatch() */
425 		break;
426 	case TE_STMF_ONLINE_REQ:
427 		/*
428 		 * We can't complete STMF's request since we need to be offlined
429 		 */
430 		scs.st_completion_status = STMF_INVALID_ARG;
431 		scs.st_additional_info = NULL;
432 		(void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE,
433 		    tgt->target_stmf_lport, &scs);
434 		break;
435 	case TE_STMF_ONLINE_COMPLETE_ACK:
436 	case TE_STMF_OFFLINE_COMPLETE_ACK:
437 		/* Ignore */
438 		break;
439 	default:
440 		ASSERT(0);
441 	}
442 }
443 
444 
445 static void
446 tgt_sm_offlining(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx)
447 {
448 	stmf_change_status_t	scs;
449 
450 	switch (ctx->te_ctx_event) {
451 	case TE_OFFLINE_COMPLETE:
452 		tgt_sm_new_state(tgt, ctx, TS_OFFLINE);
453 		break;
454 	case TE_DELETE:
455 		/* TE_DELETE is handled in tgt_sm_event_dispatch() */
456 		break;
457 	case TE_STMF_ONLINE_REQ:
458 	case TE_STMF_OFFLINE_REQ:
459 		/*
460 		 * We can't complete STMF's request since we are busy going
461 		 * offline.
462 		 */
463 		scs.st_completion_status = STMF_INVALID_ARG;
464 		scs.st_additional_info = NULL;
465 		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
466 		    STMF_CMD_LPORT_ONLINE_COMPLETE :
467 		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
468 		    tgt->target_stmf_lport, &scs);
469 		break;
470 	case TE_STMF_ONLINE_COMPLETE_ACK:
471 	case TE_STMF_OFFLINE_COMPLETE_ACK:
472 		/* Ignore */
473 		break;
474 	default:
475 		ASSERT(0);
476 	}
477 }
478 
479 
480 static void
481 tgt_sm_offline(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx)
482 {
483 	stmf_change_status_t	scs;
484 
485 	switch (ctx->te_ctx_event) {
486 	case TE_STMF_OFFLINE_COMPLETE_ACK:
487 		if (tgt->target_deleting) {
488 			tgt_sm_new_state(tgt, ctx, TS_DELETING_STMF_DEREG);
489 		} else {
490 			tgt_sm_new_state(tgt, ctx, TS_STMF_OFFLINE);
491 		}
492 		break;
493 	case TE_DELETE:
494 		/* TE_DELETE is handled in tgt_sm_event_dispatch() */
495 		break;
496 	case TE_STMF_ONLINE_REQ:
497 	case TE_STMF_OFFLINE_REQ:
498 		/*
499 		 * We can't complete STMF's request since we are busy going
500 		 * offline.
501 		 */
502 		scs.st_completion_status = STMF_INVALID_ARG;
503 		scs.st_additional_info = NULL;
504 		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
505 		    STMF_CMD_LPORT_ONLINE_COMPLETE :
506 		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
507 		    tgt->target_stmf_lport, &scs);
508 		break;
509 	case TE_STMF_ONLINE_COMPLETE_ACK:
510 		/* Ignore */
511 		break;
512 	default:
513 		ASSERT(0);
514 	}
515 }
516 
517 
518 static void
519 tgt_sm_stmf_offline(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx)
520 {
521 	stmf_change_status_t	scs;
522 
523 	switch (ctx->te_ctx_event) {
524 	case TE_STMF_ONLINE_REQ:
525 		tgt_sm_new_state(tgt, ctx, TS_ONLINING);
526 		break;
527 	case TE_DELETE:
528 		tgt_sm_new_state(tgt, ctx, TS_DELETING_STMF_DEREG);
529 		break;
530 	case TE_STMF_OFFLINE_REQ:
531 		/* Already offline */
532 		scs.st_completion_status = STMF_ALREADY;
533 		scs.st_additional_info = NULL;
534 		(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE,
535 		    tgt->target_stmf_lport, &scs);
536 		break;
537 	case TE_STMF_ONLINE_COMPLETE_ACK:
538 	case TE_STMF_OFFLINE_COMPLETE_ACK:
539 		/* Ignore */
540 		break;
541 	default:
542 		ASSERT(0);
543 	}
544 }
545 
546 
547 static void
548 tgt_sm_deleting_stmf_dereg(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx)
549 {
550 	stmf_change_status_t	scs;
551 
552 	/* Terminal state, no events */
553 	switch (ctx->te_ctx_event) {
554 	case TE_STMF_ONLINE_REQ:
555 	case TE_STMF_OFFLINE_REQ:
556 		/*
557 		 * We can't complete STMF's request since we are being deleted
558 		 */
559 		scs.st_completion_status = STMF_INVALID_ARG;
560 		scs.st_additional_info = NULL;
561 		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
562 		    STMF_CMD_LPORT_ONLINE_COMPLETE :
563 		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
564 		    tgt->target_stmf_lport, &scs);
565 		break;
566 	case TE_STMF_ONLINE_COMPLETE_ACK:
567 	case TE_STMF_OFFLINE_COMPLETE_ACK:
568 		/* Ignore */
569 		break;
570 	case TE_STMF_DEREG_SUCCESS:
571 		tgt_sm_new_state(tgt, ctx, TS_DELETING);
572 		break;
573 	case TE_STMF_DEREG_FAIL:
574 		tgt_sm_new_state(tgt, ctx, TS_DELETING_STMF_DEREG_FAIL);
575 		break;
576 	default:
577 		ASSERT(0);
578 	}
579 }
580 
581 static void
582 tgt_sm_deleting_stmf_dereg_fail(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx)
583 {
584 	stmf_change_status_t	scs;
585 
586 	/* Terminal state, no events */
587 	switch (ctx->te_ctx_event) {
588 	case TE_STMF_ONLINE_REQ:
589 	case TE_STMF_OFFLINE_REQ:
590 		/*
591 		 * We can't complete STMF's request since we are being deleted
592 		 */
593 		scs.st_completion_status = STMF_INVALID_ARG;
594 		scs.st_additional_info = NULL;
595 		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
596 		    STMF_CMD_LPORT_ONLINE_COMPLETE :
597 		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
598 		    tgt->target_stmf_lport, &scs);
599 		break;
600 	case TE_STMF_ONLINE_COMPLETE_ACK:
601 	case TE_STMF_OFFLINE_COMPLETE_ACK:
602 		/* Ignore */
603 		break;
604 	case TE_STMF_DEREG_RETRY:
605 		tgt_sm_new_state(tgt, ctx, TS_DELETING_STMF_DEREG);
606 		break;
607 	default:
608 		ASSERT(0);
609 	}
610 }
611 
612 static void
613 tgt_sm_deleting(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx)
614 {
615 	stmf_change_status_t	scs;
616 
617 	/* Terminal state, no events */
618 	switch (ctx->te_ctx_event) {
619 	case TE_STMF_ONLINE_REQ:
620 	case TE_STMF_OFFLINE_REQ:
621 		/*
622 		 * We can't complete STMF's request since we are being deleted
623 		 */
624 		scs.st_completion_status = STMF_INVALID_ARG;
625 		scs.st_additional_info = NULL;
626 		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
627 		    STMF_CMD_LPORT_ONLINE_COMPLETE :
628 		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
629 		    tgt->target_stmf_lport, &scs);
630 		break;
631 	case TE_STMF_ONLINE_COMPLETE_ACK:
632 	case TE_STMF_OFFLINE_COMPLETE_ACK:
633 		/* Ignore */
634 		break;
635 	default:
636 		ASSERT(0);
637 	}
638 }
639 
640 
641 static void
642 iscsit_tgt_dereg_retry(void *arg)
643 {
644 	iscsit_tgt_t *tgt = arg;
645 
646 	/*
647 	 * Rather than guaranteeing the target state machine code will not
648 	 * block for long periods of time (tying up this callout thread)
649 	 * we will queue a task on the taskq to send the retry event.
650 	 * If it fails we'll setup another timeout and try again later.
651 	 */
652 	if (taskq_dispatch(iscsit_global.global_dispatch_taskq,
653 	    iscsit_tgt_dereg_task, tgt, DDI_NOSLEEP) == NULL) {
654 		/* Dispatch failed, try again later */
655 		(void) timeout(iscsit_tgt_dereg_retry, tgt,
656 		    drv_usectohz(TGT_DEREG_RETRY_SECONDS * 1000000));
657 	}
658 }
659 
660 static void
661 iscsit_tgt_dereg_task(void *arg)
662 {
663 	iscsit_tgt_t *tgt = arg;
664 
665 	iscsit_tgt_sm_event(tgt, TE_STMF_DEREG_RETRY);
666 }
667 
668 static void
669 tgt_sm_new_state(iscsit_tgt_t *tgt, tgt_event_ctx_t *ctx,
670     iscsit_tgt_state_t new_state)
671 {
672 	stmf_local_port_t		*lport = tgt->target_stmf_lport;
673 	stmf_change_status_t		scs;
674 	stmf_state_change_info_t	sci;
675 	idm_status_t			idmrc;
676 	stmf_status_t			stmfrc;
677 
678 	scs.st_completion_status = STMF_SUCCESS;
679 	scs.st_additional_info = NULL;
680 
681 	/*
682 	 * Validate new state
683 	 */
684 	ASSERT(new_state != TS_UNDEFINED);
685 	ASSERT3U(new_state, <, TS_MAX_STATE);
686 
687 	new_state = (new_state < TS_MAX_STATE) ?
688 	    new_state : TS_UNDEFINED;
689 
690 	IDM_SM_LOG(CE_NOTE, "tgt_sm_new_state: tgt %p, %s(%d) --> %s(%d)\n",
691 	    (void *) tgt, iscsit_ts_name[tgt->target_state], tgt->target_state,
692 	    iscsit_ts_name[new_state], new_state);
693 	DTRACE_PROBE3(target__state__change,
694 	    iscsit_tgt_t *, tgt, tgt_event_ctx_t *, ctx,
695 	    iscsit_tgt_state_t, new_state);
696 
697 	mutex_enter(&tgt->target_mutex);
698 	idm_sm_audit_state_change(&tgt->target_state_audit, SAS_ISCSIT_TGT,
699 	    (int)tgt->target_state, (int)new_state);
700 	tgt->target_last_state = tgt->target_state;
701 	tgt->target_state = new_state;
702 	mutex_exit(&tgt->target_mutex);
703 
704 	switch (tgt->target_state) {
705 	case TS_ONLINING:
706 		idmrc = iscsit_tgt_online(tgt);
707 		if (idmrc != IDM_STATUS_SUCCESS) {
708 			scs.st_completion_status = STMF_TARGET_FAILURE;
709 			iscsit_tgt_sm_event(tgt, TE_ONLINE_FAIL);
710 		} else {
711 			iscsit_tgt_sm_event(tgt, TE_ONLINE_SUCCESS);
712 		}
713 		/*
714 		 * Let STMF know the how the online operation completed.
715 		 * STMF will respond with an acknowlege later
716 		 */
717 		(void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE, lport, &scs);
718 		break;
719 	case TS_ONLINE:
720 		break;
721 	case TS_STMF_ONLINE:
722 		(void) iscsit_isns_register(tgt);
723 		break;
724 	case TS_DELETING_NEED_OFFLINE:
725 		sci.st_rflags = STMF_RFLAG_STAY_OFFLINED;
726 		sci.st_additional_info = "Offline for delete";
727 		(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE, lport, &sci);
728 		break;
729 	case TS_OFFLINING:
730 		/* Async callback generates completion event */
731 		iscsit_tgt_offline(tgt);
732 		break;
733 	case TS_OFFLINE:
734 		break;
735 	case TS_STMF_OFFLINE:
736 		break;
737 	case TS_DELETING_STMF_DEREG:
738 		stmfrc = stmf_deregister_local_port(tgt->target_stmf_lport);
739 		if (stmfrc == STMF_SUCCESS) {
740 			iscsit_tgt_sm_event(tgt, TE_STMF_DEREG_SUCCESS);
741 		} else {
742 			iscsit_tgt_sm_event(tgt, TE_STMF_DEREG_FAIL);
743 		}
744 		break;
745 	case TS_DELETING_STMF_DEREG_FAIL:
746 		/* Retry dereg in 1 second */
747 		(void) timeout(iscsit_tgt_dereg_retry, tgt,
748 		    drv_usectohz(TGT_DEREG_RETRY_SECONDS * 1000000));
749 		break;
750 	case TS_DELETING:
751 		iscsit_tgt_async_wait_ref(tgt, iscsit_tgt_unref);
752 		break;
753 	default:
754 		ASSERT(0);
755 	}
756 }
757 
758 
759 /*
760  * Target, TPGT, TPG utility functions
761  */
762 
763 it_cfg_status_t
764 iscsit_config_merge_tgt(it_config_t *cfg)
765 {
766 	it_tgt_t	*cfg_tgt;
767 	iscsit_tgt_t	*tgt, *next_tgt;
768 	it_cfg_status_t	itrc = ITCFG_SUCCESS;
769 
770 
771 	/*
772 	 * 1. >> Lock <<
773 	 * 2. Removing deleted objects
774 	 * 3. Add deleted targets to global delete list
775 	 * 4. "delete" event to target state machine
776 	 * 5. >> Unlock <<
777 	 * 6. Create new targets, update modified targets
778 	 */
779 	for (tgt = avl_first(&iscsit_global.global_target_list);
780 	    tgt != NULL;
781 	    tgt = next_tgt) {
782 		next_tgt = AVL_NEXT(&iscsit_global.global_target_list, tgt);
783 
784 		if (it_tgt_lookup(cfg, tgt->target_name) == NULL) {
785 			avl_remove(&iscsit_global.global_target_list, tgt);
786 			list_insert_tail(
787 			    &iscsit_global.global_deleted_target_list, tgt);
788 			iscsit_tgt_sm_event(tgt, TE_DELETE);
789 		}
790 	}
791 
792 	/* Now walk through the list of configured targets */
793 	for (cfg_tgt = cfg->config_tgt_list;
794 	    cfg_tgt != NULL;
795 	    cfg_tgt = cfg_tgt->tgt_next) {
796 		/* See if we have an existing target */
797 		tgt = iscsit_tgt_lookup_locked(cfg_tgt->tgt_name);
798 
799 		if (tgt == NULL) {
800 			tgt = iscsit_tgt_create(cfg_tgt);
801 			if (tgt == NULL)
802 				return (ITCFG_TGT_CREATE_ERR);
803 			avl_add(&iscsit_global.global_target_list, tgt);
804 		} else {
805 			if (iscsit_tgt_modify(tgt, cfg_tgt) !=
806 			    IDM_STATUS_SUCCESS)
807 				itrc = ITCFG_MISC_ERR;
808 			iscsit_tgt_rele(tgt);
809 		}
810 	}
811 
812 	/*
813 	 * Targets on the iscsit_global.global_deleted_target_list will remove
814 	 * and destroy themselves when their associated state machines reach
815 	 * the TS_DELETED state and all references are released.
816 	 */
817 	return (itrc);
818 }
819 
820 iscsit_tgt_t *
821 iscsit_tgt_lookup(char *target_name)
822 {
823 	iscsit_tgt_t	*result;
824 
825 	ISCSIT_GLOBAL_LOCK(RW_READER);
826 	result = iscsit_tgt_lookup_locked(target_name);
827 	ISCSIT_GLOBAL_UNLOCK();
828 
829 	return (result);
830 }
831 
832 iscsit_tgt_t *
833 iscsit_tgt_lookup_locked(char *target_name)
834 {
835 	iscsit_tgt_t	tmp_tgt;
836 	iscsit_tgt_t	*result;
837 
838 	/*
839 	 * Use a dummy target for lookup, filling in all fields used in AVL
840 	 * comparison.
841 	 */
842 	tmp_tgt.target_name = target_name;
843 	if ((result = avl_find(&iscsit_global.global_target_list,
844 	    &tmp_tgt, NULL)) != NULL) {
845 		iscsit_tgt_hold(result);
846 	}
847 
848 	return (result);
849 }
850 
851 iscsit_tgt_t *
852 iscsit_tgt_create(it_tgt_t *cfg_tgt)
853 {
854 	iscsit_tgt_t		*result;
855 	stmf_local_port_t	*lport;
856 	char			*alias;
857 
858 	/*
859 	 * Each target is an STMF local port.
860 	 */
861 	lport = stmf_alloc(STMF_STRUCT_STMF_LOCAL_PORT,
862 	    sizeof (iscsit_tgt_t) + sizeof (scsi_devid_desc_t) +
863 	    strnlen(cfg_tgt->tgt_name, MAX_ISCSI_NODENAMELEN) + 1, 0);
864 	if (lport == NULL) {
865 		return (NULL);
866 	}
867 
868 	result = lport->lport_port_private;
869 	result->target_state = TS_CREATED;
870 	result->target_stmf_lport_registered = 0;
871 	/* Use pointer arithmetic to find scsi_devid_desc_t */
872 	result->target_devid = (scsi_devid_desc_t *)(result + 1);
873 	(void) strcpy((char *)result->target_devid->ident, cfg_tgt->tgt_name);
874 	result->target_devid->ident_length =
875 	    strnlen(cfg_tgt->tgt_name, MAX_ISCSI_NODENAMELEN);
876 	result->target_devid->protocol_id = PROTOCOL_iSCSI;
877 	result->target_devid->piv = 1;
878 	result->target_devid->code_set = CODE_SET_ASCII;
879 	result->target_devid->association = ID_IS_TARGET_PORT;
880 
881 	/* Store a shortcut to the target name */
882 	result->target_name = (char *)result->target_devid->ident;
883 	idm_sm_audit_init(&result->target_state_audit);
884 	mutex_init(&result->target_mutex, NULL, MUTEX_DEFAULT, NULL);
885 	avl_create(&result->target_sess_list, iscsit_sess_avl_compare,
886 	    sizeof (iscsit_sess_t), offsetof(iscsit_sess_t, ist_tgt_ln));
887 	avl_create(&result->target_tpgt_list, iscsit_tpgt_avl_compare,
888 	    sizeof (iscsit_tpgt_t), offsetof(iscsit_tpgt_t, tpgt_tgt_ln));
889 	list_create(&result->target_events, sizeof (tgt_event_ctx_t),
890 	    offsetof(tgt_event_ctx_t, te_ctx_node));
891 	idm_refcnt_init(&result->target_refcnt, result);
892 	idm_refcnt_init(&result->target_sess_refcnt, result);
893 
894 	/* Set target alias */
895 	if (nvlist_lookup_string(cfg_tgt->tgt_properties, "alias", &alias) == 0)
896 		lport->lport_alias = strdup(alias);
897 
898 	/* Finish initializing local port */
899 	/*
900 	 * Would like infinite timeout, but this is about as long as can
901 	 * be specified to stmf on a 32 bit kernel.
902 	 */
903 	lport->lport_abort_timeout = 2000; /* seconds */
904 	lport->lport_id = result->target_devid;
905 	lport->lport_pp = iscsit_global.global_pp;
906 	lport->lport_ds = iscsit_global.global_dbuf_store;
907 	lport->lport_xfer_data = &iscsit_xfer_scsi_data;
908 	lport->lport_send_status = &iscsit_send_scsi_status;
909 	lport->lport_task_free = &iscsit_lport_task_free;
910 	lport->lport_abort = &iscsit_abort;
911 	lport->lport_ctl = &iscsit_ctl;
912 	result->target_stmf_lport = lport;
913 
914 	/*
915 	 * We need a global hold until the STMF-ONLINE state machine
916 	 * completes.  Acquire that hold now, in case we need to call
917 	 * iscsit_tgt_destroy, which will also release the hold.
918 	 */
919 	iscsit_global_hold();
920 
921 	/*
922 	 * Additional target modifications from config
923 	 */
924 	if (iscsit_tgt_modify(result, cfg_tgt) != IDM_STATUS_SUCCESS) {
925 		iscsit_tgt_destroy(result);
926 		return (NULL);
927 	}
928 
929 	/*
930 	 * Register the target with STMF but not until we have all the
931 	 * TPGT bindings and any other additional config setup.  STMF
932 	 * may immediately ask us to go online.
933 	 */
934 	if (stmf_register_local_port(lport) != STMF_SUCCESS) {
935 		iscsit_tgt_destroy(result);
936 		return (NULL);
937 	}
938 	result->target_stmf_lport_registered = 1;
939 
940 	return (result);
941 }
942 
943 static idm_status_t
944 iscsit_tgt_modify(iscsit_tgt_t *tgt, it_tgt_t *cfg_tgt)
945 {
946 	idm_status_t	idmrc = IDM_STATUS_SUCCESS;
947 	list_t		tpgt_del_list;
948 	char		*alias;
949 
950 	/* Merge TPGT */
951 	list_create(&tpgt_del_list, sizeof (iscsit_tpgt_t),
952 	    offsetof(iscsit_tpgt_t, tpgt_delete_ln));
953 
954 	mutex_enter(&tgt->target_mutex);
955 	if (tgt->target_props) {
956 		nvlist_free(tgt->target_props);
957 		tgt->target_props = NULL;
958 	}
959 	(void) nvlist_dup(cfg_tgt->tgt_properties, &tgt->target_props,
960 	    KM_SLEEP);
961 
962 	/* Update alias */
963 	if (tgt->target_stmf_lport->lport_alias) {
964 		strfree(tgt->target_stmf_lport->lport_alias);
965 		tgt->target_stmf_lport->lport_alias = NULL;
966 	}
967 	if (nvlist_lookup_string(tgt->target_props, "alias", &alias) == 0)
968 		tgt->target_stmf_lport->lport_alias = strdup(alias);
969 
970 	if ((idmrc = iscsit_tgt_merge_tpgt(tgt, cfg_tgt, &tpgt_del_list)) !=
971 	    IDM_STATUS_SUCCESS) {
972 		/* This should never happen */
973 		cmn_err(CE_WARN, "Fail to configure TPGTs for "
974 		    "target %s, the target modification could not be "
975 		    "completed.", tgt->target_name);
976 	}
977 
978 	mutex_exit(&tgt->target_mutex);
979 
980 	iscsit_config_destroy_tpgts(&tpgt_del_list);
981 
982 	/*
983 	 * If the target is truly modified (not newly created),
984 	 * inform iSNS to update the target registration.
985 	 */
986 	if ((tgt->target_generation > 0) &&
987 	    (cfg_tgt->tgt_generation > tgt->target_generation)) {
988 		iscsit_isns_target_update(tgt);
989 	}
990 
991 	tgt->target_generation = cfg_tgt->tgt_generation;
992 
993 	return (idmrc);
994 }
995 
996 void
997 iscsit_config_destroy_tpgts(list_t *tpgt_del_list)
998 {
999 	iscsit_tpgt_t	*tpgt, *next_tpgt;
1000 
1001 	for (tpgt = list_head(tpgt_del_list);
1002 	    tpgt != NULL;
1003 	    tpgt = next_tpgt) {
1004 		next_tpgt = list_next(tpgt_del_list, tpgt);
1005 
1006 		list_remove(tpgt_del_list, tpgt);
1007 		idm_refcnt_wait_ref(&tpgt->tpgt_refcnt);
1008 		iscsit_tpgt_destroy(tpgt);
1009 	}
1010 }
1011 
1012 void
1013 iscsit_tgt_unref(void *tgt_void)
1014 {
1015 	iscsit_tgt_t	*tgt = tgt_void;
1016 
1017 	ISCSIT_GLOBAL_LOCK(RW_WRITER);
1018 	list_remove(&iscsit_global.global_deleted_target_list, tgt);
1019 	ISCSIT_GLOBAL_UNLOCK();
1020 	iscsit_tgt_destroy(tgt);
1021 }
1022 
1023 void
1024 iscsit_tgt_async_wait_ref(iscsit_tgt_t *tgt, idm_refcnt_cb_t *cb_func)
1025 {
1026 	idm_refcnt_async_wait_ref(&tgt->target_refcnt, cb_func);
1027 }
1028 
1029 static void
1030 iscsit_tgt_destroy(iscsit_tgt_t *tgt)
1031 {
1032 	iscsit_tpgt_t *tpgt, *next_tpgt;
1033 
1034 	ASSERT(tgt->target_state == TS_DELETING ||
1035 	    (tgt->target_state == TS_CREATED &&
1036 	    tgt->target_stmf_lport_registered == 0));
1037 
1038 	/*
1039 	 * Destroy all target portal group tags
1040 	 */
1041 	mutex_enter(&tgt->target_mutex);
1042 	for (tpgt = avl_first(&tgt->target_tpgt_list);
1043 	    tpgt != NULL;
1044 	    tpgt = next_tpgt) {
1045 		next_tpgt = AVL_NEXT(&tgt->target_tpgt_list, tpgt);
1046 		avl_remove(&tgt->target_tpgt_list, tpgt);
1047 		iscsit_tpgt_destroy(tpgt);
1048 	}
1049 
1050 	if (tgt->target_props) {
1051 		nvlist_free(tgt->target_props);
1052 	}
1053 	mutex_exit(&tgt->target_mutex);
1054 
1055 	/*
1056 	 * Destroy target
1057 	 */
1058 	idm_refcnt_destroy(&tgt->target_sess_refcnt);
1059 	idm_refcnt_destroy(&tgt->target_refcnt);
1060 	list_destroy(&tgt->target_events);
1061 	avl_destroy(&tgt->target_tpgt_list);
1062 	avl_destroy(&tgt->target_sess_list);
1063 	mutex_destroy(&tgt->target_mutex);
1064 	if (tgt->target_stmf_lport->lport_alias)
1065 		strfree(tgt->target_stmf_lport->lport_alias);
1066 	stmf_free(tgt->target_stmf_lport); /* Also frees "tgt' */
1067 	iscsit_global_rele();
1068 }
1069 
1070 void
1071 iscsit_tgt_hold(iscsit_tgt_t *tgt)
1072 {
1073 	idm_refcnt_hold(&tgt->target_refcnt);
1074 }
1075 
1076 void
1077 iscsit_tgt_rele(iscsit_tgt_t *tgt)
1078 {
1079 	idm_refcnt_rele(&tgt->target_refcnt);
1080 }
1081 
1082 int
1083 iscsit_tgt_avl_compare(const void *void_tgt1, const void *void_tgt2)
1084 {
1085 	const iscsit_tgt_t	*tgt1 = void_tgt1;
1086 	const iscsit_tgt_t	*tgt2 = void_tgt2;
1087 	int 			result;
1088 
1089 	/*
1090 	 * Sort by ISID first then TSIH
1091 	 */
1092 	result = strcmp(tgt1->target_name, tgt2->target_name);
1093 	if (result < 0) {
1094 		return (-1);
1095 	} else if (result > 0) {
1096 		return (1);
1097 	}
1098 
1099 	return (0);
1100 }
1101 
1102 
1103 iscsit_tpgt_t *
1104 iscsit_tgt_lookup_tpgt(iscsit_tgt_t *tgt, uint16_t tag)
1105 {
1106 	iscsit_tpgt_t *result;
1107 
1108 	mutex_enter(&tgt->target_mutex);
1109 	result = iscsit_tgt_lookup_tpgt_locked(tgt, tag);
1110 	mutex_exit(&tgt->target_mutex);
1111 
1112 	return (result);
1113 }
1114 
1115 static iscsit_tpgt_t *
1116 iscsit_tgt_lookup_tpgt_locked(iscsit_tgt_t *tgt, uint16_t tag)
1117 {
1118 	iscsit_tpgt_t	tmp_tpgt;
1119 	iscsit_tpgt_t	*result;
1120 
1121 	/* Caller holds tgt->target_mutex */
1122 	tmp_tpgt.tpgt_tag = tag;
1123 	if ((result = avl_find(&tgt->target_tpgt_list, &tmp_tpgt, NULL)) !=
1124 	    NULL) {
1125 		iscsit_tpgt_hold(result);
1126 	}
1127 
1128 	return (result);
1129 }
1130 
1131 iscsit_portal_t *
1132 iscsit_tgt_lookup_portal(iscsit_tgt_t *tgt, struct sockaddr_storage *sa,
1133     iscsit_tpgt_t **output_tpgt)
1134 {
1135 	iscsit_tpgt_t 	*tpgt;
1136 	iscsit_portal_t	*portal;
1137 
1138 	/* Caller holds tgt->target_mutex */
1139 	ASSERT(mutex_owned(&tgt->target_mutex));
1140 	for (tpgt = avl_first(&tgt->target_tpgt_list);
1141 	    tpgt != NULL;
1142 	    tpgt = AVL_NEXT(&tgt->target_tpgt_list, tpgt)) {
1143 		portal = iscsit_tpg_portal_lookup(tpgt->tpgt_tpg, sa);
1144 		if (portal) {
1145 			iscsit_tpgt_hold(tpgt);
1146 			*output_tpgt = tpgt;
1147 			return (portal);
1148 		}
1149 	}
1150 
1151 	return (NULL);
1152 }
1153 
1154 
1155 void
1156 iscsit_tgt_bind_sess(iscsit_tgt_t *tgt, iscsit_sess_t *sess)
1157 {
1158 	if (tgt) {
1159 		sess->ist_lport = tgt->target_stmf_lport;
1160 		iscsit_tgt_hold(tgt);
1161 		idm_refcnt_hold(&tgt->target_sess_refcnt);
1162 		mutex_enter(&tgt->target_mutex);
1163 		avl_add(&tgt->target_sess_list, sess);
1164 		mutex_exit(&tgt->target_mutex);
1165 	} else {
1166 		/* Discovery session */
1167 		sess->ist_lport = NULL;
1168 		ISCSIT_GLOBAL_LOCK(RW_WRITER);
1169 		avl_add(&iscsit_global.global_discovery_sessions, sess);
1170 		ISCSIT_GLOBAL_UNLOCK();
1171 	}
1172 }
1173 
1174 void
1175 iscsit_tgt_unbind_sess(iscsit_tgt_t *tgt, iscsit_sess_t *sess)
1176 {
1177 	if (tgt) {
1178 		mutex_enter(&tgt->target_mutex);
1179 		avl_remove(&tgt->target_sess_list, sess);
1180 		mutex_exit(&tgt->target_mutex);
1181 		sess->ist_tgt = (iscsit_tgt_t *)SESS_UNBOUND_FROM_TGT;
1182 		idm_refcnt_rele(&tgt->target_sess_refcnt);
1183 		iscsit_tgt_rele(tgt);
1184 	} else {
1185 		/* Discovery session */
1186 		ISCSIT_GLOBAL_LOCK(RW_WRITER);
1187 		avl_remove(&iscsit_global.global_discovery_sessions, sess);
1188 		ISCSIT_GLOBAL_UNLOCK();
1189 	}
1190 }
1191 
1192 #define	LOCK_FOR_SESS_LOOKUP(lookup_tgt) { 			\
1193 	if ((lookup_tgt) == NULL) {				\
1194 		ISCSIT_GLOBAL_LOCK(RW_READER);			\
1195 	} else {						\
1196 		mutex_enter(&(lookup_tgt)->target_mutex);	\
1197 	}							\
1198 }
1199 
1200 #define	UNLOCK_FOR_SESS_LOOKUP(lookup_tgt) { 			\
1201 	if ((lookup_tgt) == NULL) {				\
1202 		ISCSIT_GLOBAL_UNLOCK();				\
1203 	} else {					 	\
1204 		mutex_exit(&(lookup_tgt)->target_mutex); 	\
1205 	}							\
1206 }
1207 
1208 iscsit_sess_t *
1209 iscsit_tgt_lookup_sess(iscsit_tgt_t *tgt, char *initiator_name,
1210     uint8_t *isid, uint16_t tsih, uint16_t tag)
1211 {
1212 	iscsit_sess_t	tmp_sess;
1213 	avl_tree_t	*sess_avl;
1214 	avl_index_t	where;
1215 	iscsit_sess_t	*result;
1216 
1217 	/*
1218 	 * If tgt is NULL then we are looking for a discovery session
1219 	 */
1220 	if (tgt == NULL) {
1221 		sess_avl = &iscsit_global.global_discovery_sessions;
1222 	} else {
1223 		sess_avl = &tgt->target_sess_list;
1224 	}
1225 
1226 	LOCK_FOR_SESS_LOOKUP(tgt);
1227 	if (avl_numnodes(sess_avl) == NULL) {
1228 		UNLOCK_FOR_SESS_LOOKUP(tgt);
1229 		return (NULL);
1230 	}
1231 
1232 	/*
1233 	 * We'll try to find a session matching ISID + TSIH first.  If we
1234 	 * can't find one then we will return the closest match.  If the
1235 	 * caller needs an exact match it must compare the TSIH after
1236 	 * the session is returned.
1237 	 *
1238 	 * The reason we do this "fuzzy matching" is to allow matching
1239 	 * sessions with different TSIH values on the same AVL list.  This
1240 	 * makes session reinstatement much easier since the new session can
1241 	 * live on the list at the same time as the old session is cleaning up.
1242 	 */
1243 	bcopy(isid, tmp_sess.ist_isid, ISCSI_ISID_LEN);
1244 	tmp_sess.ist_initiator_name = initiator_name;
1245 	tmp_sess.ist_tsih = tsih;
1246 	tmp_sess.ist_tpgt_tag = tag;
1247 
1248 	result = avl_find(sess_avl, &tmp_sess, &where);
1249 	if (result != NULL) {
1250 		goto found_result;
1251 	}
1252 
1253 	/*
1254 	 * avl_find_nearest() may return a result with a different ISID so
1255 	 * we should only return a result if the name and ISID match
1256 	 */
1257 	result = avl_nearest(sess_avl, where, AVL_BEFORE);
1258 	if ((result != NULL) &&
1259 	    (strcmp(result->ist_initiator_name, initiator_name) == 0) &&
1260 	    (memcmp(result->ist_isid, isid, ISCSI_ISID_LEN) == 0) &&
1261 	    (result->ist_tpgt_tag == tag)) {
1262 		goto found_result;
1263 	}
1264 
1265 	result = avl_nearest(sess_avl, where, AVL_AFTER);
1266 	if ((result != NULL) &&
1267 	    (strcmp(result->ist_initiator_name, initiator_name) == 0) &&
1268 	    (memcmp(result->ist_isid, isid, ISCSI_ISID_LEN) == 0) &&
1269 	    (result->ist_tpgt_tag == tag)) {
1270 		goto found_result;
1271 	}
1272 
1273 	result = NULL;
1274 
1275 found_result:
1276 	if ((result != NULL) &&
1277 	    (iscsit_sess_check_hold(result) != IDM_STATUS_SUCCESS)) {
1278 		result = NULL;
1279 	}
1280 	UNLOCK_FOR_SESS_LOOKUP(tgt);
1281 	return (result);
1282 }
1283 
1284 static idm_status_t
1285 iscsit_tgt_merge_tpgt(iscsit_tgt_t *tgt, it_tgt_t *cfg_tgt,
1286     list_t *tpgt_del_list)
1287 {
1288 	iscsit_tpgt_t	*tpgt, *next_tpgt;
1289 	it_tpgt_t	*cfg_tpgt;
1290 	idm_status_t	status = IDM_STATUS_SUCCESS;
1291 
1292 	/*
1293 	 * 1. >> Lock <<
1294 	 * 2. Removing all objects and place on a temp list
1295 	 * 3. Add new objects
1296 	 * 4. >> Unlock <<
1297 	 * 5. tpgt_del_list contains deleted objects
1298 	 */
1299 	ASSERT(avl_is_empty(&tgt->target_tpgt_list) ||
1300 	    (tpgt_del_list != NULL));
1301 
1302 	if (tpgt_del_list) {
1303 		for (tpgt = avl_first(&tgt->target_tpgt_list);
1304 		    tpgt != NULL; tpgt = next_tpgt) {
1305 			next_tpgt = AVL_NEXT(&tgt->target_tpgt_list, tpgt);
1306 			avl_remove(&tgt->target_tpgt_list, tpgt);
1307 			if (tgt->target_state == TS_STMF_ONLINE) {
1308 				tpgt->tpgt_needs_tpg_offline = B_TRUE;
1309 			}
1310 			list_insert_tail(tpgt_del_list, tpgt);
1311 		}
1312 	}
1313 
1314 	if (cfg_tgt->tgt_tpgt_list != NULL) {
1315 		/* Add currently defined TPGTs */
1316 		for (cfg_tpgt = cfg_tgt->tgt_tpgt_list;
1317 		    cfg_tpgt != NULL;
1318 		    cfg_tpgt = cfg_tpgt->tpgt_next) {
1319 			tpgt = iscsit_tpgt_create(cfg_tpgt);
1320 			if (tpgt == NULL) {
1321 				/*
1322 				 * There is a problem in the configuration we
1323 				 * received from the ioctl -- a missing tpg.
1324 				 * All the unbind operations have already
1325 				 * taken place.  To leave the system in a
1326 				 * non-panic'd state, use the default tpgt.
1327 				 */
1328 				status = IDM_STATUS_FAIL;
1329 				continue;
1330 			}
1331 			if (tgt->target_state == TS_STMF_ONLINE) {
1332 				(void) iscsit_tpg_online(tpgt->tpgt_tpg);
1333 			}
1334 			avl_add(&tgt->target_tpgt_list, tpgt);
1335 		}
1336 	}
1337 
1338 	/* If no TPGTs defined, add the default TPGT */
1339 	if (avl_numnodes(&tgt->target_tpgt_list) == 0) {
1340 		tpgt = iscsit_tpgt_create_default();
1341 		if (tgt->target_state == TS_STMF_ONLINE) {
1342 			(void) iscsit_tpg_online(tpgt->tpgt_tpg);
1343 		}
1344 		avl_add(&tgt->target_tpgt_list, tpgt);
1345 	}
1346 
1347 	return (status);
1348 }
1349 
1350 static iscsit_tpgt_t *
1351 iscsit_tpgt_create(it_tpgt_t *cfg_tpgt)
1352 {
1353 	iscsit_tpg_t	*tpg;
1354 	iscsit_tpgt_t	*result;
1355 
1356 	/* This takes a reference on the TPG */
1357 	tpg = iscsit_tpg_lookup_locked(cfg_tpgt->tpgt_tpg_name);
1358 	if (tpg == NULL)
1359 		return (NULL);
1360 
1361 	result = kmem_zalloc(sizeof (*result), KM_SLEEP);
1362 
1363 	result->tpgt_tpg = tpg;
1364 	result->tpgt_tag = cfg_tpgt->tpgt_tag;
1365 
1366 	return (result);
1367 }
1368 
1369 iscsit_tpgt_t *
1370 iscsit_tpgt_create_default()
1371 {
1372 	iscsit_tpgt_t	*result;
1373 
1374 	result = kmem_zalloc(sizeof (*result), KM_SLEEP);
1375 
1376 	result->tpgt_tpg = iscsit_global.global_default_tpg;
1377 	iscsit_tpg_hold(result->tpgt_tpg);
1378 	result->tpgt_tag = ISCSIT_DEFAULT_TPGT;
1379 
1380 	return (result);
1381 }
1382 
1383 void
1384 iscsit_tpgt_destroy(iscsit_tpgt_t *tpgt)
1385 {
1386 	if (tpgt->tpgt_needs_tpg_offline) {
1387 		iscsit_tpg_offline(tpgt->tpgt_tpg);
1388 	}
1389 	iscsit_tpg_rele(tpgt->tpgt_tpg);
1390 	kmem_free(tpgt, sizeof (*tpgt));
1391 }
1392 
1393 void
1394 iscsit_tpgt_hold(iscsit_tpgt_t *tpgt)
1395 {
1396 	idm_refcnt_hold(&tpgt->tpgt_refcnt);
1397 }
1398 
1399 void
1400 iscsit_tpgt_rele(iscsit_tpgt_t *tpgt)
1401 {
1402 	idm_refcnt_rele(&tpgt->tpgt_refcnt);
1403 }
1404 
1405 int
1406 iscsit_tpgt_avl_compare(const void *void_tpgt1, const void *void_tpgt2)
1407 {
1408 	const iscsit_tpgt_t	*tpgt1 = void_tpgt1;
1409 	const iscsit_tpgt_t	*tpgt2 = void_tpgt2;
1410 
1411 	if (tpgt1->tpgt_tag < tpgt2->tpgt_tag)
1412 		return (-1);
1413 	else if (tpgt1->tpgt_tag > tpgt2->tpgt_tag)
1414 		return (1);
1415 
1416 	return (0);
1417 }
1418 
1419 static idm_status_t
1420 iscsit_tgt_online(iscsit_tgt_t *tgt)
1421 {
1422 	iscsit_tpgt_t 		*tpgt, *tpgt_fail;
1423 	idm_status_t		rc;
1424 
1425 	mutex_enter(&tgt->target_mutex);
1426 
1427 	ASSERT(tgt->target_sess_list.avl_numnodes == 0);
1428 	idm_refcnt_reset(&tgt->target_sess_refcnt);
1429 	for (tpgt = avl_first(&tgt->target_tpgt_list);
1430 	    tpgt != NULL;
1431 	    tpgt = AVL_NEXT(&tgt->target_tpgt_list, tpgt)) {
1432 		rc = iscsit_tpg_online(tpgt->tpgt_tpg);
1433 		if (rc != IDM_STATUS_SUCCESS) {
1434 			tpgt_fail = tpgt;
1435 			goto tgt_online_fail;
1436 		}
1437 	}
1438 
1439 	mutex_exit(&tgt->target_mutex);
1440 
1441 	return (IDM_STATUS_SUCCESS);
1442 
1443 tgt_online_fail:
1444 	/* Offline all the tpgs we successfully onlined up to the failure */
1445 	for (tpgt = avl_first(&tgt->target_tpgt_list);
1446 	    tpgt != tpgt_fail;
1447 	    tpgt = AVL_NEXT(&tgt->target_tpgt_list, tpgt)) {
1448 		iscsit_tpg_offline(tpgt->tpgt_tpg);
1449 	}
1450 	mutex_exit(&tgt->target_mutex);
1451 	return (rc);
1452 }
1453 
1454 static void
1455 iscsit_tgt_offline_cb(void *tgt_void)
1456 {
1457 	iscsit_tgt_t *tgt = tgt_void;
1458 	stmf_change_status_t	scs;
1459 
1460 	iscsit_tgt_sm_event(tgt, TE_OFFLINE_COMPLETE);
1461 
1462 	scs.st_completion_status = STMF_SUCCESS;
1463 	scs.st_additional_info = NULL;
1464 	(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE,
1465 	    tgt->target_stmf_lport, &scs);
1466 }
1467 
1468 static void
1469 iscsit_tgt_offline(iscsit_tgt_t *tgt)
1470 {
1471 	iscsit_tpgt_t 		*tpgt;
1472 	iscsit_sess_t		*ist;
1473 
1474 	mutex_enter(&tgt->target_mutex);
1475 
1476 	/* Offline target portal groups */
1477 	for (tpgt = avl_first(&tgt->target_tpgt_list);
1478 	    tpgt != NULL;
1479 	    tpgt = AVL_NEXT(&tgt->target_tpgt_list, tpgt)) {
1480 		iscsit_tpg_offline(tpgt->tpgt_tpg);
1481 	}
1482 
1483 	/* Close any active sessions */
1484 	for (ist = avl_first(&tgt->target_sess_list);
1485 	    ist != NULL;
1486 	    ist = AVL_NEXT(&tgt->target_sess_list, ist)) {
1487 		/*
1488 		 * This is not a synchronous operation but after all
1489 		 * sessions have been cleaned up there will be no
1490 		 * more session-related holds on the target.
1491 		 */
1492 		iscsit_sess_close(ist);
1493 	}
1494 
1495 	mutex_exit(&tgt->target_mutex);
1496 
1497 	/*
1498 	 * Wait for all the sessions to quiesce.
1499 	 */
1500 	idm_refcnt_async_wait_ref(&tgt->target_sess_refcnt,
1501 	    &iscsit_tgt_offline_cb);
1502 }
1503 
1504 it_cfg_status_t
1505 iscsit_config_merge_tpg(it_config_t *cfg, list_t *tpg_del_list)
1506 {
1507 	it_tpg_t	*cfg_tpg;
1508 	iscsit_tpg_t	*tpg, *next_tpg;
1509 
1510 	/*
1511 	 * 1. >> Lock <<
1512 	 * 2. Removing deleted objects and place on a temp list
1513 	 * 3. Add new objects
1514 	 * 4. >> Unlock <<
1515 	 * 5. tpg_del_list contains objects to destroy
1516 	 */
1517 	for (tpg = avl_first(&iscsit_global.global_tpg_list);
1518 	    tpg != NULL;
1519 	    tpg = next_tpg) {
1520 		next_tpg = AVL_NEXT(&iscsit_global.global_tpg_list, tpg);
1521 
1522 		if (it_tpg_lookup(cfg, tpg->tpg_name) == NULL) {
1523 			/*
1524 			 * The policy around when to allow a target portal
1525 			 * group to be deleted is implemented in libiscsit.
1526 			 * By the time the request gets to the kernel module
1527 			 * we expect that it conforms to policy so we will
1528 			 * cleanup all references to TPG and destroy it if it
1529 			 * is possible to do so.
1530 			 *
1531 			 */
1532 			avl_remove(&iscsit_global.global_tpg_list, tpg);
1533 			list_insert_tail(tpg_del_list, tpg);
1534 		}
1535 	}
1536 
1537 	/* Now walk through the list of configured target portal groups */
1538 	for (cfg_tpg = cfg->config_tpg_list;
1539 	    cfg_tpg != NULL;
1540 	    cfg_tpg = cfg_tpg->tpg_next) {
1541 		/* See if we have an existing target portal group */
1542 		tpg = iscsit_tpg_lookup_locked(cfg_tpg->tpg_name);
1543 
1544 		if (tpg == NULL) {
1545 			tpg = iscsit_tpg_create(cfg_tpg);
1546 			ASSERT(tpg != NULL);
1547 			avl_add(&iscsit_global.global_tpg_list, tpg);
1548 		} else {
1549 			mutex_enter(&tpg->tpg_mutex);
1550 			iscsit_tpg_modify(tpg, cfg_tpg);
1551 			mutex_exit(&tpg->tpg_mutex);
1552 			iscsit_tpg_rele(tpg);
1553 		}
1554 	}
1555 
1556 	return (ITCFG_SUCCESS);
1557 }
1558 
1559 
1560 void
1561 iscsit_config_destroy_tpgs(list_t *tpg_del_list)
1562 {
1563 	iscsit_tpg_t	*tpg, *next_tpg;
1564 
1565 	/* Now finish destroying the target portal groups */
1566 	for (tpg = list_head(tpg_del_list);
1567 	    tpg != NULL;
1568 	    tpg = next_tpg) {
1569 		next_tpg = list_next(tpg_del_list, tpg);
1570 		list_remove(tpg_del_list, tpg);
1571 		idm_refcnt_wait_ref(&tpg->tpg_refcnt);
1572 
1573 		/* Kill it */
1574 		iscsit_tpg_destroy(tpg);
1575 	}
1576 }
1577 
1578 iscsit_tpg_t *
1579 iscsit_tpg_lookup(char *tpg_name)
1580 {
1581 	iscsit_tpg_t *result;
1582 
1583 	ISCSIT_GLOBAL_LOCK(RW_READER);
1584 	result = iscsit_tpg_lookup_locked(tpg_name);
1585 	ISCSIT_GLOBAL_UNLOCK();
1586 
1587 	return (result);
1588 }
1589 
1590 static iscsit_tpg_t *
1591 iscsit_tpg_lookup_locked(char *tpg_name)
1592 {
1593 	iscsit_tpg_t	tmp_tpg;
1594 	iscsit_tpg_t	*result;
1595 
1596 	(void) strlcpy(tmp_tpg.tpg_name, tpg_name, MAX_ISCSI_NODENAMELEN);
1597 	if ((result = avl_find(&iscsit_global.global_tpg_list,
1598 	    &tmp_tpg, NULL)) != NULL) {
1599 		iscsit_tpg_hold(result);
1600 	}
1601 
1602 	return (result);
1603 }
1604 
1605 iscsit_tpg_t *
1606 iscsit_tpg_create(it_tpg_t *cfg_tpg)
1607 {
1608 	iscsit_tpg_t *tpg;
1609 
1610 	tpg = kmem_zalloc(sizeof (*tpg), KM_SLEEP);
1611 
1612 	mutex_init(&tpg->tpg_mutex, NULL, MUTEX_DEFAULT, NULL);
1613 	(void) strlcpy(tpg->tpg_name, cfg_tpg->tpg_name, MAX_TPG_NAMELEN);
1614 	avl_create(&tpg->tpg_portal_list, iscsit_portal_avl_compare,
1615 	    sizeof (iscsit_portal_t), offsetof(iscsit_portal_t, portal_tpg_ln));
1616 	idm_refcnt_init(&tpg->tpg_refcnt, tpg);
1617 
1618 	mutex_enter(&tpg->tpg_mutex);
1619 	iscsit_tpg_modify(tpg, cfg_tpg);
1620 	mutex_exit(&tpg->tpg_mutex);
1621 	iscsit_global_hold();
1622 
1623 	return (tpg);
1624 }
1625 
1626 static void
1627 iscsit_tpg_modify(iscsit_tpg_t *tpg, it_tpg_t *cfg_tpg)
1628 {
1629 	iscsit_portal_t		*portal, *next_portal;
1630 	it_portal_t		*cfg_portal;
1631 
1632 	/* Update portals */
1633 	for (portal = avl_first(&tpg->tpg_portal_list);
1634 	    portal != NULL;
1635 	    portal = next_portal) {
1636 		next_portal = AVL_NEXT(&tpg->tpg_portal_list, portal);
1637 		if (it_portal_lookup(cfg_tpg, &portal->portal_addr) == NULL) {
1638 			avl_remove(&tpg->tpg_portal_list, portal);
1639 			iscsit_portal_delete(portal);
1640 			/*
1641 			 * If the last portal is deleted from the target
1642 			 * portal group, then the tpg->tpg_online count
1643 			 * must be decremented. The other two callers of
1644 			 * iscsit_portal_delete() destroy the target portal
1645 			 * after deleting the portal so it is not necessary
1646 			 * to decrement the tpg->tpg_online count.
1647 			 */
1648 			if (avl_is_empty(&tpg->tpg_portal_list)) {
1649 				tpg->tpg_online--;
1650 			}
1651 		}
1652 	}
1653 
1654 	for (cfg_portal = cfg_tpg->tpg_portal_list;
1655 	    cfg_portal != NULL;
1656 	    cfg_portal = cfg_portal->portal_next) {
1657 		if ((portal = iscsit_tpg_portal_lookup_locked(tpg,
1658 		    &cfg_portal->portal_addr)) == NULL) {
1659 			(void) iscsit_portal_create(tpg,
1660 			    &cfg_portal->portal_addr);
1661 		} else {
1662 			iscsit_portal_rele(portal);
1663 		}
1664 	}
1665 }
1666 
1667 void
1668 iscsit_tpg_destroy(iscsit_tpg_t *tpg)
1669 {
1670 	iscsit_portal_t *portal, *next_portal;
1671 
1672 	for (portal = avl_first(&tpg->tpg_portal_list);
1673 	    portal != NULL;
1674 	    portal = next_portal) {
1675 		next_portal = AVL_NEXT(&tpg->tpg_portal_list, portal);
1676 		avl_remove(&tpg->tpg_portal_list, portal);
1677 		iscsit_portal_delete(portal);
1678 	}
1679 
1680 	idm_refcnt_wait_ref(&tpg->tpg_refcnt);
1681 	idm_refcnt_destroy(&tpg->tpg_refcnt);
1682 	avl_destroy(&tpg->tpg_portal_list);
1683 	mutex_destroy(&tpg->tpg_mutex);
1684 	kmem_free(tpg, sizeof (*tpg));
1685 	iscsit_global_rele();
1686 }
1687 
1688 void
1689 iscsit_tpg_hold(iscsit_tpg_t *tpg)
1690 {
1691 	idm_refcnt_hold(&tpg->tpg_refcnt);
1692 }
1693 
1694 void
1695 iscsit_tpg_rele(iscsit_tpg_t *tpg)
1696 {
1697 	idm_refcnt_rele(&tpg->tpg_refcnt);
1698 }
1699 
1700 iscsit_tpg_t *
1701 iscsit_tpg_createdefault()
1702 {
1703 	iscsit_tpg_t *tpg;
1704 
1705 	tpg = kmem_zalloc(sizeof (*tpg), KM_SLEEP);
1706 
1707 	mutex_init(&tpg->tpg_mutex, NULL, MUTEX_DEFAULT, NULL);
1708 	(void) strlcpy(tpg->tpg_name, ISCSIT_DEFAULT_TPG, MAX_TPG_NAMELEN);
1709 	avl_create(&tpg->tpg_portal_list, iscsit_portal_avl_compare,
1710 	    sizeof (iscsit_portal_t), offsetof(iscsit_portal_t, portal_tpg_ln));
1711 	idm_refcnt_init(&tpg->tpg_refcnt, tpg);
1712 
1713 	/* Now create default portal */
1714 	if (iscsit_portal_create(tpg, NULL) == NULL) {
1715 		iscsit_tpg_destroy(tpg);
1716 		return (NULL);
1717 	}
1718 
1719 	return (tpg);
1720 }
1721 
1722 void
1723 iscsit_tpg_destroydefault(iscsit_tpg_t *tpg)
1724 {
1725 	iscsit_portal_t *portal;
1726 
1727 	portal = avl_first(&tpg->tpg_portal_list);
1728 	ASSERT(portal != NULL);
1729 	avl_remove(&tpg->tpg_portal_list, portal);
1730 	iscsit_portal_delete(portal);
1731 
1732 	idm_refcnt_wait_ref(&tpg->tpg_refcnt);
1733 	idm_refcnt_destroy(&tpg->tpg_refcnt);
1734 	avl_destroy(&tpg->tpg_portal_list);
1735 	mutex_destroy(&tpg->tpg_mutex);
1736 	kmem_free(tpg, sizeof (*tpg));
1737 }
1738 
1739 int
1740 iscsit_tpg_avl_compare(const void *void_tpg1, const void *void_tpg2)
1741 {
1742 	const iscsit_tpg_t	*tpg1 = void_tpg1;
1743 	const iscsit_tpg_t	*tpg2 = void_tpg2;
1744 	int 			result;
1745 
1746 	/*
1747 	 * Sort by ISID first then TSIH
1748 	 */
1749 	result = strcmp(tpg1->tpg_name, tpg2->tpg_name);
1750 	if (result < 0) {
1751 		return (-1);
1752 	} else if (result > 0) {
1753 		return (1);
1754 	}
1755 
1756 	return (0);
1757 }
1758 
1759 idm_status_t
1760 iscsit_tpg_online(iscsit_tpg_t *tpg)
1761 {
1762 	iscsit_portal_t *portal, *portal_fail;
1763 	idm_status_t	rc;
1764 
1765 	mutex_enter(&tpg->tpg_mutex);
1766 	if (tpg->tpg_online == 0) {
1767 		for (portal = avl_first(&tpg->tpg_portal_list);
1768 		    portal != NULL;
1769 		    portal = AVL_NEXT(&tpg->tpg_portal_list, portal)) {
1770 			rc = iscsit_portal_online(portal);
1771 			if (rc != IDM_STATUS_SUCCESS) {
1772 				portal_fail = portal;
1773 				goto tpg_online_fail;
1774 			}
1775 		}
1776 	}
1777 	tpg->tpg_online++;
1778 
1779 	mutex_exit(&tpg->tpg_mutex);
1780 	return (IDM_STATUS_SUCCESS);
1781 
1782 tpg_online_fail:
1783 	/* Offline all the portals we successfully onlined up to the failure */
1784 	for (portal = avl_first(&tpg->tpg_portal_list);
1785 	    portal != portal_fail;
1786 	    portal = AVL_NEXT(&tpg->tpg_portal_list, portal)) {
1787 		iscsit_portal_offline(portal);
1788 	}
1789 	mutex_exit(&tpg->tpg_mutex);
1790 	return (rc);
1791 }
1792 
1793 void
1794 iscsit_tpg_offline(iscsit_tpg_t *tpg)
1795 {
1796 	iscsit_portal_t *portal;
1797 
1798 	mutex_enter(&tpg->tpg_mutex);
1799 	tpg->tpg_online--;
1800 	if (tpg->tpg_online == 0) {
1801 		for (portal = avl_first(&tpg->tpg_portal_list);
1802 		    portal != NULL;
1803 		    portal = AVL_NEXT(&tpg->tpg_portal_list, portal)) {
1804 			iscsit_portal_offline(portal);
1805 		}
1806 	}
1807 	mutex_exit(&tpg->tpg_mutex);
1808 }
1809 
1810 iscsit_portal_t *
1811 iscsit_tpg_portal_lookup(iscsit_tpg_t *tpg, struct sockaddr_storage *sa)
1812 {
1813 	iscsit_portal_t	*result;
1814 
1815 	mutex_enter(&tpg->tpg_mutex);
1816 	result = iscsit_tpg_portal_lookup_locked(tpg, sa);
1817 	mutex_exit(&tpg->tpg_mutex);
1818 
1819 	return (result);
1820 }
1821 
1822 static iscsit_portal_t *
1823 iscsit_tpg_portal_lookup_locked(iscsit_tpg_t *tpg,
1824     struct sockaddr_storage *sa)
1825 {
1826 	iscsit_portal_t	tmp_portal;
1827 	iscsit_portal_t	*result;
1828 
1829 	/* Caller holds tpg->tpg_mutex */
1830 	bcopy(sa, &tmp_portal.portal_addr, sizeof (*sa));
1831 	if ((result = avl_find(&tpg->tpg_portal_list, &tmp_portal, NULL)) !=
1832 	    NULL) {
1833 		iscsit_portal_hold(result);
1834 	}
1835 
1836 	return (result);
1837 }
1838 
1839 iscsit_portal_t *
1840 iscsit_portal_create(iscsit_tpg_t *tpg, struct sockaddr_storage *sa)
1841 {
1842 	iscsit_portal_t *portal;
1843 
1844 	portal = kmem_zalloc(sizeof (*portal), KM_SLEEP);
1845 	/*
1846 	 * If (sa == NULL) then we are being asked to create the default
1847 	 * portal -- targets will use this portal when no portals are
1848 	 * explicitly configured.
1849 	 */
1850 	if (sa == NULL) {
1851 		portal->portal_default = B_TRUE;
1852 	} else {
1853 		portal->portal_default = B_FALSE;
1854 		bcopy(sa, &portal->portal_addr, sizeof (*sa));
1855 	}
1856 
1857 	idm_refcnt_init(&portal->portal_refcnt, portal);
1858 
1859 	/*
1860 	 * Add this portal to the list
1861 	 */
1862 	avl_add(&tpg->tpg_portal_list, portal);
1863 
1864 	return (portal);
1865 }
1866 
1867 void
1868 iscsit_portal_delete(iscsit_portal_t *portal)
1869 {
1870 	if (portal->portal_online > 0) {
1871 		iscsit_portal_offline(portal);
1872 	}
1873 
1874 	if (portal->portal_online == 0) {
1875 		ASSERT(portal->portal_svc == NULL);
1876 		idm_refcnt_destroy(&portal->portal_refcnt);
1877 		kmem_free(portal, sizeof (*portal));
1878 	}
1879 }
1880 
1881 void
1882 iscsit_portal_hold(iscsit_portal_t *portal)
1883 {
1884 	idm_refcnt_hold(&portal->portal_refcnt);
1885 }
1886 
1887 void
1888 iscsit_portal_rele(iscsit_portal_t *portal)
1889 {
1890 	idm_refcnt_rele(&portal->portal_refcnt);
1891 }
1892 
1893 int
1894 iscsit_portal_avl_compare(const void *void_portal1, const void *void_portal2)
1895 {
1896 	const iscsit_portal_t			*portal1 = void_portal1;
1897 	const iscsit_portal_t			*portal2 = void_portal2;
1898 	const struct sockaddr_storage		*ss1, *ss2;
1899 	const struct in_addr			*in1, *in2;
1900 	const struct in6_addr			*in61, *in62;
1901 	int i;
1902 
1903 	/*
1904 	 * Compare ports, then address family, then ip address
1905 	 */
1906 	ss1 = &portal1->portal_addr;
1907 	ss2 = &portal2->portal_addr;
1908 	if (((struct sockaddr_in *)ss1)->sin_port !=
1909 	    ((struct sockaddr_in *)ss2)->sin_port) {
1910 		if (((struct sockaddr_in *)ss1)->sin_port >
1911 		    ((struct sockaddr_in *)ss2)->sin_port)
1912 			return (1);
1913 		else
1914 			return (-1);
1915 	}
1916 
1917 	/*
1918 	 * ports are the same
1919 	 */
1920 	if (ss1->ss_family != ss2->ss_family) {
1921 		if (ss1->ss_family == AF_INET)
1922 			return (1);
1923 		else
1924 			return (-1);
1925 	}
1926 	/*
1927 	 * address families are the same
1928 	 */
1929 	if (ss1->ss_family == AF_INET) {
1930 		in1 = &((struct sockaddr_in *)ss1)->sin_addr;
1931 		in2 = &((struct sockaddr_in *)ss2)->sin_addr;
1932 
1933 		if (in1->s_addr > in2->s_addr)
1934 			return (1);
1935 		else if (in1->s_addr < in2->s_addr)
1936 			return (-1);
1937 		else
1938 			return (0);
1939 	} else if (ss1->ss_family == AF_INET6) {
1940 		in61 = &((struct sockaddr_in6 *)ss1)->sin6_addr;
1941 		in62 = &((struct sockaddr_in6 *)ss2)->sin6_addr;
1942 
1943 		for (i = 0; i < 4; i++) {
1944 			if (in61->s6_addr32[i] > in62->s6_addr32[i])
1945 				return (1);
1946 			else if (in61->s6_addr32[i] < in62->s6_addr32[i])
1947 				return (-1);
1948 		}
1949 		return (0);
1950 	} else
1951 		cmn_err(CE_WARN,
1952 		    "iscsit_portal_avl_compare: unknown ss_family %d",
1953 		    ss1->ss_family);
1954 
1955 	return (1);
1956 }
1957 
1958 
1959 idm_status_t
1960 iscsit_portal_online(iscsit_portal_t *portal)
1961 {
1962 	idm_status_t rc = 0;
1963 	idm_svc_t	*svc;
1964 	idm_svc_req_t	sr;
1965 	uint16_t	port;
1966 	struct sockaddr_in *sin;
1967 
1968 	/* Caller holds parent TPG mutex */
1969 	if (portal->portal_online == 0) {
1970 		/*
1971 		 * If there is no existing IDM service instance for this port,
1972 		 * create one.  If the service exists, then the lookup,
1973 		 * creates a reference on the existing service.
1974 		 */
1975 		sin = (struct sockaddr_in *)&portal->portal_addr;
1976 		port = ntohs(sin->sin_port);
1977 		if (port == 0)
1978 			port = ISCSI_LISTEN_PORT;
1979 		ASSERT(portal->portal_svc == NULL);
1980 		if ((svc = idm_tgt_svc_lookup(port)) == NULL) {
1981 			sr.sr_port = port;
1982 			sr.sr_li = iscsit_global.global_li;
1983 			sr.sr_conn_ops.icb_rx_scsi_cmd = &iscsit_op_scsi_cmd;
1984 			sr.sr_conn_ops.icb_rx_scsi_rsp = NULL;
1985 			sr.sr_conn_ops.icb_rx_misc = &iscsit_rx_pdu;
1986 			sr.sr_conn_ops.icb_rx_error = &iscsit_rx_pdu_error;
1987 			sr.sr_conn_ops.icb_task_aborted = &iscsit_task_aborted;
1988 			sr.sr_conn_ops.icb_client_notify =
1989 			    &iscsit_client_notify;
1990 			sr.sr_conn_ops.icb_build_hdr = &iscsit_build_hdr;
1991 			sr.sr_conn_ops.icb_update_statsn =
1992 			    &iscsit_update_statsn;
1993 			sr.sr_conn_ops.icb_keepalive = &iscsit_keepalive;
1994 
1995 			if (idm_tgt_svc_create(&sr, &svc) !=
1996 			    IDM_STATUS_SUCCESS) {
1997 				return (IDM_STATUS_FAIL);
1998 			}
1999 
2000 			/* Get reference on the service we just created */
2001 			idm_tgt_svc_hold(svc);
2002 		}
2003 		if ((rc = idm_tgt_svc_online(svc)) != IDM_STATUS_SUCCESS) {
2004 			idm_tgt_svc_rele_and_destroy(svc);
2005 			return (IDM_STATUS_FAIL);
2006 		}
2007 		portal->portal_svc = svc;
2008 
2009 		/*
2010 		 * Only call iSNS for first online
2011 		 */
2012 		iscsit_isns_portal_online(portal);
2013 	}
2014 
2015 	portal->portal_online++;
2016 
2017 	return (rc);
2018 }
2019 
2020 void
2021 iscsit_portal_offline(iscsit_portal_t *portal)
2022 {
2023 	portal->portal_online--;
2024 
2025 	if (portal->portal_online == 0) {
2026 		/*
2027 		 * Only call iSNS for last offline
2028 		 */
2029 		iscsit_isns_portal_offline(portal);
2030 		idm_tgt_svc_offline(portal->portal_svc);
2031 		/* If service is unreferenced, destroy it too */
2032 		idm_tgt_svc_rele_and_destroy(portal->portal_svc);
2033 		portal->portal_svc = NULL;
2034 	}
2035 
2036 }
2037 
2038 it_cfg_status_t
2039 iscsit_config_merge_ini(it_config_t *cfg)
2040 {
2041 	iscsit_ini_t	*ini, *next_ini;
2042 	it_ini_t	*cfg_ini;
2043 
2044 	/*
2045 	 * Initiator objects are so simple we will just destroy all the current
2046 	 * objects and build new ones.  Nothing should ever reference an
2047 	 * initator object.. instead just lookup the initiator object and
2048 	 * grab the properties while holding the global config lock.
2049 	 */
2050 	for (ini = avl_first(&iscsit_global.global_ini_list);
2051 	    ini != NULL;
2052 	    ini = next_ini) {
2053 		next_ini = AVL_NEXT(&iscsit_global.global_ini_list, ini);
2054 		avl_remove(&iscsit_global.global_ini_list, ini);
2055 		nvlist_free(ini->ini_props);
2056 		kmem_free(ini, sizeof (*ini));
2057 		iscsit_global_rele();
2058 	}
2059 
2060 	for (cfg_ini = cfg->config_ini_list;
2061 	    cfg_ini != NULL;
2062 	    cfg_ini = cfg_ini->ini_next) {
2063 		ini = kmem_zalloc(sizeof (iscsit_ini_t), KM_SLEEP);
2064 		(void) strlcpy(ini->ini_name, cfg_ini->ini_name,
2065 		    MAX_ISCSI_NODENAMELEN);
2066 		(void) nvlist_dup(cfg_ini->ini_properties, &ini->ini_props,
2067 		    KM_SLEEP);
2068 		avl_add(&iscsit_global.global_ini_list, ini);
2069 		iscsit_global_hold();
2070 	}
2071 
2072 	return (ITCFG_SUCCESS);
2073 }
2074 
2075 int
2076 iscsit_ini_avl_compare(const void *void_ini1, const void *void_ini2)
2077 {
2078 	const iscsit_ini_t	*ini1 = void_ini1;
2079 	const iscsit_ini_t	*ini2 = void_ini2;
2080 	int 			result;
2081 
2082 	/*
2083 	 * Sort by ISID first then TSIH
2084 	 */
2085 	result = strcmp(ini1->ini_name, ini2->ini_name);
2086 	if (result < 0) {
2087 		return (-1);
2088 	} else if (result > 0) {
2089 		return (1);
2090 	}
2091 
2092 	return (0);
2093 }
2094 
2095 iscsit_ini_t *
2096 iscsit_ini_lookup_locked(char *ini_name)
2097 {
2098 	iscsit_ini_t	tmp_ini;
2099 	iscsit_ini_t	*result;
2100 
2101 	/*
2102 	 * Use a dummy target for lookup, filling in all fields used in AVL
2103 	 * comparison.
2104 	 */
2105 	(void) strlcpy(tmp_ini.ini_name, ini_name, MAX_ISCSI_NODENAMELEN);
2106 	result = avl_find(&iscsit_global.global_ini_list, &tmp_ini, NULL);
2107 
2108 	return (result);
2109 }
2110