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