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
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
163 _info(struct modinfo *modinfop)
164 {
165 return (mod_info(&modlinkage, modinfop));
166 }
167
168 int
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
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
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
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
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
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
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
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
371 rc = ddi_copyin((void *)(unsigned long)iocd.pppt_buf,
372 buf, buf_size, flag);
373 if (rc) {
374 kmem_free(buf, buf_size);
375 return (EFAULT);
376 }
377
378 stmf_ic_rx_msg(buf, buf_size);
379
380 kmem_free(buf, buf_size);
381 break;
382 case PPPT_INSTALL_DOOR:
383
384 new_handle = door_ki_lookup((int)iocd.pppt_door_fd);
385 if (new_handle == NULL)
386 return (EINVAL);
387
388 mutex_enter(&pppt_global.global_door_lock);
389 ASSERT(pppt_global.global_svc_state == PSS_ENABLED);
390 if (pppt_global.global_door != NULL) {
391 /*
392 * There can only be one door installed
393 */
394 mutex_exit(&pppt_global.global_door_lock);
395 door_ki_rele(new_handle);
396 return (EBUSY);
397 }
398 pppt_global.global_door = new_handle;
399 mutex_exit(&pppt_global.global_door_lock);
400 break;
401 }
402
403 return (rc);
404 }
405
406 /*
407 * pppt_enable_svc
408 *
409 * registers all the configured targets and target portals with STMF
410 */
411 static int
412 pppt_enable_svc(void)
413 {
414 stmf_port_provider_t *pp;
415 stmf_dbuf_store_t *dbuf_store;
416 int rc = 0;
417
418 ASSERT(pppt_global.global_svc_state == PSS_ENABLING);
419
420 /*
421 * Make sure that can tell if we have partially allocated
422 * in case we need to exit and tear down anything allocated.
423 */
424 pppt_global.global_dbuf_store = NULL;
425 pp = NULL;
426 pppt_global.global_pp = NULL;
427 pppt_global.global_dispatch_taskq = NULL;
428 pppt_global.global_sess_taskq = NULL;
429
430 avl_create(&pppt_global.global_target_list,
431 pppt_tgt_avl_compare, sizeof (pppt_tgt_t),
432 offsetof(pppt_tgt_t, target_global_ln));
433
434 avl_create(&pppt_global.global_sess_list,
435 pppt_sess_avl_compare_by_id, sizeof (pppt_sess_t),
436 offsetof(pppt_sess_t, ps_global_ln));
437
438 /*
439 * Setup STMF dbuf store. Tf buffers are associated with a particular
440 * lport (FC, SRP) then the dbuf_store should stored in the lport
441 * context, otherwise (iSCSI) the dbuf_store should be global.
442 */
443 dbuf_store = stmf_alloc(STMF_STRUCT_DBUF_STORE, 0, 0);
444 if (dbuf_store == NULL) {
445 rc = ENOMEM;
446 goto tear_down_and_return;
447 }
448 dbuf_store->ds_alloc_data_buf = pppt_dbuf_alloc;
449 dbuf_store->ds_free_data_buf = pppt_dbuf_free;
450 dbuf_store->ds_port_private = NULL;
451 pppt_global.global_dbuf_store = dbuf_store;
452
453 /* Register port provider */
454 pp = stmf_alloc(STMF_STRUCT_PORT_PROVIDER, 0, 0);
455 if (pp == NULL) {
456 rc = ENOMEM;
457 goto tear_down_and_return;
458 }
459
460 pp->pp_portif_rev = PORTIF_REV_1;
461 pp->pp_instance = 0;
462 pp->pp_name = PPPT_MODNAME;
463 pp->pp_cb = NULL;
464
465 pppt_global.global_pp = pp;
466
467 if (stmf_register_port_provider(pp) != STMF_SUCCESS) {
468 rc = EIO;
469 goto tear_down_and_return;
470 }
471
472 pppt_global.global_dispatch_taskq = taskq_create("pppt_dispatch",
473 1, minclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE);
474
475 pppt_global.global_sess_taskq = taskq_create("pppt_session",
476 1, minclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE);
477
478 return (0);
479
480 tear_down_and_return:
481
482 if (pppt_global.global_sess_taskq) {
483 taskq_destroy(pppt_global.global_sess_taskq);
484 pppt_global.global_sess_taskq = NULL;
485 }
486
487 if (pppt_global.global_dispatch_taskq) {
488 taskq_destroy(pppt_global.global_dispatch_taskq);
489 pppt_global.global_dispatch_taskq = NULL;
490 }
491
492 if (pppt_global.global_pp)
493 pppt_global.global_pp = NULL;
494
495 if (pp)
496 stmf_free(pp);
497
498 if (pppt_global.global_dbuf_store) {
499 stmf_free(pppt_global.global_dbuf_store);
500 pppt_global.global_dbuf_store = NULL;
501 }
502
503 avl_destroy(&pppt_global.global_sess_list);
504 avl_destroy(&pppt_global.global_target_list);
505
506 return (rc);
507 }
508
509 /*
510 * pppt_disable_svc
511 *
512 * clean up all existing sessions and deregister targets from STMF
513 */
514 static void
515 pppt_disable_svc(void)
516 {
517 pppt_tgt_t *tgt, *next_tgt;
518 avl_tree_t delete_target_list;
519
520 ASSERT(pppt_global.global_svc_state == PSS_DISABLING);
521
522 avl_create(&delete_target_list,
523 pppt_tgt_avl_compare, sizeof (pppt_tgt_t),
524 offsetof(pppt_tgt_t, target_global_ln));
525
526 PPPT_GLOBAL_LOCK();
527 for (tgt = avl_first(&pppt_global.global_target_list);
528 tgt != NULL;
529 tgt = next_tgt) {
530 next_tgt = AVL_NEXT(&pppt_global.global_target_list, tgt);
531 avl_remove(&pppt_global.global_target_list, tgt);
532 avl_add(&delete_target_list, tgt);
533 pppt_tgt_async_delete(tgt);
534 }
535 PPPT_GLOBAL_UNLOCK();
536
537 for (tgt = avl_first(&delete_target_list);
538 tgt != NULL;
539 tgt = next_tgt) {
540 next_tgt = AVL_NEXT(&delete_target_list, tgt);
541 mutex_enter(&tgt->target_mutex);
542 while ((tgt->target_refcount > 0) ||
543 (tgt->target_state != TS_DELETING)) {
544 cv_wait(&tgt->target_cv, &tgt->target_mutex);
545 }
546 mutex_exit(&tgt->target_mutex);
547
548 avl_remove(&delete_target_list, tgt);
549 pppt_tgt_destroy(tgt);
550 }
551
552 taskq_destroy(pppt_global.global_sess_taskq);
553
554 taskq_destroy(pppt_global.global_dispatch_taskq);
555
556 avl_destroy(&pppt_global.global_sess_list);
557 avl_destroy(&pppt_global.global_target_list);
558
559 (void) stmf_deregister_port_provider(pppt_global.global_pp);
560
561 stmf_free(pppt_global.global_dbuf_store);
562 pppt_global.global_dbuf_store = NULL;
563
564 stmf_free(pppt_global.global_pp);
565 pppt_global.global_pp = NULL;
566 }
567
568 /*
569 * STMF callbacks
570 */
571
572 /*ARGSUSED*/
573 static stmf_data_buf_t *
574 pppt_dbuf_alloc(scsi_task_t *task, uint32_t size, uint32_t *pminsize,
575 uint32_t flags)
576 {
577 stmf_data_buf_t *result;
578 pppt_buf_t *pbuf;
579 uint8_t *buf;
580
581 /* Get buffer */
582 buf = kmem_alloc(size, KM_SLEEP);
583
584 /*
585 * Allocate stmf buf with private port provider section
586 * (pppt_buf_t)
587 */
588 result = stmf_alloc(STMF_STRUCT_DATA_BUF, sizeof (pppt_buf_t), 0);
589 if (result != NULL) {
590 /* Fill in pppt_buf_t */
591 pbuf = result->db_port_private;
592 pbuf->pbuf_stmf_buf = result;
593 pbuf->pbuf_is_immed = B_FALSE;
594
595 /*
596 * Fill in stmf_data_buf_t. DB_DONT CACHE tells
597 * stmf not to cache buffers but STMF doesn't do
598 * that yet so it's a no-op. Port providers like
599 * FC and SRP that have buffers associated with the
600 * target port would want to let STMF cache
601 * the buffers. Port providers like iSCSI would
602 * not want STMF to cache because the buffers are
603 * really associated with a connection, not an
604 * STMF target port so there is no way for STMF
605 * to cache the buffers effectively. These port
606 * providers should cache buffers internally if
607 * there is significant buffer setup overhead.
608 *
609 * And of course, since STMF doesn't do any internal
610 * caching right now anyway, all port providers should
611 * do what they can to minimize buffer setup overhead.
612 */
613 result->db_flags = DB_DONT_CACHE;
614 result->db_buf_size = size;
615 result->db_data_size = size;
616 result->db_sglist_length = 1;
617 result->db_sglist[0].seg_addr = buf;
618 result->db_sglist[0].seg_length = size;
619 return (result);
620 } else {
621 /*
622 * Couldn't get the stmf_data_buf_t so free the
623 * buffer
624 */
625 kmem_free(buf, size);
626 }
627
628 return (NULL);
629 }
630
631 /*ARGSUSED*/
632 static void
633 pppt_dbuf_free(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf)
634 {
635 pppt_buf_t *pbuf = dbuf->db_port_private;
636
637 if (pbuf->pbuf_is_immed) {
638 stmf_ic_msg_free(pbuf->pbuf_immed_msg);
639 } else {
640 kmem_free(dbuf->db_sglist[0].seg_addr,
641 dbuf->db_sglist[0].seg_length);
642 stmf_free(dbuf);
643 }
644 }
645
646 /*ARGSUSED*/
647 stmf_status_t
648 pppt_lport_xfer_data(scsi_task_t *task, stmf_data_buf_t *dbuf,
649 uint32_t ioflags)
650 {
651 pppt_task_t *pppt_task = task->task_port_private;
652 pppt_buf_t *pbuf = dbuf->db_port_private;
653 stmf_ic_msg_t *msg;
654 stmf_ic_msg_status_t ic_msg_status;
655
656 /*
657 * If we are aborting then we can ignore this request, otherwise
658 * add a reference.
659 */
660 if (pppt_task_hold(pppt_task) != PPPT_STATUS_SUCCESS) {
661 return (STMF_SUCCESS);
662 }
663
664 /*
665 * If it's not immediate data then start the transfer
666 */
667 ASSERT(pbuf->pbuf_is_immed == B_FALSE);
668 if (dbuf->db_flags & DB_DIRECTION_TO_RPORT) {
669
670 /* Send read data */
671 msg = stmf_ic_scsi_data_msg_alloc(
672 pppt_task->pt_task_id,
673 pppt_task->pt_sess->ps_session_id,
674 pppt_task->pt_lun_id,
675 dbuf->db_sglist[0].seg_length,
676 dbuf->db_sglist[0].seg_addr, 0);
677
678 pppt_task->pt_read_buf = pbuf;
679 pppt_task->pt_read_xfer_msgid = msg->icm_msgid;
680
681 ic_msg_status = stmf_ic_tx_msg(msg);
682 pppt_task_rele(pppt_task);
683 if (ic_msg_status != STMF_IC_MSG_SUCCESS) {
684 return (STMF_FAILURE);
685 } else {
686 return (STMF_SUCCESS);
687 }
688 } else if (dbuf->db_flags & DB_DIRECTION_FROM_RPORT) {
689 pppt_task_rele(pppt_task);
690 return (STMF_FAILURE);
691 }
692
693 pppt_task_rele(pppt_task);
694
695 return (STMF_INVALID_ARG);
696 }
697
698 void
699 pppt_xfer_read_complete(pppt_task_t *pppt_task, stmf_status_t status)
700 {
701 pppt_buf_t *pppt_buf;
702 stmf_data_buf_t *dbuf;
703
704 /*
705 * Caller should have taken a task hold (likely via pppt_task_lookup)
706 *
707 * Get pppt_buf_t and stmf_data_buf_t pointers
708 */
709 pppt_buf = pppt_task->pt_read_buf;
710 dbuf = pppt_buf->pbuf_stmf_buf;
711 dbuf->db_xfer_status = (status == STMF_SUCCESS) ?
712 STMF_SUCCESS : STMF_FAILURE;
713
714 /*
715 * COMSTAR currently requires port providers to support
716 * the DB_SEND_STATUS_GOOD flag even if phase collapse is
717 * not supported. So we will roll our own... pretend we are
718 * COMSTAR and ask for a status message.
719 */
720 if ((dbuf->db_flags & DB_SEND_STATUS_GOOD) &&
721 (status == STMF_SUCCESS)) {
722 /*
723 * It's possible the task has been aborted since the time we
724 * looked it up. We need to release the hold before calling
725 * pppt_lport_send_status and as soon as we release the hold
726 * the task may disappear. Calling pppt_task_done allows us
727 * to determine whether the task has been aborted (in which
728 * case we will stop processing and return) and mark the task
729 * "done" which will prevent the task from being aborted while
730 * we are trying to send the status.
731 */
732 if (pppt_task_done(pppt_task) != PPPT_STATUS_SUCCESS) {
733 /* STMF will free task and buffer(s) */
734 pppt_task_rele(pppt_task);
735 return;
736 }
737 pppt_task_rele(pppt_task);
738
739 if (pppt_lport_send_status(pppt_task->pt_stmf_task, 0)
740 != STMF_SUCCESS) {
741 /* Failed to send status */
742 dbuf->db_xfer_status = STMF_FAILURE;
743 stmf_data_xfer_done(pppt_task->pt_stmf_task, dbuf,
744 STMF_IOF_LPORT_DONE);
745 }
746 } else {
747 pppt_task_rele(pppt_task);
748 stmf_data_xfer_done(pppt_task->pt_stmf_task, dbuf, 0);
749 }
750 }
751
752 /*ARGSUSED*/
753 stmf_status_t
754 pppt_lport_send_status(scsi_task_t *task, uint32_t ioflags)
755 {
756 pppt_task_t *ptask = task->task_port_private;
757 stmf_ic_msg_t *msg;
758 stmf_ic_msg_status_t ic_msg_status;
759
760 /*
761 * Mark task completed. If the state indicates it was aborted
762 * then we don't need to respond.
763 */
764 if (pppt_task_done(ptask) == PPPT_STATUS_ABORTED) {
765 return (STMF_SUCCESS);
766 }
767
768 /*
769 * Send status.
770 */
771 msg = stmf_ic_scsi_status_msg_alloc(
772 ptask->pt_task_id,
773 ptask->pt_sess->ps_session_id,
774 ptask->pt_lun_id,
775 0,
776 task->task_scsi_status,
777 task->task_status_ctrl, task->task_resid,
778 task->task_sense_length, task->task_sense_data, 0);
779
780 ic_msg_status = stmf_ic_tx_msg(msg);
781
782 if (ic_msg_status != STMF_IC_MSG_SUCCESS) {
783 pppt_task_sent_status(ptask);
784 stmf_send_status_done(ptask->pt_stmf_task,
785 STMF_FAILURE, STMF_IOF_LPORT_DONE);
786 return (STMF_FAILURE);
787 } else {
788 pppt_task_sent_status(ptask);
789 stmf_send_status_done(ptask->pt_stmf_task,
790 STMF_SUCCESS, STMF_IOF_LPORT_DONE);
791 return (STMF_SUCCESS);
792 }
793 }
794
795 void
796 pppt_lport_task_free(scsi_task_t *task)
797 {
798 pppt_task_t *ptask = task->task_port_private;
799 pppt_sess_t *ps = ptask->pt_sess;
800
801 pppt_task_rele(ptask);
802 pppt_sess_rele(ps);
803 }
804
805 /*ARGSUSED*/
806 stmf_status_t
807 pppt_lport_abort(stmf_local_port_t *lport, int abort_cmd, void *arg,
808 uint32_t flags)
809 {
810 scsi_task_t *st = (scsi_task_t *)arg;
811 pppt_task_t *ptask;
812
813 ptask = st->task_port_private;
814
815 if (pppt_task_try_abort(ptask) == PPPT_STATUS_DONE) {
816 /*
817 * This task is beyond the point where abort makes sense
818 * and we will soon be sending status. Tell STMF to
819 * go away.
820 */
821 return (STMF_BUSY);
822 } else {
823 return (STMF_ABORT_SUCCESS);
824 }
825 /*NOTREACHED*/
826 }
827
828 /*ARGSUSED*/
829 void
830 pppt_lport_ctl(stmf_local_port_t *lport, int cmd, void *arg)
831 {
832 switch (cmd) {
833 case STMF_CMD_LPORT_ONLINE:
834 case STMF_CMD_LPORT_OFFLINE:
835 case STMF_ACK_LPORT_ONLINE_COMPLETE:
836 case STMF_ACK_LPORT_OFFLINE_COMPLETE:
837 pppt_tgt_sm_ctl(lport, cmd, arg);
838 break;
839
840 default:
841 ASSERT(0);
842 break;
843 }
844 }
845
846 pppt_sess_t *
847 pppt_sess_lookup_locked(uint64_t session_id,
848 scsi_devid_desc_t *lport_devid, stmf_remote_port_t *rport)
849 {
850 pppt_tgt_t *tgt;
851 pppt_sess_t *ps;
852 int lport_cmp;
853
854 ASSERT(mutex_owned(&pppt_global.global_lock));
855
856 /*
857 * Look for existing session for this ID
858 */
859 ps = pppt_sess_lookup_by_id_locked(session_id);
860 if (ps == NULL) {
861 PPPT_INC_STAT(es_sess_lookup_no_session);
862 return (NULL);
863 }
864
865 tgt = ps->ps_target;
866
867 mutex_enter(&tgt->target_mutex);
868
869 /* Validate local/remote port names */
870 if ((lport_devid->ident_length !=
871 tgt->target_stmf_lport->lport_id->ident_length) ||
872 (rport->rport_tptid_sz !=
873 ps->ps_stmf_sess->ss_rport->rport_tptid_sz)) {
874 mutex_exit(&tgt->target_mutex);
875 PPPT_INC_STAT(es_sess_lookup_ident_mismatch);
876 return (NULL);
877 } else {
878 lport_cmp = bcmp(lport_devid->ident,
879 tgt->target_stmf_lport->lport_id->ident,
880 lport_devid->ident_length);
881 if (lport_cmp != 0 ||
882 (stmf_scsilib_tptid_compare(rport->rport_tptid,
883 ps->ps_stmf_sess->ss_rport->rport_tptid) != B_TRUE)) {
884 mutex_exit(&tgt->target_mutex);
885 PPPT_INC_STAT(es_sess_lookup_ident_mismatch);
886 return (NULL);
887 }
888
889 if (tgt->target_state != TS_STMF_ONLINE) {
890 mutex_exit(&tgt->target_mutex);
891 PPPT_INC_STAT(es_sess_lookup_bad_tgt_state);
892 return (NULL);
893 }
894 }
895 mutex_exit(&tgt->target_mutex);
896
897 return (ps);
898 }
899
900 pppt_sess_t *
901 pppt_sess_lookup_by_id_locked(uint64_t session_id)
902 {
903 pppt_sess_t tmp_ps;
904 pppt_sess_t *ps;
905
906 ASSERT(mutex_owned(&pppt_global.global_lock));
907 tmp_ps.ps_session_id = session_id;
908 tmp_ps.ps_closed = 0;
909 ps = avl_find(&pppt_global.global_sess_list, &tmp_ps, NULL);
910 if (ps != NULL) {
911 mutex_enter(&ps->ps_mutex);
912 if (!ps->ps_closed) {
913 ps->ps_refcnt++;
914 mutex_exit(&ps->ps_mutex);
915 return (ps);
916 }
917 mutex_exit(&ps->ps_mutex);
918 }
919
920 return (NULL);
921 }
922
923 /* New session */
924 pppt_sess_t *
925 pppt_sess_lookup_create(scsi_devid_desc_t *lport_devid,
926 scsi_devid_desc_t *rport_devid, stmf_remote_port_t *rport,
927 uint64_t session_id, stmf_status_t *statusp)
928 {
929 pppt_tgt_t *tgt;
930 pppt_sess_t *ps;
931 stmf_scsi_session_t *ss;
932 pppt_sess_t tmp_ps;
933 stmf_scsi_session_t tmp_ss;
934 *statusp = STMF_SUCCESS;
935
936 PPPT_GLOBAL_LOCK();
937
938 /*
939 * Look for existing session for this ID
940 */
941 ps = pppt_sess_lookup_locked(session_id, lport_devid, rport);
942
943 if (ps != NULL) {
944 PPPT_GLOBAL_UNLOCK();
945 return (ps);
946 }
947
948 /*
949 * No session with that ID, look for another session corresponding
950 * to the same IT nexus.
951 */
952 tgt = pppt_tgt_lookup_locked(lport_devid);
953 if (tgt == NULL) {
954 *statusp = STMF_NOT_FOUND;
955 PPPT_GLOBAL_UNLOCK();
956 return (NULL);
957 }
958
959 mutex_enter(&tgt->target_mutex);
960 if (tgt->target_state != TS_STMF_ONLINE) {
961 *statusp = STMF_NOT_FOUND;
962 mutex_exit(&tgt->target_mutex);
963 PPPT_GLOBAL_UNLOCK();
964 /* Can't create session to offline target */
965 return (NULL);
966 }
967
968 bzero(&tmp_ps, sizeof (tmp_ps));
969 bzero(&tmp_ss, sizeof (tmp_ss));
970 tmp_ps.ps_stmf_sess = &tmp_ss;
971 tmp_ss.ss_rport = rport;
972
973 /*
974 * Look for an existing session on this IT nexus
975 */
976 ps = avl_find(&tgt->target_sess_list, &tmp_ps, NULL);
977
978 if (ps != NULL) {
979 /*
980 * Now check the session ID. It should not match because if
981 * it did we would have found it on the global session list.
982 * If the session ID in the command is higher than the existing
983 * session ID then we need to tear down the existing session.
984 */
985 mutex_enter(&ps->ps_mutex);
986 ASSERT(ps->ps_session_id != session_id);
987 if (ps->ps_session_id > session_id) {
988 /* Invalid session ID */
989 mutex_exit(&ps->ps_mutex);
990 mutex_exit(&tgt->target_mutex);
991 PPPT_GLOBAL_UNLOCK();
992 *statusp = STMF_INVALID_ARG;
993 return (NULL);
994 } else {
995 /* Existing session needs to be invalidated */
996 if (!ps->ps_closed) {
997 pppt_sess_close_locked(ps);
998 }
999 }
1000 mutex_exit(&ps->ps_mutex);
1001
1002 /* Fallthrough and create new session */
1003 }
1004
1005 /*
1006 * Allocate and fill in pppt_session_t with the appropriate data
1007 * for the protocol.
1008 */
1009 ps = kmem_zalloc(sizeof (*ps), KM_SLEEP);
1010
1011 /* Fill in session fields */
1012 ps->ps_target = tgt;
1013 ps->ps_session_id = session_id;
1014
1015 ss = stmf_alloc(STMF_STRUCT_SCSI_SESSION, 0,
1016 0);
1017 if (ss == NULL) {
1018 mutex_exit(&tgt->target_mutex);
1019 PPPT_GLOBAL_UNLOCK();
1020 kmem_free(ps, sizeof (*ps));
1021 *statusp = STMF_ALLOC_FAILURE;
1022 return (NULL);
1023 }
1024
1025 ss->ss_rport_id = kmem_zalloc(sizeof (scsi_devid_desc_t) +
1026 rport_devid->ident_length + 1, KM_SLEEP);
1027 bcopy(rport_devid, ss->ss_rport_id,
1028 sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1);
1029
1030 ss->ss_lport = tgt->target_stmf_lport;
1031
1032 ss->ss_rport = stmf_remote_port_alloc(rport->rport_tptid_sz);
1033 bcopy(rport->rport_tptid, ss->ss_rport->rport_tptid,
1034 rport->rport_tptid_sz);
1035
1036 if (stmf_register_scsi_session(tgt->target_stmf_lport, ss) !=
1037 STMF_SUCCESS) {
1038 mutex_exit(&tgt->target_mutex);
1039 PPPT_GLOBAL_UNLOCK();
1040 kmem_free(ss->ss_rport_id,
1041 sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1);
1042 stmf_remote_port_free(ss->ss_rport);
1043 stmf_free(ss);
1044 kmem_free(ps, sizeof (*ps));
1045 *statusp = STMF_TARGET_FAILURE;
1046 return (NULL);
1047 }
1048
1049 ss->ss_port_private = ps;
1050 mutex_init(&ps->ps_mutex, NULL, MUTEX_DEFAULT, NULL);
1051 cv_init(&ps->ps_cv, NULL, CV_DEFAULT, NULL);
1052 avl_create(&ps->ps_task_list, pppt_task_avl_compare,
1053 sizeof (pppt_task_t), offsetof(pppt_task_t, pt_sess_ln));
1054 ps->ps_refcnt = 1;
1055 ps->ps_stmf_sess = ss;
1056 avl_add(&tgt->target_sess_list, ps);
1057 avl_add(&pppt_global.global_sess_list, ps);
1058 mutex_exit(&tgt->target_mutex);
1059 PPPT_GLOBAL_UNLOCK();
1060 stmf_trace("pppt", "New session %p", (void *)ps);
1061
1062 return (ps);
1063 }
1064
1065 void
1066 pppt_sess_rele(pppt_sess_t *ps)
1067 {
1068 mutex_enter(&ps->ps_mutex);
1069 pppt_sess_rele_locked(ps);
1070 mutex_exit(&ps->ps_mutex);
1071 }
1072
1073 void
1074 pppt_sess_rele_locked(pppt_sess_t *ps)
1075 {
1076 ASSERT(mutex_owned(&ps->ps_mutex));
1077 ps->ps_refcnt--;
1078 if (ps->ps_refcnt == 0) {
1079 cv_signal(&ps->ps_cv);
1080 }
1081 }
1082
1083 static void pppt_sess_destroy_task(void *ps_void)
1084 {
1085 pppt_sess_t *ps = ps_void;
1086 stmf_scsi_session_t *ss;
1087
1088 stmf_trace("pppt", "Session destroy task %p", (void *)ps);
1089
1090 ss = ps->ps_stmf_sess;
1091 mutex_enter(&ps->ps_mutex);
1092 stmf_deregister_scsi_session(ss->ss_lport, ss);
1093 kmem_free(ss->ss_rport_id,
1094 sizeof (scsi_devid_desc_t) + ss->ss_rport_id->ident_length + 1);
1095 stmf_remote_port_free(ss->ss_rport);
1096 avl_destroy(&ps->ps_task_list);
1097 mutex_exit(&ps->ps_mutex);
1098 cv_destroy(&ps->ps_cv);
1099 mutex_destroy(&ps->ps_mutex);
1100 stmf_free(ps->ps_stmf_sess);
1101 kmem_free(ps, sizeof (*ps));
1102
1103 stmf_trace("pppt", "Session destroy task complete %p", (void *)ps);
1104 }
1105
1106 int
1107 pppt_sess_avl_compare_by_id(const void *void_sess1, const void *void_sess2)
1108 {
1109 const pppt_sess_t *psess1 = void_sess1;
1110 const pppt_sess_t *psess2 = void_sess2;
1111
1112 if (psess1->ps_session_id < psess2->ps_session_id)
1113 return (-1);
1114 else if (psess1->ps_session_id > psess2->ps_session_id)
1115 return (1);
1116
1117 /* Allow multiple duplicate sessions if one is closed */
1118 ASSERT(!(psess1->ps_closed && psess2->ps_closed));
1119 if (psess1->ps_closed)
1120 return (-1);
1121 else if (psess2->ps_closed)
1122 return (1);
1123
1124 return (0);
1125 }
1126
1127 int
1128 pppt_sess_avl_compare_by_name(const void *void_sess1, const void *void_sess2)
1129 {
1130 const pppt_sess_t *psess1 = void_sess1;
1131 const pppt_sess_t *psess2 = void_sess2;
1132 int result;
1133
1134 /* Compare by tptid size */
1135 if (psess1->ps_stmf_sess->ss_rport->rport_tptid_sz <
1136 psess2->ps_stmf_sess->ss_rport->rport_tptid_sz) {
1137 return (-1);
1138 } else if (psess1->ps_stmf_sess->ss_rport->rport_tptid_sz >
1139 psess2->ps_stmf_sess->ss_rport->rport_tptid_sz) {
1140 return (1);
1141 }
1142
1143 /* Now compare tptid */
1144 result = memcmp(psess1->ps_stmf_sess->ss_rport->rport_tptid,
1145 psess2->ps_stmf_sess->ss_rport->rport_tptid,
1146 psess1->ps_stmf_sess->ss_rport->rport_tptid_sz);
1147
1148 if (result < 0) {
1149 return (-1);
1150 } else if (result > 0) {
1151 return (1);
1152 }
1153
1154 return (0);
1155 }
1156
1157 void
1158 pppt_sess_close_locked(pppt_sess_t *ps)
1159 {
1160 pppt_tgt_t *tgt = ps->ps_target;
1161 pppt_task_t *ptask;
1162
1163 stmf_trace("pppt", "Session close %p", (void *)ps);
1164
1165 ASSERT(mutex_owned(&pppt_global.global_lock));
1166 ASSERT(mutex_owned(&tgt->target_mutex));
1167 ASSERT(mutex_owned(&ps->ps_mutex));
1168 ASSERT(!ps->ps_closed); /* Caller should ensure session is not closed */
1169
1170 ps->ps_closed = B_TRUE;
1171 for (ptask = avl_first(&ps->ps_task_list); ptask != NULL;
1172 ptask = AVL_NEXT(&ps->ps_task_list, ptask)) {
1173 mutex_enter(&ptask->pt_mutex);
1174 if (ptask->pt_state == PTS_ACTIVE) {
1175 stmf_abort(STMF_QUEUE_TASK_ABORT, ptask->pt_stmf_task,
1176 STMF_ABORTED, NULL);
1177 }
1178 mutex_exit(&ptask->pt_mutex);
1179 }
1180
1181 /*
1182 * Now that all the tasks are aborting the session refcnt should
1183 * go to 0.
1184 */
1185 while (ps->ps_refcnt != 0) {
1186 cv_wait(&ps->ps_cv, &ps->ps_mutex);
1187 }
1188
1189 avl_remove(&tgt->target_sess_list, ps);
1190 avl_remove(&pppt_global.global_sess_list, ps);
1191 (void) taskq_dispatch(pppt_global.global_sess_taskq,
1192 &pppt_sess_destroy_task, ps, KM_SLEEP);
1193
1194 stmf_trace("pppt", "Session close complete %p", (void *)ps);
1195 }
1196
1197 pppt_task_t *
1198 pppt_task_alloc(void)
1199 {
1200 pppt_task_t *ptask;
1201 pppt_buf_t *immed_pbuf;
1202
1203 ptask = kmem_alloc(sizeof (pppt_task_t) + sizeof (pppt_buf_t) +
1204 sizeof (stmf_data_buf_t), KM_NOSLEEP);
1205 if (ptask != NULL) {
1206 ptask->pt_state = PTS_INIT;
1207 ptask->pt_read_buf = NULL;
1208 ptask->pt_read_xfer_msgid = 0;
1209 ptask->pt_refcnt = 0;
1210 mutex_init(&ptask->pt_mutex, NULL, MUTEX_DRIVER, NULL);
1211 immed_pbuf = (pppt_buf_t *)(ptask + 1);
1212 bzero(immed_pbuf, sizeof (*immed_pbuf));
1213 immed_pbuf->pbuf_is_immed = B_TRUE;
1214 immed_pbuf->pbuf_stmf_buf = (stmf_data_buf_t *)(immed_pbuf + 1);
1215
1216 bzero(immed_pbuf->pbuf_stmf_buf, sizeof (stmf_data_buf_t));
1217 immed_pbuf->pbuf_stmf_buf->db_port_private = immed_pbuf;
1218 immed_pbuf->pbuf_stmf_buf->db_sglist_length = 1;
1219 immed_pbuf->pbuf_stmf_buf->db_flags = DB_DIRECTION_FROM_RPORT |
1220 DB_DONT_CACHE;
1221 ptask->pt_immed_data = immed_pbuf;
1222 }
1223
1224 return (ptask);
1225
1226 }
1227
1228 void
1229 pppt_task_free(pppt_task_t *ptask)
1230 {
1231 mutex_enter(&ptask->pt_mutex);
1232 ASSERT(ptask->pt_refcnt == 0);
1233 mutex_destroy(&ptask->pt_mutex);
1234 kmem_free(ptask, sizeof (pppt_task_t) + sizeof (pppt_buf_t) +
1235 sizeof (stmf_data_buf_t));
1236 }
1237
1238 pppt_status_t
1239 pppt_task_start(pppt_task_t *ptask)
1240 {
1241 avl_index_t where;
1242
1243 ASSERT(ptask->pt_state == PTS_INIT);
1244
1245 mutex_enter(&ptask->pt_sess->ps_mutex);
1246 mutex_enter(&ptask->pt_mutex);
1247 if (avl_find(&ptask->pt_sess->ps_task_list, ptask, &where) == NULL) {
1248 pppt_task_update_state(ptask, PTS_ACTIVE);
1249 /* Manually increment refcnt, sincd we hold the mutex... */
1250 ptask->pt_refcnt++;
1251 avl_insert(&ptask->pt_sess->ps_task_list, ptask, where);
1252 mutex_exit(&ptask->pt_mutex);
1253 mutex_exit(&ptask->pt_sess->ps_mutex);
1254 return (PPPT_STATUS_SUCCESS);
1255 }
1256 mutex_exit(&ptask->pt_mutex);
1257 mutex_exit(&ptask->pt_sess->ps_mutex);
1258
1259 return (PPPT_STATUS_FAIL);
1260 }
1261
1262 pppt_status_t
1263 pppt_task_done(pppt_task_t *ptask)
1264 {
1265 pppt_status_t pppt_status = PPPT_STATUS_SUCCESS;
1266 boolean_t remove = B_FALSE;
1267
1268 mutex_enter(&ptask->pt_mutex);
1269
1270 switch (ptask->pt_state) {
1271 case PTS_ACTIVE:
1272 remove = B_TRUE;
1273 pppt_task_update_state(ptask, PTS_DONE);
1274 break;
1275 case PTS_ABORTED:
1276 pppt_status = PPPT_STATUS_ABORTED;
1277 break;
1278 case PTS_DONE:
1279 /* Repeat calls are OK. Do nothing, return success */
1280 break;
1281 default:
1282 ASSERT(0);
1283 }
1284
1285 mutex_exit(&ptask->pt_mutex);
1286
1287 if (remove) {
1288 mutex_enter(&ptask->pt_sess->ps_mutex);
1289 avl_remove(&ptask->pt_sess->ps_task_list, ptask);
1290 mutex_exit(&ptask->pt_sess->ps_mutex);
1291 /* Out of the AVL tree, so drop a reference. */
1292 pppt_task_rele(ptask);
1293 }
1294
1295 return (pppt_status);
1296 }
1297
1298 void
1299 pppt_task_sent_status(pppt_task_t *ptask)
1300 {
1301 /*
1302 * If STMF tries to abort a task after the task state changed to
1303 * PTS_DONE (meaning all task processing is complete from
1304 * the port provider perspective) then we return STMF_BUSY
1305 * from pppt_lport_abort. STMF will return after a short interval
1306 * but our calls to stmf_send_status_done will be ignored since
1307 * STMF is aborting the task. That's where this state comes in.
1308 * This state essentially says we are calling stmf_send_status_done
1309 * so we will not be touching the task again. The next time
1310 * STMF calls pppt_lport_abort we will return a success full
1311 * status and the abort will succeed.
1312 */
1313 mutex_enter(&ptask->pt_mutex);
1314 pppt_task_update_state(ptask, PTS_SENT_STATUS);
1315 mutex_exit(&ptask->pt_mutex);
1316 }
1317
1318 pppt_task_t *
1319 pppt_task_lookup(stmf_ic_msgid_t msgid)
1320 {
1321 pppt_tgt_t *tgt;
1322 pppt_sess_t *sess;
1323 pppt_task_t lookup_task;
1324 pppt_task_t *result;
1325
1326 bzero(&lookup_task, sizeof (lookup_task));
1327 lookup_task.pt_task_id = msgid;
1328 PPPT_GLOBAL_LOCK();
1329 for (tgt = avl_first(&pppt_global.global_target_list); tgt != NULL;
1330 tgt = AVL_NEXT(&pppt_global.global_target_list, tgt)) {
1331
1332 mutex_enter(&tgt->target_mutex);
1333 for (sess = avl_first(&tgt->target_sess_list); sess != NULL;
1334 sess = AVL_NEXT(&tgt->target_sess_list, sess)) {
1335 mutex_enter(&sess->ps_mutex);
1336 if ((result = avl_find(&sess->ps_task_list,
1337 &lookup_task, NULL)) != NULL) {
1338 if (pppt_task_hold(result) !=
1339 PPPT_STATUS_SUCCESS) {
1340 result = NULL;
1341 }
1342 mutex_exit(&sess->ps_mutex);
1343 mutex_exit(&tgt->target_mutex);
1344 PPPT_GLOBAL_UNLOCK();
1345 return (result);
1346 }
1347 mutex_exit(&sess->ps_mutex);
1348 }
1349 mutex_exit(&tgt->target_mutex);
1350 }
1351 PPPT_GLOBAL_UNLOCK();
1352
1353 return (NULL);
1354 }
1355
1356 static int
1357 pppt_task_avl_compare(const void *void_task1, const void *void_task2)
1358 {
1359 const pppt_task_t *ptask1 = void_task1;
1360 const pppt_task_t *ptask2 = void_task2;
1361
1362 if (ptask1->pt_task_id < ptask2->pt_task_id)
1363 return (-1);
1364 else if (ptask1->pt_task_id > ptask2->pt_task_id)
1365 return (1);
1366
1367 return (0);
1368 }
1369
1370 static pppt_status_t
1371 pppt_task_try_abort(pppt_task_t *ptask)
1372 {
1373 boolean_t remove = B_FALSE;
1374 pppt_status_t pppt_status = PPPT_STATUS_SUCCESS;
1375
1376 mutex_enter(&ptask->pt_mutex);
1377
1378 switch (ptask->pt_state) {
1379 case PTS_ACTIVE:
1380 remove = B_TRUE;
1381 pppt_task_update_state(ptask, PTS_ABORTED);
1382 break;
1383 case PTS_DONE:
1384 pppt_status = PPPT_STATUS_DONE;
1385 break;
1386 case PTS_SENT_STATUS:
1387 /*
1388 * Already removed so leave remove set to B_FALSE
1389 * and leave status set to PPPT_STATUS_SUCCESS.
1390 */
1391 pppt_task_update_state(ptask, PTS_ABORTED);
1392 break;
1393 case PTS_ABORTED:
1394 break;
1395 default:
1396 ASSERT(0);
1397 }
1398
1399 mutex_exit(&ptask->pt_mutex);
1400
1401 if (remove) {
1402 mutex_enter(&ptask->pt_sess->ps_mutex);
1403 avl_remove(&ptask->pt_sess->ps_task_list, ptask);
1404 mutex_exit(&ptask->pt_sess->ps_mutex);
1405 /* Out of the AVL tree, so drop a reference. */
1406 pppt_task_rele(ptask);
1407 }
1408
1409 return (pppt_status);
1410 }
1411
1412 pppt_status_t
1413 pppt_task_hold(pppt_task_t *ptask)
1414 {
1415 pppt_status_t pppt_status = PPPT_STATUS_SUCCESS;
1416
1417 mutex_enter(&ptask->pt_mutex);
1418 if (ptask->pt_state == PTS_ACTIVE) {
1419 ptask->pt_refcnt++;
1420 } else {
1421 pppt_status = PPPT_STATUS_FAIL;
1422 }
1423 mutex_exit(&ptask->pt_mutex);
1424
1425 return (pppt_status);
1426 }
1427
1428 static void
1429 pppt_task_rele(pppt_task_t *ptask)
1430 {
1431 boolean_t freeit;
1432
1433 mutex_enter(&ptask->pt_mutex);
1434 ptask->pt_refcnt--;
1435 freeit = (ptask->pt_refcnt == 0);
1436 mutex_exit(&ptask->pt_mutex);
1437 if (freeit)
1438 pppt_task_free(ptask);
1439 }
1440
1441 static void
1442 pppt_task_update_state(pppt_task_t *ptask,
1443 pppt_task_state_t new_state)
1444 {
1445 PPPT_LOG(CE_NOTE, "task %p %d -> %d", (void *)ptask,
1446 ptask->pt_state, new_state);
1447
1448 ASSERT(mutex_owned(&ptask->pt_mutex));
1449 ptask->pt_state = new_state;
1450 }