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) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <sys/cpuvar.h>
26 #include <sys/types.h>
27 #include <sys/conf.h>
28 #include <sys/stat.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/nvpair.h>
35 #include <sys/door.h>
36 #include <sys/sdt.h>
37 
38 #include <sys/stmf.h>
39 #include <sys/stmf_ioctl.h>
40 #include <sys/pppt_ioctl.h>
41 #include <sys/portif.h>
42 
43 #include "pppt.h"
44 
45 #define	PPPT_VERSION		BUILD_DATE "-1.18dev"
46 #define	PPPT_NAME_VERSION	"COMSTAR PPPT v" PPPT_VERSION
47 
48 /*
49  * DDI entry points.
50  */
51 static int pppt_drv_attach(dev_info_t *, ddi_attach_cmd_t);
52 static int pppt_drv_detach(dev_info_t *, ddi_detach_cmd_t);
53 static int pppt_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
54 static int pppt_drv_open(dev_t *, int, int, cred_t *);
55 static int pppt_drv_close(dev_t, int, int, cred_t *);
56 static boolean_t pppt_drv_busy(void);
57 static int pppt_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
58 
59 extern pppt_status_t pppt_ic_so_enable(boolean_t);
60 extern void pppt_ic_so_disable();
61 extern void stmf_ic_rx_msg(char *, size_t);
62 
63 extern struct mod_ops mod_miscops;
64 
65 static struct cb_ops pppt_cb_ops = {
66 	pppt_drv_open,	/* cb_open */
67 	pppt_drv_close,	/* cb_close */
68 	nodev,			/* cb_strategy */
69 	nodev,			/* cb_print */
70 	nodev,			/* cb_dump */
71 	nodev,			/* cb_read */
72 	nodev,			/* cb_write */
73 	pppt_drv_ioctl,		/* cb_ioctl */
74 	nodev,			/* cb_devmap */
75 	nodev,			/* cb_mmap */
76 	nodev,			/* cb_segmap */
77 	nochpoll,		/* cb_chpoll */
78 	ddi_prop_op,		/* cb_prop_op */
79 	NULL,			/* cb_streamtab */
80 	D_MP,			/* cb_flag */
81 	CB_REV,			/* cb_rev */
82 	nodev,			/* cb_aread */
83 	nodev,			/* cb_awrite */
84 };
85 
86 static struct dev_ops pppt_dev_ops = {
87 	DEVO_REV,		/* devo_rev */
88 	0,			/* devo_refcnt */
89 	pppt_drv_getinfo,	/* devo_getinfo */
90 	nulldev,		/* devo_identify */
91 	nulldev,		/* devo_probe */
92 	pppt_drv_attach,	/* devo_attach */
93 	pppt_drv_detach,	/* devo_detach */
94 	nodev,			/* devo_reset */
95 	&pppt_cb_ops,		/* devo_cb_ops */
96 	NULL,			/* devo_bus_ops */
97 	NULL,			/* devo_power */
98 	ddi_quiesce_not_needed,	/* quiesce */
99 };
100 
101 static struct modldrv modldrv = {
102 	&mod_driverops,
103 	"Proxy Port Provider",
104 	&pppt_dev_ops,
105 };
106 
107 static struct modlinkage modlinkage = {
108 	MODREV_1,
109 	&modldrv,
110 	NULL,
111 };
112 
113 pppt_global_t pppt_global;
114 
115 int pppt_logging = 0;
116 
117 static int pppt_enable_svc(void);
118 
119 static void pppt_disable_svc(void);
120 
121 static int pppt_task_avl_compare(const void *tgt1, const void *tgt2);
122 
123 static stmf_data_buf_t *pppt_dbuf_alloc(scsi_task_t *task,
124     uint32_t size, uint32_t *pminsize, uint32_t flags);
125 
126 static void pppt_dbuf_free(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf);
127 
128 static void pppt_sess_destroy_task(void *ps_void);
129 
130 static void pppt_task_sent_status(pppt_task_t *ptask);
131 
132 static pppt_status_t pppt_task_try_abort(pppt_task_t *ptask);
133 
134 static pppt_status_t pppt_task_hold(pppt_task_t *ptask);
135 
136 static void pppt_task_rele(pppt_task_t *ptask);
137 
138 static void pppt_task_update_state(pppt_task_t *ptask,
139     pppt_task_state_t new_state);
140 
141 /*
142  * Lock order:  global --> target --> session --> task
143  */
144 
145 int
146 _init(void)
147 {
148 	int rc;
149 
150 	mutex_init(&pppt_global.global_lock, NULL, MUTEX_DEFAULT, NULL);
151 	mutex_init(&pppt_global.global_door_lock, NULL, MUTEX_DEFAULT, NULL);
152 	pppt_global.global_svc_state = PSS_DETACHED;
153 
154 	if ((rc = mod_install(&modlinkage)) != 0) {
155 		mutex_destroy(&pppt_global.global_door_lock);
156 		mutex_destroy(&pppt_global.global_lock);
157 		return (rc);
158 	}
159 
160 	return (rc);
161 }
162 
163 int
164 _info(struct modinfo *modinfop)
165 {
166 	return (mod_info(&modlinkage, modinfop));
167 }
168 
169 int
170 _fini(void)
171 {
172 	int rc;
173 
174 	rc = mod_remove(&modlinkage);
175 
176 	if (rc == 0) {
177 		mutex_destroy(&pppt_global.global_lock);
178 		mutex_destroy(&pppt_global.global_door_lock);
179 	}
180 
181 	return (rc);
182 }
183 
184 /*
185  * DDI entry points.
186  */
187 
188 /* ARGSUSED */
189 static int
190 pppt_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
191     void **result)
192 {
193 	ulong_t instance = getminor((dev_t)arg);
194 
195 	switch (cmd) {
196 	case DDI_INFO_DEVT2DEVINFO:
197 		*result = pppt_global.global_dip;
198 		return (DDI_SUCCESS);
199 
200 	case DDI_INFO_DEVT2INSTANCE:
201 		*result = (void *)instance;
202 		return (DDI_SUCCESS);
203 
204 	default:
205 		break;
206 	}
207 
208 	return (DDI_FAILURE);
209 }
210 
211 static int
212 pppt_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
213 {
214 	if (cmd != DDI_ATTACH) {
215 		return (DDI_FAILURE);
216 	}
217 
218 	if (ddi_get_instance(dip) != 0) {
219 		/* we only allow instance 0 to attach */
220 		return (DDI_FAILURE);
221 	}
222 
223 	/* create the minor node */
224 	if (ddi_create_minor_node(dip, PPPT_MODNAME, S_IFCHR, 0,
225 	    DDI_PSEUDO, 0) != DDI_SUCCESS) {
226 		cmn_err(CE_WARN, "pppt_drv_attach: "
227 		    "failed creating minor node");
228 		return (DDI_FAILURE);
229 	}
230 
231 	pppt_global.global_svc_state = PSS_DISABLED;
232 	pppt_global.global_dip = dip;
233 
234 	return (DDI_SUCCESS);
235 }
236 
237 /*ARGSUSED*/
238 static int
239 pppt_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
240 {
241 	if (cmd != DDI_DETACH)
242 		return (DDI_FAILURE);
243 
244 	PPPT_GLOBAL_LOCK();
245 	if (pppt_drv_busy()) {
246 		PPPT_GLOBAL_UNLOCK();
247 		return (EBUSY);
248 	}
249 
250 	ddi_remove_minor_node(dip, NULL);
251 	ddi_prop_remove_all(dip);
252 
253 	pppt_global.global_svc_state = PSS_DETACHED;
254 
255 	PPPT_GLOBAL_UNLOCK();
256 
257 	return (DDI_SUCCESS);
258 }
259 
260 /*ARGSUSED*/
261 static int
262 pppt_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
263 {
264 	int	rc = 0;
265 
266 	PPPT_GLOBAL_LOCK();
267 
268 	switch (pppt_global.global_svc_state) {
269 	case PSS_DISABLED:
270 		pppt_global.global_svc_state = PSS_ENABLING;
271 		PPPT_GLOBAL_UNLOCK();
272 		rc = pppt_enable_svc();
273 		PPPT_GLOBAL_LOCK();
274 		if (rc == 0) {
275 			pppt_global.global_svc_state = PSS_ENABLED;
276 		} else {
277 			pppt_global.global_svc_state = PSS_DISABLED;
278 		}
279 		break;
280 	case PSS_DISABLING:
281 	case PSS_ENABLING:
282 	case PSS_ENABLED:
283 		rc = EBUSY;
284 		break;
285 	default:
286 		rc = EFAULT;
287 		break;
288 	}
289 
290 	PPPT_GLOBAL_UNLOCK();
291 
292 	return (rc);
293 }
294 
295 /* ARGSUSED */
296 static int
297 pppt_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
298 {
299 	int rc = 0;
300 
301 	PPPT_GLOBAL_LOCK();
302 
303 	switch (pppt_global.global_svc_state) {
304 	case PSS_ENABLED:
305 		pppt_global.global_svc_state = PSS_DISABLING;
306 		PPPT_GLOBAL_UNLOCK();
307 		pppt_disable_svc();
308 		PPPT_GLOBAL_LOCK();
309 		pppt_global.global_svc_state = PSS_DISABLED;
310 		/*
311 		 * release the door to the daemon
312 		 */
313 		mutex_enter(&pppt_global.global_door_lock);
314 		if (pppt_global.global_door != NULL) {
315 			door_ki_rele(pppt_global.global_door);
316 			pppt_global.global_door = NULL;
317 		}
318 		mutex_exit(&pppt_global.global_door_lock);
319 		break;
320 	default:
321 		rc = EFAULT;
322 		break;
323 	}
324 
325 	PPPT_GLOBAL_UNLOCK();
326 
327 	return (rc);
328 }
329 
330 static boolean_t
331 pppt_drv_busy(void)
332 {
333 	switch (pppt_global.global_svc_state) {
334 	case PSS_DISABLED:
335 	case PSS_DETACHED:
336 		return (B_FALSE);
337 	default:
338 		return (B_TRUE);
339 	}
340 	/* NOTREACHED */
341 }
342 
343 /* ARGSUSED */
344 static int
345 pppt_drv_ioctl(dev_t drv, int cmd, intptr_t argp, int flag, cred_t *cred,
346     int *retval)
347 {
348 	int				rc;
349 	void				*buf;
350 	size_t				buf_size;
351 	pppt_iocdata_t			iocd;
352 	door_handle_t			new_handle;
353 
354 	if (drv_priv(cred) != 0) {
355 		return (EPERM);
356 	}
357 
358 	rc = ddi_copyin((void *)argp, &iocd, sizeof (iocd), flag);
359 	if (rc)
360 		return (EFAULT);
361 
362 	if (iocd.pppt_version != PPPT_VERSION_1)
363 		return (EINVAL);
364 
365 	switch (cmd) {
366 	case PPPT_MESSAGE:
367 
368 		/* XXX limit buf_size ? */
369 		buf_size = (size_t)iocd.pppt_buf_size;
370 		buf = kmem_alloc(buf_size, KM_SLEEP);
371 		if (buf == NULL)
372 			return (ENOMEM);
373 
374 		rc = ddi_copyin((void *)(unsigned long)iocd.pppt_buf,
375 		    buf, buf_size, flag);
376 		if (rc) {
377 			kmem_free(buf, buf_size);
378 			return (EFAULT);
379 		}
380 
381 		stmf_ic_rx_msg(buf, buf_size);
382 
383 		kmem_free(buf, buf_size);
384 		break;
385 	case PPPT_INSTALL_DOOR:
386 
387 		new_handle = door_ki_lookup((int)iocd.pppt_door_fd);
388 		if (new_handle == NULL)
389 			return (EINVAL);
390 
391 		mutex_enter(&pppt_global.global_door_lock);
392 		ASSERT(pppt_global.global_svc_state == PSS_ENABLED);
393 		if (pppt_global.global_door != NULL) {
394 			/*
395 			 * There can only be one door installed
396 			 */
397 			mutex_exit(&pppt_global.global_door_lock);
398 			door_ki_rele(new_handle);
399 			return (EBUSY);
400 		}
401 		pppt_global.global_door = new_handle;
402 		mutex_exit(&pppt_global.global_door_lock);
403 		break;
404 	}
405 
406 	return (rc);
407 }
408 
409 /*
410  * pppt_enable_svc
411  *
412  * registers all the configured targets and target portals with STMF
413  */
414 static int
415 pppt_enable_svc(void)
416 {
417 	stmf_port_provider_t	*pp;
418 	stmf_dbuf_store_t	*dbuf_store;
419 	int			rc = 0;
420 
421 	ASSERT(pppt_global.global_svc_state == PSS_ENABLING);
422 
423 	/*
424 	 * Make sure that can tell if we have partially allocated
425 	 * in case we need to exit and tear down anything allocated.
426 	 */
427 	pppt_global.global_dbuf_store = NULL;
428 	pp = NULL;
429 	pppt_global.global_pp = NULL;
430 	pppt_global.global_dispatch_taskq = NULL;
431 	pppt_global.global_sess_taskq = NULL;
432 
433 	avl_create(&pppt_global.global_target_list,
434 	    pppt_tgt_avl_compare, sizeof (pppt_tgt_t),
435 	    offsetof(pppt_tgt_t, target_global_ln));
436 
437 	avl_create(&pppt_global.global_sess_list,
438 	    pppt_sess_avl_compare_by_id, sizeof (pppt_sess_t),
439 	    offsetof(pppt_sess_t, ps_global_ln));
440 
441 	/*
442 	 * Setup STMF dbuf store.  Tf buffers are associated with a particular
443 	 * lport (FC, SRP) then the dbuf_store should stored in the lport
444 	 * context, otherwise (iSCSI) the dbuf_store should be global.
445 	 */
446 	dbuf_store = stmf_alloc(STMF_STRUCT_DBUF_STORE, 0, 0);
447 	if (dbuf_store == NULL) {
448 		rc = ENOMEM;
449 		goto tear_down_and_return;
450 	}
451 	dbuf_store->ds_alloc_data_buf = pppt_dbuf_alloc;
452 	dbuf_store->ds_free_data_buf = pppt_dbuf_free;
453 	dbuf_store->ds_port_private = NULL;
454 	pppt_global.global_dbuf_store = dbuf_store;
455 
456 	/* Register port provider */
457 	pp = stmf_alloc(STMF_STRUCT_PORT_PROVIDER, 0, 0);
458 	if (pp == NULL) {
459 		rc = ENOMEM;
460 		goto tear_down_and_return;
461 	}
462 
463 	pp->pp_portif_rev = PORTIF_REV_1;
464 	pp->pp_instance = 0;
465 	pp->pp_name = PPPT_MODNAME;
466 	pp->pp_cb = NULL;
467 
468 	pppt_global.global_pp = pp;
469 
470 	if (stmf_register_port_provider(pp) != STMF_SUCCESS) {
471 		rc = EIO;
472 		goto tear_down_and_return;
473 	}
474 
475 	pppt_global.global_dispatch_taskq = taskq_create("pppt_dispatch",
476 	    1, minclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE);
477 
478 	pppt_global.global_sess_taskq = taskq_create("pppt_session",
479 	    1, minclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE);
480 
481 	return (0);
482 
483 tear_down_and_return:
484 
485 	if (pppt_global.global_sess_taskq) {
486 		taskq_destroy(pppt_global.global_sess_taskq);
487 		pppt_global.global_sess_taskq = NULL;
488 	}
489 
490 	if (pppt_global.global_dispatch_taskq) {
491 		taskq_destroy(pppt_global.global_dispatch_taskq);
492 		pppt_global.global_dispatch_taskq = NULL;
493 	}
494 
495 	if (pppt_global.global_pp)
496 		pppt_global.global_pp = NULL;
497 
498 	if (pp)
499 		stmf_free(pp);
500 
501 	if (pppt_global.global_dbuf_store) {
502 		stmf_free(pppt_global.global_dbuf_store);
503 		pppt_global.global_dbuf_store = NULL;
504 	}
505 
506 	avl_destroy(&pppt_global.global_sess_list);
507 	avl_destroy(&pppt_global.global_target_list);
508 
509 	return (rc);
510 }
511 
512 /*
513  * pppt_disable_svc
514  *
515  * clean up all existing sessions and deregister targets from STMF
516  */
517 static void
518 pppt_disable_svc(void)
519 {
520 	pppt_tgt_t	*tgt, *next_tgt;
521 	avl_tree_t	delete_target_list;
522 
523 	ASSERT(pppt_global.global_svc_state == PSS_DISABLING);
524 
525 	avl_create(&delete_target_list,
526 	    pppt_tgt_avl_compare, sizeof (pppt_tgt_t),
527 	    offsetof(pppt_tgt_t, target_global_ln));
528 
529 	PPPT_GLOBAL_LOCK();
530 	for (tgt = avl_first(&pppt_global.global_target_list);
531 	    tgt != NULL;
532 	    tgt = next_tgt) {
533 		next_tgt = AVL_NEXT(&pppt_global.global_target_list, tgt);
534 		avl_remove(&pppt_global.global_target_list, tgt);
535 		avl_add(&delete_target_list, tgt);
536 		pppt_tgt_async_delete(tgt);
537 	}
538 	PPPT_GLOBAL_UNLOCK();
539 
540 	for (tgt = avl_first(&delete_target_list);
541 	    tgt != NULL;
542 	    tgt = next_tgt) {
543 		next_tgt = AVL_NEXT(&delete_target_list, tgt);
544 		mutex_enter(&tgt->target_mutex);
545 		while ((tgt->target_refcount > 0) ||
546 		    (tgt->target_state != TS_DELETING)) {
547 			cv_wait(&tgt->target_cv, &tgt->target_mutex);
548 		}
549 		mutex_exit(&tgt->target_mutex);
550 
551 		avl_remove(&delete_target_list, tgt);
552 		pppt_tgt_destroy(tgt);
553 	}
554 
555 	taskq_destroy(pppt_global.global_sess_taskq);
556 
557 	taskq_destroy(pppt_global.global_dispatch_taskq);
558 
559 	avl_destroy(&pppt_global.global_sess_list);
560 	avl_destroy(&pppt_global.global_target_list);
561 
562 	(void) stmf_deregister_port_provider(pppt_global.global_pp);
563 
564 	stmf_free(pppt_global.global_dbuf_store);
565 	pppt_global.global_dbuf_store = NULL;
566 
567 	stmf_free(pppt_global.global_pp);
568 	pppt_global.global_pp = NULL;
569 }
570 
571 /*
572  * STMF callbacks
573  */
574 
575 /*ARGSUSED*/
576 static stmf_data_buf_t *
577 pppt_dbuf_alloc(scsi_task_t *task, uint32_t size, uint32_t *pminsize,
578     uint32_t flags)
579 {
580 	stmf_data_buf_t	*result;
581 	pppt_buf_t	*pbuf;
582 	uint8_t		*buf;
583 
584 	/* Get buffer */
585 	buf = kmem_alloc(size, KM_SLEEP);
586 
587 	/*
588 	 *  Allocate stmf buf with private port provider section
589 	 * (pppt_buf_t)
590 	 */
591 	result = stmf_alloc(STMF_STRUCT_DATA_BUF, sizeof (pppt_buf_t), 0);
592 	if (result != NULL) {
593 		/* Fill in pppt_buf_t */
594 		pbuf = result->db_port_private;
595 		pbuf->pbuf_stmf_buf = result;
596 		pbuf->pbuf_is_immed = B_FALSE;
597 
598 		/*
599 		 * Fill in stmf_data_buf_t.  DB_DONT CACHE tells
600 		 * stmf not to cache buffers but STMF doesn't do
601 		 * that yet so it's a no-op.  Port providers like
602 		 * FC and SRP that have buffers associated with the
603 		 * target port would want to let STMF cache
604 		 * the buffers.  Port providers like iSCSI would
605 		 * not want STMF to cache because the buffers are
606 		 * really associated with a connection, not an
607 		 * STMF target port so there is no way for STMF
608 		 * to cache the buffers effectively.  These port
609 		 * providers should cache buffers internally if
610 		 * there is significant buffer setup overhead.
611 		 *
612 		 * And of course, since STMF doesn't do any internal
613 		 * caching right now anyway, all port providers should
614 		 * do what they can to minimize buffer setup overhead.
615 		 */
616 		result->db_flags = DB_DONT_CACHE;
617 		result->db_buf_size = size;
618 		result->db_data_size = size;
619 		result->db_sglist_length = 1;
620 		result->db_sglist[0].seg_addr = buf;
621 		result->db_sglist[0].seg_length = size;
622 		return (result);
623 	} else {
624 		/*
625 		 * Couldn't get the stmf_data_buf_t so free the
626 		 * buffer
627 		 */
628 		kmem_free(buf, size);
629 	}
630 
631 	return (NULL);
632 }
633 
634 /*ARGSUSED*/
635 static void
636 pppt_dbuf_free(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf)
637 {
638 	pppt_buf_t *pbuf = dbuf->db_port_private;
639 
640 	if (pbuf->pbuf_is_immed) {
641 		stmf_ic_msg_free(pbuf->pbuf_immed_msg);
642 	} else {
643 		kmem_free(dbuf->db_sglist[0].seg_addr,
644 		    dbuf->db_sglist[0].seg_length);
645 		stmf_free(dbuf);
646 	}
647 }
648 
649 /*ARGSUSED*/
650 stmf_status_t
651 pppt_lport_xfer_data(scsi_task_t *task, stmf_data_buf_t *dbuf,
652     uint32_t ioflags)
653 {
654 	pppt_task_t		*pppt_task = task->task_port_private;
655 	pppt_buf_t		*pbuf = dbuf->db_port_private;
656 	stmf_ic_msg_t		*msg;
657 	stmf_ic_msg_status_t	ic_msg_status;
658 
659 	/*
660 	 * If we are aborting then we can ignore this request, otherwise
661 	 * add a reference.
662 	 */
663 	if (pppt_task_hold(pppt_task) != PPPT_STATUS_SUCCESS) {
664 		return (STMF_SUCCESS);
665 	}
666 
667 	/*
668 	 * If it's not immediate data then start the transfer
669 	 */
670 	ASSERT(pbuf->pbuf_is_immed == B_FALSE);
671 	if (dbuf->db_flags & DB_DIRECTION_TO_RPORT) {
672 
673 		/* Send read data */
674 		msg = stmf_ic_scsi_data_msg_alloc(
675 		    pppt_task->pt_task_id,
676 		    pppt_task->pt_sess->ps_session_id,
677 		    pppt_task->pt_lun_id,
678 		    dbuf->db_sglist[0].seg_length,
679 		    dbuf->db_sglist[0].seg_addr, 0);
680 
681 		pppt_task->pt_read_buf = pbuf;
682 		pppt_task->pt_read_xfer_msgid = msg->icm_msgid;
683 
684 		ic_msg_status = stmf_ic_tx_msg(msg);
685 		pppt_task_rele(pppt_task);
686 		if (ic_msg_status != STMF_IC_MSG_SUCCESS) {
687 			return (STMF_FAILURE);
688 		} else {
689 			return (STMF_SUCCESS);
690 		}
691 	} else if (dbuf->db_flags & DB_DIRECTION_FROM_RPORT) {
692 		pppt_task_rele(pppt_task);
693 		return (STMF_FAILURE);
694 	}
695 
696 	pppt_task_rele(pppt_task);
697 
698 	return (STMF_INVALID_ARG);
699 }
700 
701 void
702 pppt_xfer_read_complete(pppt_task_t *pppt_task, stmf_status_t status)
703 {
704 	pppt_buf_t		*pppt_buf;
705 	stmf_data_buf_t		*dbuf;
706 
707 	/*
708 	 * Caller should have taken a task hold (likely via pppt_task_lookup)
709 	 *
710 	 * Get pppt_buf_t and stmf_data_buf_t pointers
711 	 */
712 	pppt_buf = pppt_task->pt_read_buf;
713 	dbuf = pppt_buf->pbuf_stmf_buf;
714 	dbuf->db_xfer_status = (status == STMF_SUCCESS) ?
715 	    STMF_SUCCESS : STMF_FAILURE;
716 
717 	/*
718 	 * COMSTAR currently requires port providers to support
719 	 * the DB_SEND_STATUS_GOOD flag even if phase collapse is
720 	 * not supported.  So we will roll our own... pretend we are
721 	 * COMSTAR and ask for a status message.
722 	 */
723 	if ((dbuf->db_flags & DB_SEND_STATUS_GOOD) &&
724 	    (status == STMF_SUCCESS)) {
725 		/*
726 		 * It's possible the task has been aborted since the time we
727 		 * looked it up.  We need to release the hold before calling
728 		 * pppt_lport_send_status and as soon as we release the hold
729 		 * the task may disappear.  Calling pppt_task_done allows us
730 		 * to determine whether the task has been aborted (in which
731 		 * case we will stop processing and return) and mark the task
732 		 * "done" which will prevent the task from being aborted while
733 		 * we are trying to send the status.
734 		 */
735 		if (pppt_task_done(pppt_task) != PPPT_STATUS_SUCCESS) {
736 			/* STMF will free task and buffer(s) */
737 			pppt_task_rele(pppt_task);
738 			return;
739 		}
740 		pppt_task_rele(pppt_task);
741 
742 		if (pppt_lport_send_status(pppt_task->pt_stmf_task, 0)
743 		    != STMF_SUCCESS) {
744 			/* Failed to send status */
745 			dbuf->db_xfer_status = STMF_FAILURE;
746 			stmf_data_xfer_done(pppt_task->pt_stmf_task, dbuf,
747 			    STMF_IOF_LPORT_DONE);
748 		}
749 	} else {
750 		pppt_task_rele(pppt_task);
751 		stmf_data_xfer_done(pppt_task->pt_stmf_task, dbuf, 0);
752 	}
753 }
754 
755 /*ARGSUSED*/
756 stmf_status_t
757 pppt_lport_send_status(scsi_task_t *task, uint32_t ioflags)
758 {
759 	pppt_task_t *ptask =		task->task_port_private;
760 	stmf_ic_msg_t			*msg;
761 	stmf_ic_msg_status_t		ic_msg_status;
762 
763 	/*
764 	 * Mark task completed.  If the state indicates it was aborted
765 	 * then we don't need to respond.
766 	 */
767 	if (pppt_task_done(ptask) == PPPT_STATUS_ABORTED) {
768 		return (STMF_SUCCESS);
769 	}
770 
771 	/*
772 	 * Send status.
773 	 */
774 	msg = stmf_ic_scsi_status_msg_alloc(
775 	    ptask->pt_task_id,
776 	    ptask->pt_sess->ps_session_id,
777 	    ptask->pt_lun_id,
778 	    0,
779 	    task->task_scsi_status,
780 	    task->task_status_ctrl, task->task_resid,
781 	    task->task_sense_length, task->task_sense_data, 0);
782 
783 	ic_msg_status = stmf_ic_tx_msg(msg);
784 
785 	if (ic_msg_status != STMF_IC_MSG_SUCCESS) {
786 		pppt_task_sent_status(ptask);
787 		stmf_send_status_done(ptask->pt_stmf_task,
788 		    STMF_FAILURE, STMF_IOF_LPORT_DONE);
789 		return (STMF_FAILURE);
790 	} else {
791 		pppt_task_sent_status(ptask);
792 		stmf_send_status_done(ptask->pt_stmf_task,
793 		    STMF_SUCCESS, STMF_IOF_LPORT_DONE);
794 		return (STMF_SUCCESS);
795 	}
796 }
797 
798 void
799 pppt_lport_task_free(scsi_task_t *task)
800 {
801 	pppt_task_t *ptask = task->task_port_private;
802 	pppt_sess_t *ps = ptask->pt_sess;
803 
804 	pppt_task_free(ptask);
805 	pppt_sess_rele(ps);
806 }
807 
808 /*ARGSUSED*/
809 stmf_status_t
810 pppt_lport_abort(stmf_local_port_t *lport, int abort_cmd, void *arg,
811     uint32_t flags)
812 {
813 	scsi_task_t	*st = (scsi_task_t *)arg;
814 	pppt_task_t	*ptask;
815 
816 	ptask = st->task_port_private;
817 
818 	if (pppt_task_try_abort(ptask) == PPPT_STATUS_DONE) {
819 		/*
820 		 * This task is beyond the point where abort makes sense
821 		 * and we will soon be sending status.  Tell STMF to
822 		 * go away.
823 		 */
824 		return (STMF_BUSY);
825 	} else {
826 		return (STMF_ABORT_SUCCESS);
827 	}
828 	/*NOTREACHED*/
829 }
830 
831 /*ARGSUSED*/
832 void
833 pppt_lport_ctl(stmf_local_port_t *lport, int cmd, void *arg)
834 {
835 	switch (cmd) {
836 	case STMF_CMD_LPORT_ONLINE:
837 	case STMF_CMD_LPORT_OFFLINE:
838 	case STMF_ACK_LPORT_ONLINE_COMPLETE:
839 	case STMF_ACK_LPORT_OFFLINE_COMPLETE:
840 		pppt_tgt_sm_ctl(lport, cmd, arg);
841 		break;
842 
843 	default:
844 		ASSERT(0);
845 		break;
846 	}
847 }
848 
849 pppt_sess_t *
850 pppt_sess_lookup_locked(uint64_t session_id,
851     scsi_devid_desc_t *lport_devid, stmf_remote_port_t *rport)
852 {
853 	pppt_tgt_t				*tgt;
854 	pppt_sess_t				*ps;
855 	int					lport_cmp;
856 
857 	ASSERT(mutex_owned(&pppt_global.global_lock));
858 
859 	/*
860 	 * Look for existing session for this ID
861 	 */
862 	ps = pppt_sess_lookup_by_id_locked(session_id);
863 	if (ps == NULL) {
864 		PPPT_INC_STAT(es_sess_lookup_no_session);
865 		return (NULL);
866 	}
867 
868 	tgt = ps->ps_target;
869 
870 	mutex_enter(&tgt->target_mutex);
871 
872 	/* Validate local/remote port names */
873 	if ((lport_devid->ident_length !=
874 	    tgt->target_stmf_lport->lport_id->ident_length) ||
875 	    (rport->rport_tptid_sz !=
876 	    ps->ps_stmf_sess->ss_rport->rport_tptid_sz)) {
877 		mutex_exit(&tgt->target_mutex);
878 		PPPT_INC_STAT(es_sess_lookup_ident_mismatch);
879 		return (NULL);
880 	} else {
881 		lport_cmp = bcmp(lport_devid->ident,
882 		    tgt->target_stmf_lport->lport_id->ident,
883 		    lport_devid->ident_length);
884 		if (lport_cmp != 0 ||
885 		    (stmf_scsilib_tptid_compare(rport->rport_tptid,
886 		    ps->ps_stmf_sess->ss_rport->rport_tptid) != B_TRUE)) {
887 			mutex_exit(&tgt->target_mutex);
888 			PPPT_INC_STAT(es_sess_lookup_ident_mismatch);
889 			return (NULL);
890 		}
891 
892 		if (tgt->target_state != TS_STMF_ONLINE) {
893 			mutex_exit(&tgt->target_mutex);
894 			PPPT_INC_STAT(es_sess_lookup_bad_tgt_state);
895 			return (NULL);
896 		}
897 	}
898 	mutex_exit(&tgt->target_mutex);
899 
900 	return (ps);
901 }
902 
903 pppt_sess_t *
904 pppt_sess_lookup_by_id_locked(uint64_t session_id)
905 {
906 	pppt_sess_t		tmp_ps;
907 	pppt_sess_t		*ps;
908 
909 	ASSERT(mutex_owned(&pppt_global.global_lock));
910 	tmp_ps.ps_session_id = session_id;
911 	tmp_ps.ps_closed = 0;
912 	ps = avl_find(&pppt_global.global_sess_list, &tmp_ps, NULL);
913 	if (ps != NULL) {
914 		mutex_enter(&ps->ps_mutex);
915 		if (!ps->ps_closed) {
916 			ps->ps_refcnt++;
917 			mutex_exit(&ps->ps_mutex);
918 			return (ps);
919 		}
920 		mutex_exit(&ps->ps_mutex);
921 	}
922 
923 	return (NULL);
924 }
925 
926 /* New session */
927 pppt_sess_t *
928 pppt_sess_lookup_create(scsi_devid_desc_t *lport_devid,
929     scsi_devid_desc_t *rport_devid, stmf_remote_port_t *rport,
930     uint64_t session_id, stmf_status_t *statusp)
931 {
932 	pppt_tgt_t		*tgt;
933 	pppt_sess_t		*ps;
934 	stmf_scsi_session_t	*ss;
935 	pppt_sess_t		tmp_ps;
936 	stmf_scsi_session_t	tmp_ss;
937 	*statusp = STMF_SUCCESS;
938 
939 	PPPT_GLOBAL_LOCK();
940 
941 	/*
942 	 * Look for existing session for this ID
943 	 */
944 	ps = pppt_sess_lookup_locked(session_id, lport_devid, rport);
945 
946 	if (ps != NULL) {
947 		PPPT_GLOBAL_UNLOCK();
948 		return (ps);
949 	}
950 
951 	/*
952 	 * No session with that ID, look for another session corresponding
953 	 * to the same IT nexus.
954 	 */
955 	tgt = pppt_tgt_lookup_locked(lport_devid);
956 	if (tgt == NULL) {
957 		*statusp = STMF_NOT_FOUND;
958 		PPPT_GLOBAL_UNLOCK();
959 		return (NULL);
960 	}
961 
962 	mutex_enter(&tgt->target_mutex);
963 	if (tgt->target_state != TS_STMF_ONLINE) {
964 		*statusp = STMF_NOT_FOUND;
965 		mutex_exit(&tgt->target_mutex);
966 		PPPT_GLOBAL_UNLOCK();
967 		/* Can't create session to offline target */
968 		return (NULL);
969 	}
970 
971 	bzero(&tmp_ps, sizeof (tmp_ps));
972 	bzero(&tmp_ss, sizeof (tmp_ss));
973 	tmp_ps.ps_stmf_sess = &tmp_ss;
974 	tmp_ss.ss_rport = rport;
975 
976 	/*
977 	 * Look for an existing session on this IT nexus
978 	 */
979 	ps = avl_find(&tgt->target_sess_list, &tmp_ps, NULL);
980 
981 	if (ps != NULL) {
982 		/*
983 		 * Now check the session ID.  It should not match because if
984 		 * it did we would have found it on the global session list.
985 		 * If the session ID in the command is higher than the existing
986 		 * session ID then we need to tear down the existing session.
987 		 */
988 		mutex_enter(&ps->ps_mutex);
989 		ASSERT(ps->ps_session_id != session_id);
990 		if (ps->ps_session_id > session_id) {
991 			/* Invalid session ID */
992 			mutex_exit(&ps->ps_mutex);
993 			mutex_exit(&tgt->target_mutex);
994 			PPPT_GLOBAL_UNLOCK();
995 			*statusp = STMF_INVALID_ARG;
996 			return (NULL);
997 		} else {
998 			/* Existing session needs to be invalidated */
999 			if (!ps->ps_closed) {
1000 				pppt_sess_close_locked(ps);
1001 			}
1002 		}
1003 		mutex_exit(&ps->ps_mutex);
1004 
1005 		/* Fallthrough and create new session */
1006 	}
1007 
1008 	/*
1009 	 * Allocate and fill in pppt_session_t with the appropriate data
1010 	 * for the protocol.
1011 	 */
1012 	ps = kmem_zalloc(sizeof (*ps), KM_SLEEP);
1013 
1014 	/* Fill in session fields */
1015 	ps->ps_target = tgt;
1016 	ps->ps_session_id = session_id;
1017 
1018 	ss = stmf_alloc(STMF_STRUCT_SCSI_SESSION, 0,
1019 	    0);
1020 	if (ss == NULL) {
1021 		mutex_exit(&tgt->target_mutex);
1022 		PPPT_GLOBAL_UNLOCK();
1023 		kmem_free(ps, sizeof (*ps));
1024 		*statusp = STMF_ALLOC_FAILURE;
1025 		return (NULL);
1026 	}
1027 
1028 	ss->ss_rport_id = kmem_zalloc(sizeof (scsi_devid_desc_t) +
1029 	    rport_devid->ident_length + 1, KM_SLEEP);
1030 	bcopy(rport_devid, ss->ss_rport_id,
1031 	    sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1);
1032 
1033 	ss->ss_lport = tgt->target_stmf_lport;
1034 
1035 	ss->ss_rport = stmf_remote_port_alloc(rport->rport_tptid_sz);
1036 	bcopy(rport->rport_tptid, ss->ss_rport->rport_tptid,
1037 	    rport->rport_tptid_sz);
1038 
1039 	if (stmf_register_scsi_session(tgt->target_stmf_lport, ss) !=
1040 	    STMF_SUCCESS) {
1041 		mutex_exit(&tgt->target_mutex);
1042 		PPPT_GLOBAL_UNLOCK();
1043 		kmem_free(ss->ss_rport_id,
1044 		    sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1);
1045 		stmf_remote_port_free(ss->ss_rport);
1046 		stmf_free(ss);
1047 		kmem_free(ps, sizeof (*ps));
1048 		*statusp = STMF_TARGET_FAILURE;
1049 		return (NULL);
1050 	}
1051 
1052 	ss->ss_port_private = ps;
1053 	mutex_init(&ps->ps_mutex, NULL, MUTEX_DEFAULT, NULL);
1054 	cv_init(&ps->ps_cv, NULL, CV_DEFAULT, NULL);
1055 	avl_create(&ps->ps_task_list, pppt_task_avl_compare,
1056 	    sizeof (pppt_task_t), offsetof(pppt_task_t, pt_sess_ln));
1057 	ps->ps_refcnt = 1;
1058 	ps->ps_stmf_sess = ss;
1059 	avl_add(&tgt->target_sess_list, ps);
1060 	avl_add(&pppt_global.global_sess_list, ps);
1061 	mutex_exit(&tgt->target_mutex);
1062 	PPPT_GLOBAL_UNLOCK();
1063 	stmf_trace("pppt", "New session %p", (void *)ps);
1064 
1065 	return (ps);
1066 }
1067 
1068 void
1069 pppt_sess_rele(pppt_sess_t *ps)
1070 {
1071 	mutex_enter(&ps->ps_mutex);
1072 	pppt_sess_rele_locked(ps);
1073 	mutex_exit(&ps->ps_mutex);
1074 }
1075 
1076 void
1077 pppt_sess_rele_locked(pppt_sess_t *ps)
1078 {
1079 	ASSERT(mutex_owned(&ps->ps_mutex));
1080 	ps->ps_refcnt--;
1081 	if (ps->ps_refcnt == 0) {
1082 		cv_signal(&ps->ps_cv);
1083 	}
1084 }
1085 
1086 static void pppt_sess_destroy_task(void *ps_void)
1087 {
1088 	pppt_sess_t *ps = ps_void;
1089 	stmf_scsi_session_t	*ss;
1090 
1091 	stmf_trace("pppt", "Session destroy task %p", (void *)ps);
1092 
1093 	ss = ps->ps_stmf_sess;
1094 	mutex_enter(&ps->ps_mutex);
1095 	stmf_deregister_scsi_session(ss->ss_lport, ss);
1096 	kmem_free(ss->ss_rport_id,
1097 	    sizeof (scsi_devid_desc_t) + ss->ss_rport_id->ident_length + 1);
1098 	stmf_remote_port_free(ss->ss_rport);
1099 	avl_destroy(&ps->ps_task_list);
1100 	mutex_exit(&ps->ps_mutex);
1101 	cv_destroy(&ps->ps_cv);
1102 	mutex_destroy(&ps->ps_mutex);
1103 	stmf_free(ps->ps_stmf_sess);
1104 	kmem_free(ps, sizeof (*ps));
1105 
1106 	stmf_trace("pppt", "Session destroy task complete %p", (void *)ps);
1107 }
1108 
1109 int
1110 pppt_sess_avl_compare_by_id(const void *void_sess1, const void *void_sess2)
1111 {
1112 	const	pppt_sess_t	*psess1 = void_sess1;
1113 	const	pppt_sess_t	*psess2 = void_sess2;
1114 
1115 	if (psess1->ps_session_id < psess2->ps_session_id)
1116 		return (-1);
1117 	else if (psess1->ps_session_id > psess2->ps_session_id)
1118 		return (1);
1119 
1120 	/* Allow multiple duplicate sessions if one is closed */
1121 	ASSERT(!(psess1->ps_closed && psess2->ps_closed));
1122 	if (psess1->ps_closed)
1123 		return (-1);
1124 	else if (psess2->ps_closed)
1125 		return (1);
1126 
1127 	return (0);
1128 }
1129 
1130 int
1131 pppt_sess_avl_compare_by_name(const void *void_sess1, const void *void_sess2)
1132 {
1133 	const	pppt_sess_t	*psess1 = void_sess1;
1134 	const	pppt_sess_t	*psess2 = void_sess2;
1135 	int			result;
1136 
1137 	/* Compare by tptid size */
1138 	if (psess1->ps_stmf_sess->ss_rport->rport_tptid_sz <
1139 	    psess2->ps_stmf_sess->ss_rport->rport_tptid_sz) {
1140 		return (-1);
1141 	} else if (psess1->ps_stmf_sess->ss_rport->rport_tptid_sz >
1142 	    psess2->ps_stmf_sess->ss_rport->rport_tptid_sz) {
1143 		return (1);
1144 	}
1145 
1146 	/* Now compare tptid */
1147 	result = memcmp(psess1->ps_stmf_sess->ss_rport->rport_tptid,
1148 	    psess2->ps_stmf_sess->ss_rport->rport_tptid,
1149 	    psess1->ps_stmf_sess->ss_rport->rport_tptid_sz);
1150 
1151 	if (result < 0) {
1152 		return (-1);
1153 	} else if (result > 0) {
1154 		return (1);
1155 	}
1156 
1157 	return (0);
1158 }
1159 
1160 void
1161 pppt_sess_close_locked(pppt_sess_t *ps)
1162 {
1163 	pppt_tgt_t	*tgt = ps->ps_target;
1164 	pppt_task_t	*ptask;
1165 
1166 	stmf_trace("pppt", "Session close %p", (void *)ps);
1167 
1168 	ASSERT(mutex_owned(&pppt_global.global_lock));
1169 	ASSERT(mutex_owned(&tgt->target_mutex));
1170 	ASSERT(mutex_owned(&ps->ps_mutex));
1171 	ASSERT(!ps->ps_closed); /* Caller should ensure session is not closed */
1172 
1173 	ps->ps_closed = B_TRUE;
1174 	for (ptask = avl_first(&ps->ps_task_list); ptask != NULL;
1175 	    ptask = AVL_NEXT(&ps->ps_task_list, ptask)) {
1176 		mutex_enter(&ptask->pt_mutex);
1177 		if (ptask->pt_state == PTS_ACTIVE) {
1178 			stmf_abort(STMF_QUEUE_TASK_ABORT, ptask->pt_stmf_task,
1179 			    STMF_ABORTED, NULL);
1180 		}
1181 		mutex_exit(&ptask->pt_mutex);
1182 	}
1183 
1184 	/*
1185 	 * Now that all the tasks are aborting the session refcnt should
1186 	 * go to 0.
1187 	 */
1188 	while (ps->ps_refcnt != 0) {
1189 		cv_wait(&ps->ps_cv, &ps->ps_mutex);
1190 	}
1191 
1192 	avl_remove(&tgt->target_sess_list, ps);
1193 	avl_remove(&pppt_global.global_sess_list, ps);
1194 	(void) taskq_dispatch(pppt_global.global_sess_taskq,
1195 	    &pppt_sess_destroy_task, ps, KM_SLEEP);
1196 
1197 	stmf_trace("pppt", "Session close complete %p", (void *)ps);
1198 }
1199 
1200 pppt_task_t *
1201 pppt_task_alloc(void)
1202 {
1203 	pppt_task_t	*ptask;
1204 	pppt_buf_t	*immed_pbuf;
1205 
1206 	ptask = kmem_alloc(sizeof (pppt_task_t) + sizeof (pppt_buf_t) +
1207 	    sizeof (stmf_data_buf_t), KM_NOSLEEP);
1208 	if (ptask != NULL) {
1209 		ptask->pt_state = PTS_INIT;
1210 		ptask->pt_read_buf = NULL;
1211 		ptask->pt_read_xfer_msgid = 0;
1212 		cv_init(&ptask->pt_cv, NULL, CV_DRIVER, NULL);
1213 		mutex_init(&ptask->pt_mutex, NULL, MUTEX_DRIVER, NULL);
1214 		immed_pbuf = (pppt_buf_t *)(ptask + 1);
1215 		bzero(immed_pbuf, sizeof (*immed_pbuf));
1216 		immed_pbuf->pbuf_is_immed = B_TRUE;
1217 		immed_pbuf->pbuf_stmf_buf = (stmf_data_buf_t *)(immed_pbuf + 1);
1218 
1219 		bzero(immed_pbuf->pbuf_stmf_buf, sizeof (stmf_data_buf_t));
1220 		immed_pbuf->pbuf_stmf_buf->db_port_private = immed_pbuf;
1221 		immed_pbuf->pbuf_stmf_buf->db_sglist_length = 1;
1222 		immed_pbuf->pbuf_stmf_buf->db_flags = DB_DIRECTION_FROM_RPORT |
1223 		    DB_DONT_CACHE;
1224 		ptask->pt_immed_data = immed_pbuf;
1225 	}
1226 
1227 	return (ptask);
1228 
1229 }
1230 
1231 void
1232 pppt_task_free(pppt_task_t *ptask)
1233 {
1234 	mutex_enter(&ptask->pt_mutex);
1235 	mutex_destroy(&ptask->pt_mutex);
1236 	cv_destroy(&ptask->pt_cv);
1237 	kmem_free(ptask, sizeof (pppt_task_t) + sizeof (pppt_buf_t) +
1238 	    sizeof (stmf_data_buf_t));
1239 }
1240 
1241 pppt_status_t
1242 pppt_task_start(pppt_task_t *ptask)
1243 {
1244 	avl_index_t		where;
1245 
1246 	ASSERT(ptask->pt_state == PTS_INIT);
1247 
1248 	mutex_enter(&ptask->pt_sess->ps_mutex);
1249 	mutex_enter(&ptask->pt_mutex);
1250 	if (avl_find(&ptask->pt_sess->ps_task_list, ptask, &where) == NULL) {
1251 		pppt_task_update_state(ptask, PTS_ACTIVE);
1252 		avl_insert(&ptask->pt_sess->ps_task_list, ptask, where);
1253 		mutex_exit(&ptask->pt_mutex);
1254 		mutex_exit(&ptask->pt_sess->ps_mutex);
1255 		return (PPPT_STATUS_SUCCESS);
1256 	}
1257 	mutex_exit(&ptask->pt_mutex);
1258 	mutex_exit(&ptask->pt_sess->ps_mutex);
1259 
1260 	return (PPPT_STATUS_FAIL);
1261 }
1262 
1263 pppt_status_t
1264 pppt_task_done(pppt_task_t *ptask)
1265 {
1266 	pppt_status_t	pppt_status = PPPT_STATUS_SUCCESS;
1267 	boolean_t	remove = B_FALSE;
1268 
1269 	mutex_enter(&ptask->pt_mutex);
1270 
1271 	switch (ptask->pt_state) {
1272 	case PTS_ACTIVE:
1273 		remove = B_TRUE;
1274 		pppt_task_update_state(ptask, PTS_DONE);
1275 		break;
1276 	case PTS_ABORTED:
1277 		pppt_status = PPPT_STATUS_ABORTED;
1278 		break;
1279 	case PTS_DONE:
1280 		/* Repeat calls are OK.  Do nothing, return success */
1281 		break;
1282 	default:
1283 		ASSERT(0);
1284 	}
1285 
1286 	mutex_exit(&ptask->pt_mutex);
1287 
1288 	if (remove) {
1289 		mutex_enter(&ptask->pt_sess->ps_mutex);
1290 		avl_remove(&ptask->pt_sess->ps_task_list, ptask);
1291 		mutex_exit(&ptask->pt_sess->ps_mutex);
1292 	}
1293 
1294 	return (pppt_status);
1295 }
1296 
1297 void
1298 pppt_task_sent_status(pppt_task_t *ptask)
1299 {
1300 	/*
1301 	 * If STMF tries to abort a task after the task state changed to
1302 	 * PTS_DONE (meaning all task processing is complete from
1303 	 * the port provider perspective) then we return STMF_BUSY
1304 	 * from pppt_lport_abort.  STMF will return after a short interval
1305 	 * but our calls to stmf_send_status_done will be ignored since
1306 	 * STMF is aborting the task.  That's where this state comes in.
1307 	 * This state essentially says we are calling stmf_send_status_done
1308 	 * so we will not be touching the task again.  The next time
1309 	 * STMF calls pppt_lport_abort we will return a success full
1310 	 * status and the abort will succeed.
1311 	 */
1312 	mutex_enter(&ptask->pt_mutex);
1313 	pppt_task_update_state(ptask, PTS_SENT_STATUS);
1314 	mutex_exit(&ptask->pt_mutex);
1315 }
1316 
1317 pppt_task_t *
1318 pppt_task_lookup(stmf_ic_msgid_t msgid)
1319 {
1320 	pppt_tgt_t	*tgt;
1321 	pppt_sess_t	*sess;
1322 	pppt_task_t	lookup_task;
1323 	pppt_task_t	*result;
1324 
1325 	bzero(&lookup_task, sizeof (lookup_task));
1326 	lookup_task.pt_task_id = msgid;
1327 	PPPT_GLOBAL_LOCK();
1328 	for (tgt = avl_first(&pppt_global.global_target_list); tgt != NULL;
1329 	    tgt = AVL_NEXT(&pppt_global.global_target_list, tgt)) {
1330 
1331 		mutex_enter(&tgt->target_mutex);
1332 		for (sess = avl_first(&tgt->target_sess_list); sess != NULL;
1333 		    sess = AVL_NEXT(&tgt->target_sess_list, sess)) {
1334 			mutex_enter(&sess->ps_mutex);
1335 			if ((result = avl_find(&sess->ps_task_list,
1336 			    &lookup_task, NULL)) != NULL) {
1337 				if (pppt_task_hold(result) !=
1338 				    PPPT_STATUS_SUCCESS) {
1339 					result = NULL;
1340 				}
1341 				mutex_exit(&sess->ps_mutex);
1342 				mutex_exit(&tgt->target_mutex);
1343 				PPPT_GLOBAL_UNLOCK();
1344 				return (result);
1345 			}
1346 			mutex_exit(&sess->ps_mutex);
1347 		}
1348 		mutex_exit(&tgt->target_mutex);
1349 	}
1350 	PPPT_GLOBAL_UNLOCK();
1351 
1352 	return (NULL);
1353 }
1354 
1355 static int
1356 pppt_task_avl_compare(const void *void_task1, const void *void_task2)
1357 {
1358 	const pppt_task_t	*ptask1 = void_task1;
1359 	const pppt_task_t	*ptask2 = void_task2;
1360 
1361 	if (ptask1->pt_task_id < ptask2->pt_task_id)
1362 		return (-1);
1363 	else if (ptask1->pt_task_id > ptask2->pt_task_id)
1364 		return (1);
1365 
1366 	return (0);
1367 }
1368 
1369 static pppt_status_t
1370 pppt_task_try_abort(pppt_task_t *ptask)
1371 {
1372 	boolean_t	remove = B_FALSE;
1373 	pppt_status_t	pppt_status = PPPT_STATUS_SUCCESS;
1374 
1375 	mutex_enter(&ptask->pt_mutex);
1376 
1377 	switch (ptask->pt_state) {
1378 	case PTS_ACTIVE:
1379 		remove = B_TRUE;
1380 		pppt_task_update_state(ptask, PTS_ABORTED);
1381 		break;
1382 	case PTS_DONE:
1383 		pppt_status = PPPT_STATUS_DONE;
1384 		break;
1385 	case PTS_SENT_STATUS:
1386 		/*
1387 		 * Already removed so leave remove set to B_FALSE
1388 		 * and leave status set to PPPT_STATUS_SUCCESS.
1389 		 */
1390 		pppt_task_update_state(ptask, PTS_ABORTED);
1391 		break;
1392 	case PTS_ABORTED:
1393 		break;
1394 	default:
1395 		ASSERT(0);
1396 	}
1397 
1398 	mutex_exit(&ptask->pt_mutex);
1399 
1400 	if (remove) {
1401 		mutex_enter(&ptask->pt_sess->ps_mutex);
1402 		avl_remove(&ptask->pt_sess->ps_task_list, ptask);
1403 		mutex_exit(&ptask->pt_sess->ps_mutex);
1404 	}
1405 
1406 	return (pppt_status);
1407 }
1408 
1409 static pppt_status_t
1410 pppt_task_hold(pppt_task_t *ptask)
1411 {
1412 	pppt_status_t	pppt_status = PPPT_STATUS_SUCCESS;
1413 
1414 	mutex_enter(&ptask->pt_mutex);
1415 	if (ptask->pt_state == PTS_ACTIVE) {
1416 		ptask->pt_refcnt++;
1417 	} else {
1418 		pppt_status = PPPT_STATUS_FAIL;
1419 	}
1420 	mutex_exit(&ptask->pt_mutex);
1421 
1422 	return (pppt_status);
1423 }
1424 
1425 static void
1426 pppt_task_rele(pppt_task_t *ptask)
1427 {
1428 	mutex_enter(&ptask->pt_mutex);
1429 	ptask->pt_refcnt--;
1430 	cv_signal(&ptask->pt_cv);
1431 	mutex_exit(&ptask->pt_mutex);
1432 }
1433 
1434 static void
1435 pppt_task_update_state(pppt_task_t *ptask,
1436     pppt_task_state_t new_state)
1437 {
1438 	PPPT_LOG(CE_NOTE, "task %p %d -> %d", (void *)ptask,
1439 	    ptask->pt_state, new_state);
1440 
1441 	ASSERT(mutex_owned(&ptask->pt_mutex));
1442 	ptask->pt_state = new_state;
1443 	cv_signal(&ptask->pt_cv);
1444 }
1445