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 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * dcopy.c 29 * dcopy misc module 30 */ 31 32 #include <sys/conf.h> 33 #include <sys/kmem.h> 34 #include <sys/ddi.h> 35 #include <sys/sunddi.h> 36 #include <sys/modctl.h> 37 #include <sys/sysmacros.h> 38 #include <sys/atomic.h> 39 40 41 #include <sys/dcopy.h> 42 #include <sys/dcopy_device.h> 43 44 45 /* Number of entries per channel to allocate */ 46 uint_t dcopy_channel_size = 1024; 47 48 49 typedef struct dcopy_list_s { 50 list_t dl_list; 51 kmutex_t dl_mutex; 52 uint_t dl_cnt; /* num entries on list */ 53 } dcopy_list_t; 54 55 /* device state for register/unregister */ 56 struct dcopy_device_s { 57 /* DMA device drivers private pointer */ 58 void *dc_device_private; 59 60 /* to track list of channels from this DMA device */ 61 dcopy_list_t dc_devchan_list; 62 list_node_t dc_device_list_node; 63 64 /* 65 * dc_removing_cnt track how many channels still have to be freed up 66 * before it's safe to allow the DMA device driver to detach. 67 */ 68 uint_t dc_removing_cnt; 69 dcopy_device_cb_t *dc_cb; 70 71 dcopy_device_info_t dc_info; 72 73 }; 74 75 typedef struct dcopy_stats_s { 76 kstat_named_t cs_bytes_xfer; 77 kstat_named_t cs_cmd_alloc; 78 kstat_named_t cs_cmd_post; 79 kstat_named_t cs_cmd_poll; 80 kstat_named_t cs_notify_poll; 81 kstat_named_t cs_notify_pending; 82 kstat_named_t cs_id; 83 kstat_named_t cs_capabilities; 84 } dcopy_stats_t; 85 86 /* DMA channel state */ 87 struct dcopy_channel_s { 88 /* DMA driver channel private pointer */ 89 void *ch_channel_private; 90 91 /* shortcut to device callbacks */ 92 dcopy_device_cb_t *ch_cb; 93 94 /* 95 * number of outstanding allocs for this channel. used to track when 96 * it's safe to free up this channel so the DMA device driver can 97 * detach. 98 */ 99 uint64_t ch_ref_cnt; 100 101 /* state for if channel needs to be removed when ch_ref_cnt gets to 0 */ 102 boolean_t ch_removing; 103 104 list_node_t ch_devchan_list_node; 105 list_node_t ch_globalchan_list_node; 106 107 /* 108 * per channel list of commands actively blocking waiting for 109 * completion. 110 */ 111 dcopy_list_t ch_poll_list; 112 113 /* pointer back to our device */ 114 struct dcopy_device_s *ch_device; 115 116 dcopy_query_channel_t ch_info; 117 118 kstat_t *ch_kstat; 119 dcopy_stats_t ch_stat; 120 }; 121 122 /* 123 * If grabbing both device_list mutex & globalchan_list mutex, 124 * Always grab globalchan_list mutex before device_list mutex 125 */ 126 typedef struct dcopy_state_s { 127 dcopy_list_t d_device_list; 128 dcopy_list_t d_globalchan_list; 129 } dcopy_state_t; 130 dcopy_state_t *dcopy_statep; 131 132 133 /* Module Driver Info */ 134 static struct modlmisc dcopy_modlmisc = { 135 &mod_miscops, 136 "dcopy kernel module" 137 }; 138 139 /* Module Linkage */ 140 static struct modlinkage dcopy_modlinkage = { 141 MODREV_1, 142 &dcopy_modlmisc, 143 NULL 144 }; 145 146 static int dcopy_init(); 147 static void dcopy_fini(); 148 149 static int dcopy_list_init(dcopy_list_t *list, size_t node_size, 150 offset_t link_offset); 151 static void dcopy_list_fini(dcopy_list_t *list); 152 static void dcopy_list_push(dcopy_list_t *list, void *list_node); 153 static void *dcopy_list_pop(dcopy_list_t *list); 154 155 static void dcopy_device_cleanup(dcopy_device_handle_t device, 156 boolean_t do_callback); 157 158 static int dcopy_stats_init(dcopy_handle_t channel); 159 static void dcopy_stats_fini(dcopy_handle_t channel); 160 161 162 /* 163 * _init() 164 */ 165 int 166 _init() 167 { 168 int e; 169 170 e = dcopy_init(); 171 if (e != 0) { 172 return (e); 173 } 174 175 return (mod_install(&dcopy_modlinkage)); 176 } 177 178 179 /* 180 * _info() 181 */ 182 int 183 _info(struct modinfo *modinfop) 184 { 185 return (mod_info(&dcopy_modlinkage, modinfop)); 186 } 187 188 189 /* 190 * _fini() 191 */ 192 int 193 _fini() 194 { 195 int e; 196 197 e = mod_remove(&dcopy_modlinkage); 198 if (e != 0) { 199 return (e); 200 } 201 202 dcopy_fini(); 203 204 return (e); 205 } 206 207 /* 208 * dcopy_init() 209 */ 210 static int 211 dcopy_init() 212 { 213 int e; 214 215 216 dcopy_statep = kmem_zalloc(sizeof (*dcopy_statep), KM_SLEEP); 217 218 /* Initialize the list we use to track device register/unregister */ 219 e = dcopy_list_init(&dcopy_statep->d_device_list, 220 sizeof (struct dcopy_device_s), 221 offsetof(struct dcopy_device_s, dc_device_list_node)); 222 if (e != DCOPY_SUCCESS) { 223 goto dcopyinitfail_device; 224 } 225 226 /* Initialize the list we use to track all DMA channels */ 227 e = dcopy_list_init(&dcopy_statep->d_globalchan_list, 228 sizeof (struct dcopy_channel_s), 229 offsetof(struct dcopy_channel_s, ch_globalchan_list_node)); 230 if (e != DCOPY_SUCCESS) { 231 goto dcopyinitfail_global; 232 } 233 234 return (0); 235 236 dcopyinitfail_global: 237 dcopy_list_fini(&dcopy_statep->d_device_list); 238 dcopyinitfail_device: 239 kmem_free(dcopy_statep, sizeof (*dcopy_statep)); 240 241 return (-1); 242 } 243 244 245 /* 246 * dcopy_fini() 247 */ 248 static void 249 dcopy_fini() 250 { 251 /* 252 * if mod_remove was successfull, we shouldn't have any 253 * devices/channels to worry about. 254 */ 255 ASSERT(list_head(&dcopy_statep->d_globalchan_list.dl_list) == NULL); 256 ASSERT(list_head(&dcopy_statep->d_device_list.dl_list) == NULL); 257 258 dcopy_list_fini(&dcopy_statep->d_globalchan_list); 259 dcopy_list_fini(&dcopy_statep->d_device_list); 260 kmem_free(dcopy_statep, sizeof (*dcopy_statep)); 261 } 262 263 264 /* *** EXTERNAL INTERFACE *** */ 265 /* 266 * dcopy_query() 267 */ 268 void 269 dcopy_query(dcopy_query_t *query) 270 { 271 query->dq_version = DCOPY_QUERY_V0; 272 query->dq_num_channels = dcopy_statep->d_globalchan_list.dl_cnt; 273 } 274 275 276 /* 277 * dcopy_alloc() 278 */ 279 /*ARGSUSED*/ 280 int 281 dcopy_alloc(int flags, dcopy_handle_t *handle) 282 { 283 dcopy_handle_t channel; 284 dcopy_list_t *list; 285 286 287 /* 288 * we don't use the dcopy_list_* code here because we need to due 289 * some non-standard stuff. 290 */ 291 292 list = &dcopy_statep->d_globalchan_list; 293 294 /* 295 * if nothing is on the channel list, return DCOPY_NORESOURCES. This 296 * can happen if there aren't any DMA device registered. 297 */ 298 mutex_enter(&list->dl_mutex); 299 channel = list_head(&list->dl_list); 300 if (channel == NULL) { 301 mutex_exit(&list->dl_mutex); 302 return (DCOPY_NORESOURCES); 303 } 304 305 /* 306 * increment the reference count, and pop the channel off the head and 307 * push it on the tail. This ensures we rotate through the channels. 308 * DMA channels are shared. 309 */ 310 channel->ch_ref_cnt++; 311 list_remove(&list->dl_list, channel); 312 list_insert_tail(&list->dl_list, channel); 313 mutex_exit(&list->dl_mutex); 314 315 *handle = (dcopy_handle_t)channel; 316 return (DCOPY_SUCCESS); 317 } 318 319 320 /* 321 * dcopy_free() 322 */ 323 void 324 dcopy_free(dcopy_handle_t *channel) 325 { 326 dcopy_device_handle_t device; 327 dcopy_list_t *list; 328 boolean_t cleanup = B_FALSE; 329 330 331 ASSERT(*channel != NULL); 332 333 /* 334 * we don't need to add the channel back to the list since we never 335 * removed it. decrement the reference count. 336 */ 337 list = &dcopy_statep->d_globalchan_list; 338 mutex_enter(&list->dl_mutex); 339 (*channel)->ch_ref_cnt--; 340 341 /* 342 * if we need to remove this channel, and the reference count is down 343 * to 0, decrement the number of channels which still need to be 344 * removed on the device. 345 */ 346 if ((*channel)->ch_removing && ((*channel)->ch_ref_cnt == 0)) { 347 device = (*channel)->ch_device; 348 mutex_enter(&device->dc_devchan_list.dl_mutex); 349 device->dc_removing_cnt--; 350 if (device->dc_removing_cnt == 0) { 351 cleanup = B_TRUE; 352 } 353 mutex_exit(&device->dc_devchan_list.dl_mutex); 354 } 355 mutex_exit(&list->dl_mutex); 356 357 /* 358 * if there are no channels which still need to be removed, cleanup the 359 * device state and call back into the DMA device driver to tell them 360 * the device is free. 361 */ 362 if (cleanup) { 363 dcopy_device_cleanup(device, B_TRUE); 364 } 365 366 *channel = NULL; 367 } 368 369 370 /* 371 * dcopy_query_channel() 372 */ 373 void 374 dcopy_query_channel(dcopy_handle_t channel, dcopy_query_channel_t *query) 375 { 376 *query = channel->ch_info; 377 } 378 379 380 /* 381 * dcopy_cmd_alloc() 382 */ 383 int 384 dcopy_cmd_alloc(dcopy_handle_t handle, int flags, dcopy_cmd_t *cmd) 385 { 386 dcopy_handle_t channel; 387 dcopy_cmd_priv_t priv; 388 int e; 389 390 391 channel = handle; 392 393 atomic_inc_64(&channel->ch_stat.cs_cmd_alloc.value.ui64); 394 e = channel->ch_cb->cb_cmd_alloc(channel->ch_channel_private, flags, 395 cmd); 396 if (e == DCOPY_SUCCESS) { 397 priv = (*cmd)->dp_private; 398 priv->pr_channel = channel; 399 /* 400 * we won't initialize the blocking state until we actually 401 * need to block. 402 */ 403 priv->pr_block_init = B_FALSE; 404 } 405 406 return (e); 407 } 408 409 410 /* 411 * dcopy_cmd_free() 412 */ 413 void 414 dcopy_cmd_free(dcopy_cmd_t *cmd) 415 { 416 dcopy_handle_t channel; 417 dcopy_cmd_priv_t priv; 418 419 420 ASSERT(*cmd != NULL); 421 422 priv = (*cmd)->dp_private; 423 channel = priv->pr_channel; 424 425 /* if we initialized the blocking state, clean it up too */ 426 if (priv->pr_block_init) { 427 cv_destroy(&priv->pr_cv); 428 mutex_destroy(&priv->pr_mutex); 429 } 430 431 channel->ch_cb->cb_cmd_free(channel->ch_channel_private, cmd); 432 } 433 434 435 /* 436 * dcopy_cmd_post() 437 */ 438 int 439 dcopy_cmd_post(dcopy_cmd_t cmd) 440 { 441 dcopy_handle_t channel; 442 int e; 443 444 445 channel = cmd->dp_private->pr_channel; 446 447 atomic_inc_64(&channel->ch_stat.cs_cmd_post.value.ui64); 448 if (cmd->dp_cmd == DCOPY_CMD_COPY) { 449 atomic_add_64(&channel->ch_stat.cs_bytes_xfer.value.ui64, 450 cmd->dp.copy.cc_size); 451 } 452 e = channel->ch_cb->cb_cmd_post(channel->ch_channel_private, cmd); 453 if (e != DCOPY_SUCCESS) { 454 return (e); 455 } 456 457 return (DCOPY_SUCCESS); 458 } 459 460 461 /* 462 * dcopy_cmd_poll() 463 */ 464 int 465 dcopy_cmd_poll(dcopy_cmd_t cmd, int flags) 466 { 467 dcopy_handle_t channel; 468 dcopy_cmd_priv_t priv; 469 int e; 470 471 472 priv = cmd->dp_private; 473 channel = priv->pr_channel; 474 475 /* 476 * if the caller is trying to block, they needed to post the 477 * command with DCOPY_CMD_INTR set. 478 */ 479 if ((flags & DCOPY_POLL_BLOCK) && !(cmd->dp_flags & DCOPY_CMD_INTR)) { 480 return (DCOPY_FAILURE); 481 } 482 483 atomic_inc_64(&channel->ch_stat.cs_cmd_poll.value.ui64); 484 485 repoll: 486 e = channel->ch_cb->cb_cmd_poll(channel->ch_channel_private, cmd); 487 if (e == DCOPY_PENDING) { 488 /* 489 * if the command is still active, and the blocking flag 490 * is set. 491 */ 492 if (flags & DCOPY_POLL_BLOCK) { 493 494 /* 495 * if we haven't initialized the state, do it now. A 496 * command can be re-used, so it's possible it's 497 * already been initialized. 498 */ 499 if (!priv->pr_block_init) { 500 priv->pr_block_init = B_TRUE; 501 mutex_init(&priv->pr_mutex, NULL, MUTEX_DRIVER, 502 NULL); 503 cv_init(&priv->pr_cv, NULL, CV_DRIVER, NULL); 504 priv->pr_cmd = cmd; 505 } 506 507 /* push it on the list for blocking commands */ 508 priv->pr_wait = B_TRUE; 509 dcopy_list_push(&channel->ch_poll_list, priv); 510 511 mutex_enter(&priv->pr_mutex); 512 /* 513 * it's possible we already cleared pr_wait before we 514 * grabbed the mutex. 515 */ 516 if (priv->pr_wait) { 517 cv_wait(&priv->pr_cv, &priv->pr_mutex); 518 } 519 mutex_exit(&priv->pr_mutex); 520 521 /* 522 * the command has completed, go back and poll so we 523 * get the status. 524 */ 525 goto repoll; 526 } 527 } 528 529 return (e); 530 } 531 532 /* *** END OF EXTERNAL INTERFACE *** */ 533 534 /* 535 * dcopy_list_init() 536 */ 537 static int 538 dcopy_list_init(dcopy_list_t *list, size_t node_size, offset_t link_offset) 539 { 540 mutex_init(&list->dl_mutex, NULL, MUTEX_DRIVER, NULL); 541 list_create(&list->dl_list, node_size, link_offset); 542 list->dl_cnt = 0; 543 544 return (DCOPY_SUCCESS); 545 } 546 547 548 /* 549 * dcopy_list_fini() 550 */ 551 static void 552 dcopy_list_fini(dcopy_list_t *list) 553 { 554 list_destroy(&list->dl_list); 555 mutex_destroy(&list->dl_mutex); 556 } 557 558 559 /* 560 * dcopy_list_push() 561 */ 562 static void 563 dcopy_list_push(dcopy_list_t *list, void *list_node) 564 { 565 mutex_enter(&list->dl_mutex); 566 list_insert_tail(&list->dl_list, list_node); 567 list->dl_cnt++; 568 mutex_exit(&list->dl_mutex); 569 } 570 571 572 /* 573 * dcopy_list_pop() 574 */ 575 static void * 576 dcopy_list_pop(dcopy_list_t *list) 577 { 578 list_node_t *list_node; 579 580 mutex_enter(&list->dl_mutex); 581 list_node = list_head(&list->dl_list); 582 if (list_node == NULL) { 583 mutex_exit(&list->dl_mutex); 584 return (list_node); 585 } 586 list->dl_cnt--; 587 list_remove(&list->dl_list, list_node); 588 mutex_exit(&list->dl_mutex); 589 590 return (list_node); 591 } 592 593 594 /* *** DEVICE INTERFACE *** */ 595 /* 596 * dcopy_device_register() 597 */ 598 int 599 dcopy_device_register(void *device_private, dcopy_device_info_t *info, 600 dcopy_device_handle_t *handle) 601 { 602 struct dcopy_channel_s *channel; 603 struct dcopy_device_s *device; 604 int e; 605 int i; 606 607 608 /* initialize the per device state */ 609 device = kmem_zalloc(sizeof (*device), KM_SLEEP); 610 device->dc_device_private = device_private; 611 device->dc_info = *info; 612 device->dc_removing_cnt = 0; 613 device->dc_cb = info->di_cb; 614 615 /* 616 * we have a per device channel list so we can remove a device in the 617 * future. 618 */ 619 e = dcopy_list_init(&device->dc_devchan_list, 620 sizeof (struct dcopy_channel_s), 621 offsetof(struct dcopy_channel_s, ch_devchan_list_node)); 622 if (e != DCOPY_SUCCESS) { 623 goto registerfail_devchan; 624 } 625 626 /* 627 * allocate state for each channel, allocate the channel, and then add 628 * the devices dma channels to the devices channel list. 629 */ 630 for (i = 0; i < info->di_num_dma; i++) { 631 channel = kmem_zalloc(sizeof (*channel), KM_SLEEP); 632 channel->ch_device = device; 633 channel->ch_removing = B_FALSE; 634 channel->ch_ref_cnt = 0; 635 channel->ch_cb = info->di_cb; 636 637 e = info->di_cb->cb_channel_alloc(device_private, channel, 638 DCOPY_SLEEP, dcopy_channel_size, &channel->ch_info, 639 &channel->ch_channel_private); 640 if (e != DCOPY_SUCCESS) { 641 kmem_free(channel, sizeof (*channel)); 642 goto registerfail_alloc; 643 } 644 645 e = dcopy_stats_init(channel); 646 if (e != DCOPY_SUCCESS) { 647 info->di_cb->cb_channel_free( 648 &channel->ch_channel_private); 649 kmem_free(channel, sizeof (*channel)); 650 goto registerfail_alloc; 651 } 652 653 e = dcopy_list_init(&channel->ch_poll_list, 654 sizeof (struct dcopy_cmd_priv_s), 655 offsetof(struct dcopy_cmd_priv_s, pr_poll_list_node)); 656 if (e != DCOPY_SUCCESS) { 657 dcopy_stats_fini(channel); 658 info->di_cb->cb_channel_free( 659 &channel->ch_channel_private); 660 kmem_free(channel, sizeof (*channel)); 661 goto registerfail_alloc; 662 } 663 664 dcopy_list_push(&device->dc_devchan_list, channel); 665 } 666 667 /* add the device to device list */ 668 dcopy_list_push(&dcopy_statep->d_device_list, device); 669 670 /* 671 * add the device's dma channels to the global channel list (where 672 * dcopy_alloc's come from) 673 */ 674 mutex_enter(&dcopy_statep->d_globalchan_list.dl_mutex); 675 mutex_enter(&dcopy_statep->d_device_list.dl_mutex); 676 channel = list_head(&device->dc_devchan_list.dl_list); 677 while (channel != NULL) { 678 list_insert_tail(&dcopy_statep->d_globalchan_list.dl_list, 679 channel); 680 dcopy_statep->d_globalchan_list.dl_cnt++; 681 channel = list_next(&device->dc_devchan_list.dl_list, channel); 682 } 683 mutex_exit(&dcopy_statep->d_device_list.dl_mutex); 684 mutex_exit(&dcopy_statep->d_globalchan_list.dl_mutex); 685 686 *handle = device; 687 688 /* last call-back into kernel for dcopy KAPI enabled */ 689 uioa_dcopy_enable(); 690 691 return (DCOPY_SUCCESS); 692 693 registerfail_alloc: 694 channel = list_head(&device->dc_devchan_list.dl_list); 695 while (channel != NULL) { 696 /* remove from the list */ 697 channel = dcopy_list_pop(&device->dc_devchan_list); 698 ASSERT(channel != NULL); 699 700 dcopy_list_fini(&channel->ch_poll_list); 701 dcopy_stats_fini(channel); 702 info->di_cb->cb_channel_free(&channel->ch_channel_private); 703 kmem_free(channel, sizeof (*channel)); 704 } 705 706 dcopy_list_fini(&device->dc_devchan_list); 707 registerfail_devchan: 708 kmem_free(device, sizeof (*device)); 709 710 return (DCOPY_FAILURE); 711 } 712 713 714 /* 715 * dcopy_device_unregister() 716 */ 717 /*ARGSUSED*/ 718 int 719 dcopy_device_unregister(dcopy_device_handle_t *handle) 720 { 721 struct dcopy_channel_s *channel; 722 dcopy_device_handle_t device; 723 boolean_t device_busy; 724 725 /* first call-back into kernel for dcopy KAPI disable */ 726 uioa_dcopy_disable(); 727 728 device = *handle; 729 device_busy = B_FALSE; 730 731 /* 732 * remove the devices dma channels from the global channel list (where 733 * dcopy_alloc's come from) 734 */ 735 mutex_enter(&dcopy_statep->d_globalchan_list.dl_mutex); 736 mutex_enter(&device->dc_devchan_list.dl_mutex); 737 channel = list_head(&device->dc_devchan_list.dl_list); 738 while (channel != NULL) { 739 /* 740 * if the channel has outstanding allocs, mark it as having 741 * to be removed and increment the number of channels which 742 * need to be removed in the device state too. 743 */ 744 if (channel->ch_ref_cnt != 0) { 745 channel->ch_removing = B_TRUE; 746 device_busy = B_TRUE; 747 device->dc_removing_cnt++; 748 } 749 dcopy_statep->d_globalchan_list.dl_cnt--; 750 list_remove(&dcopy_statep->d_globalchan_list.dl_list, channel); 751 channel = list_next(&device->dc_devchan_list.dl_list, channel); 752 } 753 mutex_exit(&device->dc_devchan_list.dl_mutex); 754 mutex_exit(&dcopy_statep->d_globalchan_list.dl_mutex); 755 756 /* 757 * if there are channels which still need to be removed, we will clean 758 * up the device state after they are freed up. 759 */ 760 if (device_busy) { 761 return (DCOPY_PENDING); 762 } 763 764 dcopy_device_cleanup(device, B_FALSE); 765 766 *handle = NULL; 767 return (DCOPY_SUCCESS); 768 } 769 770 771 /* 772 * dcopy_device_cleanup() 773 */ 774 static void 775 dcopy_device_cleanup(dcopy_device_handle_t device, boolean_t do_callback) 776 { 777 struct dcopy_channel_s *channel; 778 779 /* 780 * remove all the channels in the device list, free them, and clean up 781 * the state. 782 */ 783 mutex_enter(&dcopy_statep->d_device_list.dl_mutex); 784 channel = list_head(&device->dc_devchan_list.dl_list); 785 while (channel != NULL) { 786 device->dc_devchan_list.dl_cnt--; 787 list_remove(&device->dc_devchan_list.dl_list, channel); 788 dcopy_list_fini(&channel->ch_poll_list); 789 dcopy_stats_fini(channel); 790 channel->ch_cb->cb_channel_free(&channel->ch_channel_private); 791 kmem_free(channel, sizeof (*channel)); 792 channel = list_head(&device->dc_devchan_list.dl_list); 793 } 794 795 /* remove it from the list of devices */ 796 list_remove(&dcopy_statep->d_device_list.dl_list, device); 797 798 mutex_exit(&dcopy_statep->d_device_list.dl_mutex); 799 800 /* 801 * notify the DMA device driver that the device is free to be 802 * detached. 803 */ 804 if (do_callback) { 805 device->dc_cb->cb_unregister_complete( 806 device->dc_device_private, DCOPY_SUCCESS); 807 } 808 809 dcopy_list_fini(&device->dc_devchan_list); 810 kmem_free(device, sizeof (*device)); 811 } 812 813 814 /* 815 * dcopy_device_channel_notify() 816 */ 817 /*ARGSUSED*/ 818 void 819 dcopy_device_channel_notify(dcopy_handle_t handle, int status) 820 { 821 struct dcopy_channel_s *channel; 822 dcopy_list_t *poll_list; 823 dcopy_cmd_priv_t priv; 824 int e; 825 826 827 ASSERT(status == DCOPY_COMPLETION); 828 channel = handle; 829 830 poll_list = &channel->ch_poll_list; 831 832 /* 833 * when we get a completion notification from the device, go through 834 * all of the commands blocking on this channel and see if they have 835 * completed. Remove the command and wake up the block thread if they 836 * have. Once we hit a command which is still pending, we are done 837 * polling since commands in a channel complete in order. 838 */ 839 mutex_enter(&poll_list->dl_mutex); 840 if (poll_list->dl_cnt != 0) { 841 priv = list_head(&poll_list->dl_list); 842 while (priv != NULL) { 843 atomic_inc_64(&channel-> 844 ch_stat.cs_notify_poll.value.ui64); 845 e = channel->ch_cb->cb_cmd_poll( 846 channel->ch_channel_private, 847 priv->pr_cmd); 848 if (e == DCOPY_PENDING) { 849 atomic_inc_64(&channel-> 850 ch_stat.cs_notify_pending.value.ui64); 851 break; 852 } 853 854 poll_list->dl_cnt--; 855 list_remove(&poll_list->dl_list, priv); 856 857 mutex_enter(&priv->pr_mutex); 858 priv->pr_wait = B_FALSE; 859 cv_signal(&priv->pr_cv); 860 mutex_exit(&priv->pr_mutex); 861 862 priv = list_head(&poll_list->dl_list); 863 } 864 } 865 866 mutex_exit(&poll_list->dl_mutex); 867 } 868 869 870 /* 871 * dcopy_stats_init() 872 */ 873 static int 874 dcopy_stats_init(dcopy_handle_t channel) 875 { 876 #define CHANSTRSIZE 20 877 char chanstr[CHANSTRSIZE]; 878 dcopy_stats_t *stats; 879 int instance; 880 char *name; 881 882 883 stats = &channel->ch_stat; 884 name = (char *)ddi_driver_name(channel->ch_device->dc_info.di_dip); 885 instance = ddi_get_instance(channel->ch_device->dc_info.di_dip); 886 887 (void) snprintf(chanstr, CHANSTRSIZE, "channel%d", 888 (uint32_t)channel->ch_info.qc_chan_num); 889 890 channel->ch_kstat = kstat_create(name, instance, chanstr, "misc", 891 KSTAT_TYPE_NAMED, sizeof (dcopy_stats_t) / sizeof (kstat_named_t), 892 KSTAT_FLAG_VIRTUAL); 893 if (channel->ch_kstat == NULL) { 894 return (DCOPY_FAILURE); 895 } 896 channel->ch_kstat->ks_data = stats; 897 898 kstat_named_init(&stats->cs_bytes_xfer, "bytes_xfer", 899 KSTAT_DATA_UINT64); 900 kstat_named_init(&stats->cs_cmd_alloc, "cmd_alloc", 901 KSTAT_DATA_UINT64); 902 kstat_named_init(&stats->cs_cmd_post, "cmd_post", 903 KSTAT_DATA_UINT64); 904 kstat_named_init(&stats->cs_cmd_poll, "cmd_poll", 905 KSTAT_DATA_UINT64); 906 kstat_named_init(&stats->cs_notify_poll, "notify_poll", 907 KSTAT_DATA_UINT64); 908 kstat_named_init(&stats->cs_notify_pending, "notify_pending", 909 KSTAT_DATA_UINT64); 910 kstat_named_init(&stats->cs_id, "id", 911 KSTAT_DATA_UINT64); 912 kstat_named_init(&stats->cs_capabilities, "capabilities", 913 KSTAT_DATA_UINT64); 914 915 kstat_install(channel->ch_kstat); 916 917 channel->ch_stat.cs_id.value.ui64 = channel->ch_info.qc_id; 918 channel->ch_stat.cs_capabilities.value.ui64 = 919 channel->ch_info.qc_capabilities; 920 921 return (DCOPY_SUCCESS); 922 } 923 924 925 /* 926 * dcopy_stats_fini() 927 */ 928 static void 929 dcopy_stats_fini(dcopy_handle_t channel) 930 { 931 kstat_delete(channel->ch_kstat); 932 } 933 /* *** END OF DEVICE INTERFACE *** */ 934