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