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