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