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,
852     scsi_devid_desc_t *rport_devid)
853 {
854 	pppt_tgt_t				*tgt;
855 	pppt_sess_t				*ps;
856 	int					lport_cmp;
857 	int					rport_cmp;
858 
859 	ASSERT(mutex_owned(&pppt_global.global_lock));
860 
861 	/*
862 	 * Look for existing session for this ID
863 	 */
864 	ps = pppt_sess_lookup_by_id_locked(session_id);
865 	if (ps == NULL) {
866 		PPPT_INC_STAT(es_sess_lookup_no_session);
867 		return (NULL);
868 	}
869 
870 	tgt = ps->ps_target;
871 
872 	mutex_enter(&tgt->target_mutex);
873 
874 	/* Validate local/remote port names */
875 	if ((lport_devid->ident_length !=
876 	    tgt->target_stmf_lport->lport_id->ident_length) ||
877 	    (rport_devid->ident_length !=
878 	    ps->ps_stmf_sess->ss_rport_id->ident_length)) {
879 		mutex_exit(&tgt->target_mutex);
880 		PPPT_INC_STAT(es_sess_lookup_ident_mismatch);
881 		return (NULL);
882 	} else {
883 		lport_cmp = bcmp(lport_devid->ident,
884 		    tgt->target_stmf_lport->lport_id->ident,
885 		    lport_devid->ident_length);
886 		rport_cmp = bcmp(rport_devid->ident,
887 		    ps->ps_stmf_sess->ss_rport_id->ident,
888 		    rport_devid->ident_length);
889 		if (lport_cmp || rport_cmp) {
890 			mutex_exit(&tgt->target_mutex);
891 			PPPT_INC_STAT(es_sess_lookup_ident_mismatch);
892 			return (NULL);
893 		}
894 
895 		if (tgt->target_state != TS_STMF_ONLINE) {
896 			mutex_exit(&tgt->target_mutex);
897 			PPPT_INC_STAT(es_sess_lookup_bad_tgt_state);
898 			return (NULL);
899 		}
900 	}
901 	mutex_exit(&tgt->target_mutex);
902 
903 	return (ps);
904 }
905 
906 pppt_sess_t *
907 pppt_sess_lookup_by_id_locked(uint64_t session_id)
908 {
909 	pppt_sess_t		tmp_ps;
910 	pppt_sess_t		*ps;
911 
912 	ASSERT(mutex_owned(&pppt_global.global_lock));
913 	tmp_ps.ps_session_id = session_id;
914 	tmp_ps.ps_closed = 0;
915 	ps = avl_find(&pppt_global.global_sess_list, &tmp_ps, NULL);
916 	if (ps != NULL) {
917 		mutex_enter(&ps->ps_mutex);
918 		if (!ps->ps_closed) {
919 			ps->ps_refcnt++;
920 			mutex_exit(&ps->ps_mutex);
921 			return (ps);
922 		}
923 		mutex_exit(&ps->ps_mutex);
924 	}
925 
926 	return (NULL);
927 }
928 
929 /* New session */
930 pppt_sess_t *
931 pppt_sess_lookup_create(scsi_devid_desc_t *lport_devid,
932     scsi_devid_desc_t *rport_devid, uint64_t session_id,
933     stmf_status_t *statusp)
934 {
935 	pppt_tgt_t		*tgt;
936 	pppt_sess_t		*ps;
937 	stmf_scsi_session_t	*ss;
938 	pppt_sess_t		tmp_ps;
939 	stmf_scsi_session_t	tmp_ss;
940 	*statusp = STMF_SUCCESS;
941 
942 	PPPT_GLOBAL_LOCK();
943 
944 	/*
945 	 * Look for existing session for this ID
946 	 */
947 	ps = pppt_sess_lookup_locked(session_id, lport_devid, rport_devid);
948 
949 	if (ps != NULL) {
950 		PPPT_GLOBAL_UNLOCK();
951 		return (ps);
952 	}
953 
954 	/*
955 	 * No session with that ID, look for another session corresponding
956 	 * to the same IT nexus.
957 	 */
958 	tgt = pppt_tgt_lookup_locked(lport_devid);
959 	if (tgt == NULL) {
960 		*statusp = STMF_NOT_FOUND;
961 		PPPT_GLOBAL_UNLOCK();
962 		return (NULL);
963 	}
964 
965 	mutex_enter(&tgt->target_mutex);
966 	if (tgt->target_state != TS_STMF_ONLINE) {
967 		*statusp = STMF_NOT_FOUND;
968 		mutex_exit(&tgt->target_mutex);
969 		PPPT_GLOBAL_UNLOCK();
970 		/* Can't create session to offline target */
971 		return (NULL);
972 	}
973 
974 	bzero(&tmp_ps, sizeof (tmp_ps));
975 	bzero(&tmp_ss, sizeof (tmp_ss));
976 	tmp_ps.ps_stmf_sess = &tmp_ss;
977 	tmp_ss.ss_rport_id = rport_devid;
978 
979 	/*
980 	 * Look for an existing session on this IT nexus
981 	 */
982 	ps = avl_find(&tgt->target_sess_list, &tmp_ps, NULL);
983 
984 	if (ps != NULL) {
985 		/*
986 		 * Now check the session ID.  It should not match because if
987 		 * it did we would have found it on the global session list.
988 		 * If the session ID in the command is higher than the existing
989 		 * session ID then we need to tear down the existing session.
990 		 */
991 		mutex_enter(&ps->ps_mutex);
992 		ASSERT(ps->ps_session_id != session_id);
993 		if (ps->ps_session_id > session_id) {
994 			/* Invalid session ID */
995 			mutex_exit(&ps->ps_mutex);
996 			mutex_exit(&tgt->target_mutex);
997 			PPPT_GLOBAL_UNLOCK();
998 			*statusp = STMF_INVALID_ARG;
999 			return (NULL);
1000 		} else {
1001 			/* Existing session needs to be invalidated */
1002 			if (!ps->ps_closed) {
1003 				pppt_sess_close_locked(ps);
1004 			}
1005 		}
1006 		mutex_exit(&ps->ps_mutex);
1007 
1008 		/* Fallthrough and create new session */
1009 	}
1010 
1011 	/*
1012 	 * Allocate and fill in pppt_session_t with the appropriate data
1013 	 * for the protocol.
1014 	 */
1015 	ps = kmem_zalloc(sizeof (*ps), KM_SLEEP);
1016 
1017 	/* Fill in session fields */
1018 	ps->ps_target = tgt;
1019 	ps->ps_session_id = session_id;
1020 
1021 	ss = stmf_alloc(STMF_STRUCT_SCSI_SESSION, 0,
1022 	    0);
1023 	if (ss == NULL) {
1024 		mutex_exit(&tgt->target_mutex);
1025 		PPPT_GLOBAL_UNLOCK();
1026 		kmem_free(ps, sizeof (*ps));
1027 		*statusp = STMF_ALLOC_FAILURE;
1028 		return (NULL);
1029 	}
1030 
1031 	ss->ss_rport_id = kmem_zalloc(sizeof (scsi_devid_desc_t) +
1032 	    rport_devid->ident_length + 1, KM_SLEEP);
1033 	bcopy(rport_devid, ss->ss_rport_id,
1034 	    sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1);
1035 
1036 	ss->ss_lport = tgt->target_stmf_lport;
1037 
1038 	if (stmf_register_scsi_session(tgt->target_stmf_lport, ss) !=
1039 	    STMF_SUCCESS) {
1040 		mutex_exit(&tgt->target_mutex);
1041 		PPPT_GLOBAL_UNLOCK();
1042 		kmem_free(ss->ss_rport_id,
1043 		    sizeof (scsi_devid_desc_t) +
1044 		    rport_devid->ident_length + 1);
1045 		stmf_free(ss);
1046 		kmem_free(ps, sizeof (*ps));
1047 		*statusp = STMF_TARGET_FAILURE;
1048 		return (NULL);
1049 	}
1050 
1051 	ss->ss_port_private = ps;
1052 	mutex_init(&ps->ps_mutex, NULL, MUTEX_DEFAULT, NULL);
1053 	cv_init(&ps->ps_cv, NULL, CV_DEFAULT, NULL);
1054 	avl_create(&ps->ps_task_list, pppt_task_avl_compare,
1055 	    sizeof (pppt_task_t), offsetof(pppt_task_t, pt_sess_ln));
1056 	ps->ps_refcnt = 1;
1057 	ps->ps_stmf_sess = ss;
1058 	avl_add(&tgt->target_sess_list, ps);
1059 	avl_add(&pppt_global.global_sess_list, ps);
1060 	mutex_exit(&tgt->target_mutex);
1061 	PPPT_GLOBAL_UNLOCK();
1062 	stmf_trace("pppt", "New session %p", (void *)ps);
1063 
1064 	return (ps);
1065 }
1066 
1067 void
1068 pppt_sess_rele(pppt_sess_t *ps)
1069 {
1070 	mutex_enter(&ps->ps_mutex);
1071 	pppt_sess_rele_locked(ps);
1072 	mutex_exit(&ps->ps_mutex);
1073 }
1074 
1075 void
1076 pppt_sess_rele_locked(pppt_sess_t *ps)
1077 {
1078 	ASSERT(mutex_owned(&ps->ps_mutex));
1079 	ps->ps_refcnt--;
1080 	if (ps->ps_refcnt == 0) {
1081 		cv_signal(&ps->ps_cv);
1082 	}
1083 }
1084 
1085 static void pppt_sess_destroy_task(void *ps_void)
1086 {
1087 	pppt_sess_t *ps = ps_void;
1088 	stmf_scsi_session_t	*ss;
1089 
1090 	stmf_trace("pppt", "Session destroy task %p", (void *)ps);
1091 
1092 	ss = ps->ps_stmf_sess;
1093 	mutex_enter(&ps->ps_mutex);
1094 	stmf_deregister_scsi_session(ss->ss_lport, ss);
1095 	kmem_free(ss->ss_rport_id,
1096 	    sizeof (scsi_devid_desc_t) +
1097 	    ss->ss_rport_id->ident_length + 1);
1098 	avl_destroy(&ps->ps_task_list);
1099 	mutex_exit(&ps->ps_mutex);
1100 	cv_destroy(&ps->ps_cv);
1101 	mutex_destroy(&ps->ps_mutex);
1102 	stmf_free(ps->ps_stmf_sess);
1103 	kmem_free(ps, sizeof (*ps));
1104 
1105 	stmf_trace("pppt", "Session destroy task complete %p", (void *)ps);
1106 }
1107 
1108 int
1109 pppt_sess_avl_compare_by_id(const void *void_sess1, const void *void_sess2)
1110 {
1111 	const	pppt_sess_t	*psess1 = void_sess1;
1112 	const	pppt_sess_t	*psess2 = void_sess2;
1113 
1114 	if (psess1->ps_session_id < psess2->ps_session_id)
1115 		return (-1);
1116 	else if (psess1->ps_session_id > psess2->ps_session_id)
1117 		return (1);
1118 
1119 	/* Allow multiple duplicate sessions if one is closed */
1120 	ASSERT(!(psess1->ps_closed && psess2->ps_closed));
1121 	if (psess1->ps_closed)
1122 		return (-1);
1123 	else if (psess2->ps_closed)
1124 		return (1);
1125 
1126 	return (0);
1127 }
1128 
1129 int
1130 pppt_sess_avl_compare_by_name(const void *void_sess1, const void *void_sess2)
1131 {
1132 	const	pppt_sess_t	*psess1 = void_sess1;
1133 	const	pppt_sess_t	*psess2 = void_sess2;
1134 	int			result;
1135 
1136 	/* Sort by code set then ident */
1137 	if (psess1->ps_stmf_sess->ss_rport_id->code_set <
1138 	    psess2->ps_stmf_sess->ss_rport_id->code_set) {
1139 		return (-1);
1140 	} else if (psess1->ps_stmf_sess->ss_rport_id->code_set >
1141 	    psess2->ps_stmf_sess->ss_rport_id->code_set) {
1142 		return (1);
1143 	}
1144 
1145 	/* Next by ident length */
1146 	if (psess1->ps_stmf_sess->ss_rport_id->ident_length <
1147 	    psess2->ps_stmf_sess->ss_rport_id->ident_length) {
1148 		return (-1);
1149 	} else if (psess1->ps_stmf_sess->ss_rport_id->ident_length >
1150 	    psess2->ps_stmf_sess->ss_rport_id->ident_length) {
1151 		return (1);
1152 	}
1153 
1154 	/* Code set and ident length both match, now compare idents */
1155 	result = memcmp(psess1->ps_stmf_sess->ss_rport_id->ident,
1156 	    psess2->ps_stmf_sess->ss_rport_id->ident,
1157 	    psess1->ps_stmf_sess->ss_rport_id->ident_length);
1158 
1159 	if (result < 0) {
1160 		return (-1);
1161 	} else if (result > 0) {
1162 		return (1);
1163 	}
1164 
1165 	return (0);
1166 }
1167 
1168 void
1169 pppt_sess_close_locked(pppt_sess_t *ps)
1170 {
1171 	pppt_tgt_t	*tgt = ps->ps_target;
1172 	pppt_task_t	*ptask;
1173 
1174 	stmf_trace("pppt", "Session close %p", (void *)ps);
1175 
1176 	ASSERT(mutex_owned(&pppt_global.global_lock));
1177 	ASSERT(mutex_owned(&tgt->target_mutex));
1178 	ASSERT(mutex_owned(&ps->ps_mutex));
1179 	ASSERT(!ps->ps_closed); /* Caller should ensure session is not closed */
1180 
1181 	ps->ps_closed = B_TRUE;
1182 	for (ptask = avl_first(&ps->ps_task_list); ptask != NULL;
1183 	    ptask = AVL_NEXT(&ps->ps_task_list, ptask)) {
1184 		mutex_enter(&ptask->pt_mutex);
1185 		if (ptask->pt_state == PTS_ACTIVE) {
1186 			stmf_abort(STMF_QUEUE_TASK_ABORT, ptask->pt_stmf_task,
1187 			    STMF_ABORTED, NULL);
1188 		}
1189 		mutex_exit(&ptask->pt_mutex);
1190 	}
1191 
1192 	/*
1193 	 * Now that all the tasks are aborting the session refcnt should
1194 	 * go to 0.
1195 	 */
1196 	while (ps->ps_refcnt != 0) {
1197 		cv_wait(&ps->ps_cv, &ps->ps_mutex);
1198 	}
1199 
1200 	avl_remove(&tgt->target_sess_list, ps);
1201 	avl_remove(&pppt_global.global_sess_list, ps);
1202 	(void) taskq_dispatch(pppt_global.global_sess_taskq,
1203 	    &pppt_sess_destroy_task, ps, KM_SLEEP);
1204 
1205 	stmf_trace("pppt", "Session close complete %p", (void *)ps);
1206 }
1207 
1208 pppt_task_t *
1209 pppt_task_alloc(void)
1210 {
1211 	pppt_task_t	*ptask;
1212 	pppt_buf_t	*immed_pbuf;
1213 
1214 	ptask = kmem_alloc(sizeof (pppt_task_t) + sizeof (pppt_buf_t) +
1215 	    sizeof (stmf_data_buf_t), KM_NOSLEEP);
1216 	if (ptask != NULL) {
1217 		ptask->pt_state = PTS_INIT;
1218 		ptask->pt_read_buf = NULL;
1219 		ptask->pt_read_xfer_msgid = 0;
1220 		cv_init(&ptask->pt_cv, NULL, CV_DRIVER, NULL);
1221 		mutex_init(&ptask->pt_mutex, NULL, MUTEX_DRIVER, NULL);
1222 		immed_pbuf = (pppt_buf_t *)(ptask + 1);
1223 		bzero(immed_pbuf, sizeof (*immed_pbuf));
1224 		immed_pbuf->pbuf_is_immed = B_TRUE;
1225 		immed_pbuf->pbuf_stmf_buf = (stmf_data_buf_t *)(immed_pbuf + 1);
1226 
1227 		bzero(immed_pbuf->pbuf_stmf_buf, sizeof (stmf_data_buf_t));
1228 		immed_pbuf->pbuf_stmf_buf->db_port_private = immed_pbuf;
1229 		immed_pbuf->pbuf_stmf_buf->db_sglist_length = 1;
1230 		immed_pbuf->pbuf_stmf_buf->db_flags = DB_DIRECTION_FROM_RPORT |
1231 		    DB_DONT_CACHE;
1232 		ptask->pt_immed_data = immed_pbuf;
1233 	}
1234 
1235 	return (ptask);
1236 
1237 }
1238 
1239 void
1240 pppt_task_free(pppt_task_t *ptask)
1241 {
1242 	mutex_enter(&ptask->pt_mutex);
1243 	mutex_destroy(&ptask->pt_mutex);
1244 	cv_destroy(&ptask->pt_cv);
1245 	kmem_free(ptask, sizeof (pppt_task_t) + sizeof (pppt_buf_t) +
1246 	    sizeof (stmf_data_buf_t));
1247 }
1248 
1249 pppt_status_t
1250 pppt_task_start(pppt_task_t *ptask)
1251 {
1252 	avl_index_t		where;
1253 
1254 	ASSERT(ptask->pt_state == PTS_INIT);
1255 
1256 	mutex_enter(&ptask->pt_sess->ps_mutex);
1257 	mutex_enter(&ptask->pt_mutex);
1258 	if (avl_find(&ptask->pt_sess->ps_task_list, ptask, &where) == NULL) {
1259 		pppt_task_update_state(ptask, PTS_ACTIVE);
1260 		avl_insert(&ptask->pt_sess->ps_task_list, ptask, where);
1261 		mutex_exit(&ptask->pt_mutex);
1262 		mutex_exit(&ptask->pt_sess->ps_mutex);
1263 		return (PPPT_STATUS_SUCCESS);
1264 	}
1265 	mutex_exit(&ptask->pt_mutex);
1266 	mutex_exit(&ptask->pt_sess->ps_mutex);
1267 
1268 	return (PPPT_STATUS_FAIL);
1269 }
1270 
1271 pppt_status_t
1272 pppt_task_done(pppt_task_t *ptask)
1273 {
1274 	pppt_status_t	pppt_status = PPPT_STATUS_SUCCESS;
1275 	boolean_t	remove = B_FALSE;
1276 
1277 	mutex_enter(&ptask->pt_mutex);
1278 
1279 	switch (ptask->pt_state) {
1280 	case PTS_ACTIVE:
1281 		remove = B_TRUE;
1282 		pppt_task_update_state(ptask, PTS_DONE);
1283 		break;
1284 	case PTS_ABORTED:
1285 		pppt_status = PPPT_STATUS_ABORTED;
1286 		break;
1287 	case PTS_DONE:
1288 		/* Repeat calls are OK.  Do nothing, return success */
1289 		break;
1290 	default:
1291 		ASSERT(0);
1292 	}
1293 
1294 	mutex_exit(&ptask->pt_mutex);
1295 
1296 	if (remove) {
1297 		mutex_enter(&ptask->pt_sess->ps_mutex);
1298 		avl_remove(&ptask->pt_sess->ps_task_list, ptask);
1299 		mutex_exit(&ptask->pt_sess->ps_mutex);
1300 	}
1301 
1302 	return (pppt_status);
1303 }
1304 
1305 void
1306 pppt_task_sent_status(pppt_task_t *ptask)
1307 {
1308 	/*
1309 	 * If STMF tries to abort a task after the task state changed to
1310 	 * PTS_DONE (meaning all task processing is complete from
1311 	 * the port provider perspective) then we return STMF_BUSY
1312 	 * from pppt_lport_abort.  STMF will return after a short interval
1313 	 * but our calls to stmf_send_status_done will be ignored since
1314 	 * STMF is aborting the task.  That's where this state comes in.
1315 	 * This state essentially says we are calling stmf_send_status_done
1316 	 * so we will not be touching the task again.  The next time
1317 	 * STMF calls pppt_lport_abort we will return a success full
1318 	 * status and the abort will succeed.
1319 	 */
1320 	mutex_enter(&ptask->pt_mutex);
1321 	pppt_task_update_state(ptask, PTS_SENT_STATUS);
1322 	mutex_exit(&ptask->pt_mutex);
1323 }
1324 
1325 pppt_task_t *
1326 pppt_task_lookup(stmf_ic_msgid_t msgid)
1327 {
1328 	pppt_tgt_t	*tgt;
1329 	pppt_sess_t	*sess;
1330 	pppt_task_t	lookup_task;
1331 	pppt_task_t	*result;
1332 
1333 	bzero(&lookup_task, sizeof (lookup_task));
1334 	lookup_task.pt_task_id = msgid;
1335 	PPPT_GLOBAL_LOCK();
1336 	for (tgt = avl_first(&pppt_global.global_target_list); tgt != NULL;
1337 	    tgt = AVL_NEXT(&pppt_global.global_target_list, tgt)) {
1338 
1339 		mutex_enter(&tgt->target_mutex);
1340 		for (sess = avl_first(&tgt->target_sess_list); sess != NULL;
1341 		    sess = AVL_NEXT(&tgt->target_sess_list, sess)) {
1342 			mutex_enter(&sess->ps_mutex);
1343 			if ((result = avl_find(&sess->ps_task_list,
1344 			    &lookup_task, NULL)) != NULL) {
1345 				if (pppt_task_hold(result) !=
1346 				    PPPT_STATUS_SUCCESS) {
1347 					result = NULL;
1348 				}
1349 				mutex_exit(&sess->ps_mutex);
1350 				mutex_exit(&tgt->target_mutex);
1351 				PPPT_GLOBAL_UNLOCK();
1352 				return (result);
1353 			}
1354 			mutex_exit(&sess->ps_mutex);
1355 		}
1356 		mutex_exit(&tgt->target_mutex);
1357 	}
1358 	PPPT_GLOBAL_UNLOCK();
1359 
1360 	return (NULL);
1361 }
1362 
1363 static int
1364 pppt_task_avl_compare(const void *void_task1, const void *void_task2)
1365 {
1366 	const pppt_task_t	*ptask1 = void_task1;
1367 	const pppt_task_t	*ptask2 = void_task2;
1368 
1369 	if (ptask1->pt_task_id < ptask2->pt_task_id)
1370 		return (-1);
1371 	else if (ptask1->pt_task_id > ptask2->pt_task_id)
1372 		return (1);
1373 
1374 	return (0);
1375 }
1376 
1377 static pppt_status_t
1378 pppt_task_try_abort(pppt_task_t *ptask)
1379 {
1380 	boolean_t	remove = B_FALSE;
1381 	pppt_status_t	pppt_status = PPPT_STATUS_SUCCESS;
1382 
1383 	mutex_enter(&ptask->pt_mutex);
1384 
1385 	switch (ptask->pt_state) {
1386 	case PTS_ACTIVE:
1387 		remove = B_TRUE;
1388 		pppt_task_update_state(ptask, PTS_ABORTED);
1389 		break;
1390 	case PTS_DONE:
1391 		pppt_status = PPPT_STATUS_DONE;
1392 		break;
1393 	case PTS_SENT_STATUS:
1394 		/*
1395 		 * Already removed so leave remove set to B_FALSE
1396 		 * and leave status set to PPPT_STATUS_SUCCESS.
1397 		 */
1398 		pppt_task_update_state(ptask, PTS_ABORTED);
1399 		break;
1400 	case PTS_ABORTED:
1401 		break;
1402 	default:
1403 		ASSERT(0);
1404 	}
1405 
1406 	mutex_exit(&ptask->pt_mutex);
1407 
1408 	if (remove) {
1409 		mutex_enter(&ptask->pt_sess->ps_mutex);
1410 		avl_remove(&ptask->pt_sess->ps_task_list, ptask);
1411 		mutex_exit(&ptask->pt_sess->ps_mutex);
1412 	}
1413 
1414 	return (pppt_status);
1415 }
1416 
1417 static pppt_status_t
1418 pppt_task_hold(pppt_task_t *ptask)
1419 {
1420 	pppt_status_t	pppt_status = PPPT_STATUS_SUCCESS;
1421 
1422 	mutex_enter(&ptask->pt_mutex);
1423 	if (ptask->pt_state == PTS_ACTIVE) {
1424 		ptask->pt_refcnt++;
1425 	} else {
1426 		pppt_status = PPPT_STATUS_FAIL;
1427 	}
1428 	mutex_exit(&ptask->pt_mutex);
1429 
1430 	return (pppt_status);
1431 }
1432 
1433 static void
1434 pppt_task_rele(pppt_task_t *ptask)
1435 {
1436 	mutex_enter(&ptask->pt_mutex);
1437 	ptask->pt_refcnt--;
1438 	cv_signal(&ptask->pt_cv);
1439 	mutex_exit(&ptask->pt_mutex);
1440 }
1441 
1442 static void
1443 pppt_task_update_state(pppt_task_t *ptask,
1444     pppt_task_state_t new_state)
1445 {
1446 	PPPT_LOG(CE_NOTE, "task %p %d -> %d", (void *)ptask,
1447 	    ptask->pt_state, new_state);
1448 
1449 	ASSERT(mutex_owned(&ptask->pt_mutex));
1450 	ptask->pt_state = new_state;
1451 	cv_signal(&ptask->pt_cv);
1452 }
1453