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