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