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_cback: 237 dcopy_list_fini(&dcopy_statep->d_globalchan_list); 238 dcopyinitfail_global: 239 dcopy_list_fini(&dcopy_statep->d_device_list); 240 dcopyinitfail_device: 241 kmem_free(dcopy_statep, sizeof (*dcopy_statep)); 242 243 return (-1); 244 } 245 246 247 /* 248 * dcopy_fini() 249 */ 250 static void 251 dcopy_fini() 252 { 253 /* 254 * if mod_remove was successfull, we shouldn't have any 255 * devices/channels to worry about. 256 */ 257 ASSERT(list_head(&dcopy_statep->d_globalchan_list.dl_list) == NULL); 258 ASSERT(list_head(&dcopy_statep->d_device_list.dl_list) == NULL); 259 260 dcopy_list_fini(&dcopy_statep->d_globalchan_list); 261 dcopy_list_fini(&dcopy_statep->d_device_list); 262 kmem_free(dcopy_statep, sizeof (*dcopy_statep)); 263 } 264 265 266 /* *** EXTERNAL INTERFACE *** */ 267 /* 268 * dcopy_query() 269 */ 270 void 271 dcopy_query(dcopy_query_t *query) 272 { 273 query->dq_version = DCOPY_QUERY_V0; 274 query->dq_num_channels = dcopy_statep->d_globalchan_list.dl_cnt; 275 } 276 277 278 /* 279 * dcopy_alloc() 280 */ 281 /*ARGSUSED*/ 282 int 283 dcopy_alloc(int flags, dcopy_handle_t *handle) 284 { 285 dcopy_handle_t channel; 286 dcopy_list_t *list; 287 288 289 /* 290 * we don't use the dcopy_list_* code here because we need to due 291 * some non-standard stuff. 292 */ 293 294 list = &dcopy_statep->d_globalchan_list; 295 296 /* 297 * if nothing is on the channel list, return DCOPY_NORESOURCES. This 298 * can happen if there aren't any DMA device registered. 299 */ 300 mutex_enter(&list->dl_mutex); 301 channel = list_head(&list->dl_list); 302 if (channel == NULL) { 303 mutex_exit(&list->dl_mutex); 304 return (DCOPY_NORESOURCES); 305 } 306 307 /* 308 * increment the reference count, and pop the channel off the head and 309 * push it on the tail. This ensures we rotate through the channels. 310 * DMA channels are shared. 311 */ 312 channel->ch_ref_cnt++; 313 list_remove(&list->dl_list, channel); 314 list_insert_tail(&list->dl_list, channel); 315 mutex_exit(&list->dl_mutex); 316 317 *handle = (dcopy_handle_t)channel; 318 return (DCOPY_SUCCESS); 319 } 320 321 322 /* 323 * dcopy_free() 324 */ 325 void 326 dcopy_free(dcopy_handle_t *channel) 327 { 328 dcopy_device_handle_t device; 329 dcopy_list_t *list; 330 boolean_t cleanup = B_FALSE; 331 332 333 ASSERT(*channel != NULL); 334 335 /* 336 * we don't need to add the channel back to the list since we never 337 * removed it. decrement the reference count. 338 */ 339 list = &dcopy_statep->d_globalchan_list; 340 mutex_enter(&list->dl_mutex); 341 (*channel)->ch_ref_cnt--; 342 343 /* 344 * if we need to remove this channel, and the reference count is down 345 * to 0, decrement the number of channels which still need to be 346 * removed on the device. 347 */ 348 if ((*channel)->ch_removing && ((*channel)->ch_ref_cnt == 0)) { 349 device = (*channel)->ch_device; 350 mutex_enter(&device->dc_devchan_list.dl_mutex); 351 device->dc_removing_cnt--; 352 if (device->dc_removing_cnt == 0) { 353 cleanup = B_TRUE; 354 } 355 mutex_exit(&device->dc_devchan_list.dl_mutex); 356 } 357 mutex_exit(&list->dl_mutex); 358 359 /* 360 * if there are no channels which still need to be removed, cleanup the 361 * device state and call back into the DMA device driver to tell them 362 * the device is free. 363 */ 364 if (cleanup) { 365 dcopy_device_cleanup(device, B_TRUE); 366 } 367 368 *channel = NULL; 369 } 370 371 372 /* 373 * dcopy_query_channel() 374 */ 375 void 376 dcopy_query_channel(dcopy_handle_t channel, dcopy_query_channel_t *query) 377 { 378 *query = channel->ch_info; 379 } 380 381 382 /* 383 * dcopy_cmd_alloc() 384 */ 385 int 386 dcopy_cmd_alloc(dcopy_handle_t handle, int flags, dcopy_cmd_t *cmd) 387 { 388 dcopy_handle_t channel; 389 dcopy_cmd_priv_t priv; 390 int e; 391 392 393 channel = handle; 394 395 atomic_inc_64(&channel->ch_stat.cs_cmd_alloc.value.ui64); 396 e = channel->ch_cb->cb_cmd_alloc(channel->ch_channel_private, flags, 397 cmd); 398 if (e == DCOPY_SUCCESS) { 399 priv = (*cmd)->dp_private; 400 priv->pr_channel = channel; 401 /* 402 * we won't initialize the blocking state until we actually 403 * need to block. 404 */ 405 priv->pr_block_init = B_FALSE; 406 } 407 408 return (e); 409 } 410 411 412 /* 413 * dcopy_cmd_free() 414 */ 415 void 416 dcopy_cmd_free(dcopy_cmd_t *cmd) 417 { 418 dcopy_handle_t channel; 419 dcopy_cmd_priv_t priv; 420 421 422 ASSERT(*cmd != NULL); 423 424 priv = (*cmd)->dp_private; 425 channel = priv->pr_channel; 426 427 /* if we initialized the blocking state, clean it up too */ 428 if (priv->pr_block_init) { 429 cv_destroy(&priv->pr_cv); 430 mutex_destroy(&priv->pr_mutex); 431 } 432 433 channel->ch_cb->cb_cmd_free(channel->ch_channel_private, cmd); 434 } 435 436 437 /* 438 * dcopy_cmd_post() 439 */ 440 int 441 dcopy_cmd_post(dcopy_cmd_t cmd) 442 { 443 dcopy_handle_t channel; 444 int e; 445 446 447 channel = cmd->dp_private->pr_channel; 448 449 atomic_inc_64(&channel->ch_stat.cs_cmd_post.value.ui64); 450 if (cmd->dp_cmd == DCOPY_CMD_COPY) { 451 atomic_add_64(&channel->ch_stat.cs_bytes_xfer.value.ui64, 452 cmd->dp.copy.cc_size); 453 } 454 e = channel->ch_cb->cb_cmd_post(channel->ch_channel_private, cmd); 455 if (e != DCOPY_SUCCESS) { 456 return (e); 457 } 458 459 return (DCOPY_SUCCESS); 460 } 461 462 463 /* 464 * dcopy_cmd_poll() 465 */ 466 int 467 dcopy_cmd_poll(dcopy_cmd_t cmd, int flags) 468 { 469 dcopy_handle_t channel; 470 dcopy_cmd_priv_t priv; 471 int e; 472 473 474 priv = cmd->dp_private; 475 channel = priv->pr_channel; 476 477 /* 478 * if the caller is trying to block, they needed to post the 479 * command with DCOPY_CMD_INTR set. 480 */ 481 if ((flags & DCOPY_POLL_BLOCK) && !(cmd->dp_flags & DCOPY_CMD_INTR)) { 482 return (DCOPY_FAILURE); 483 } 484 485 atomic_inc_64(&channel->ch_stat.cs_cmd_poll.value.ui64); 486 487 repoll: 488 e = channel->ch_cb->cb_cmd_poll(channel->ch_channel_private, cmd); 489 if (e == DCOPY_PENDING) { 490 /* 491 * if the command is still active, and the blocking flag 492 * is set. 493 */ 494 if (flags & DCOPY_POLL_BLOCK) { 495 496 /* 497 * if we haven't initialized the state, do it now. A 498 * command can be re-used, so it's possible it's 499 * already been initialized. 500 */ 501 if (!priv->pr_block_init) { 502 priv->pr_block_init = B_TRUE; 503 mutex_init(&priv->pr_mutex, NULL, MUTEX_DRIVER, 504 NULL); 505 cv_init(&priv->pr_cv, NULL, CV_DRIVER, NULL); 506 priv->pr_cmd = cmd; 507 } 508 509 /* push it on the list for blocking commands */ 510 priv->pr_wait = B_TRUE; 511 dcopy_list_push(&channel->ch_poll_list, priv); 512 513 mutex_enter(&priv->pr_mutex); 514 /* 515 * it's possible we already cleared pr_wait before we 516 * grabbed the mutex. 517 */ 518 if (priv->pr_wait) { 519 cv_wait(&priv->pr_cv, &priv->pr_mutex); 520 } 521 mutex_exit(&priv->pr_mutex); 522 523 /* 524 * the command has completed, go back and poll so we 525 * get the status. 526 */ 527 goto repoll; 528 } 529 } 530 531 return (e); 532 } 533 534 /* *** END OF EXTERNAL INTERFACE *** */ 535 536 /* 537 * dcopy_list_init() 538 */ 539 static int 540 dcopy_list_init(dcopy_list_t *list, size_t node_size, offset_t link_offset) 541 { 542 mutex_init(&list->dl_mutex, NULL, MUTEX_DRIVER, NULL); 543 list_create(&list->dl_list, node_size, link_offset); 544 list->dl_cnt = 0; 545 546 return (DCOPY_SUCCESS); 547 } 548 549 550 /* 551 * dcopy_list_fini() 552 */ 553 static void 554 dcopy_list_fini(dcopy_list_t *list) 555 { 556 list_destroy(&list->dl_list); 557 mutex_destroy(&list->dl_mutex); 558 } 559 560 561 /* 562 * dcopy_list_push() 563 */ 564 static void 565 dcopy_list_push(dcopy_list_t *list, void *list_node) 566 { 567 mutex_enter(&list->dl_mutex); 568 list_insert_tail(&list->dl_list, list_node); 569 list->dl_cnt++; 570 mutex_exit(&list->dl_mutex); 571 } 572 573 574 /* 575 * dcopy_list_pop() 576 */ 577 static void * 578 dcopy_list_pop(dcopy_list_t *list) 579 { 580 list_node_t *list_node; 581 582 mutex_enter(&list->dl_mutex); 583 list_node = list_head(&list->dl_list); 584 if (list_node == NULL) { 585 mutex_exit(&list->dl_mutex); 586 return (list_node); 587 } 588 list->dl_cnt--; 589 list_remove(&list->dl_list, list_node); 590 mutex_exit(&list->dl_mutex); 591 592 return (list_node); 593 } 594 595 596 /* *** DEVICE INTERFACE *** */ 597 /* 598 * dcopy_device_register() 599 */ 600 int 601 dcopy_device_register(void *device_private, dcopy_device_info_t *info, 602 dcopy_device_handle_t *handle) 603 { 604 struct dcopy_channel_s *channel; 605 struct dcopy_device_s *device; 606 int e; 607 int i; 608 609 610 /* initialize the per device state */ 611 device = kmem_zalloc(sizeof (*device), KM_SLEEP); 612 device->dc_device_private = device_private; 613 device->dc_info = *info; 614 device->dc_removing_cnt = 0; 615 device->dc_cb = info->di_cb; 616 617 /* 618 * we have a per device channel list so we can remove a device in the 619 * future. 620 */ 621 e = dcopy_list_init(&device->dc_devchan_list, 622 sizeof (struct dcopy_channel_s), 623 offsetof(struct dcopy_channel_s, ch_devchan_list_node)); 624 if (e != DCOPY_SUCCESS) { 625 goto registerfail_devchan; 626 } 627 628 /* 629 * allocate state for each channel, allocate the channel, and then add 630 * the devices dma channels to the devices channel list. 631 */ 632 for (i = 0; i < info->di_num_dma; i++) { 633 channel = kmem_zalloc(sizeof (*channel), KM_SLEEP); 634 channel->ch_device = device; 635 channel->ch_removing = B_FALSE; 636 channel->ch_ref_cnt = 0; 637 channel->ch_cb = info->di_cb; 638 639 e = info->di_cb->cb_channel_alloc(device_private, channel, 640 DCOPY_SLEEP, dcopy_channel_size, &channel->ch_info, 641 &channel->ch_channel_private); 642 if (e != DCOPY_SUCCESS) { 643 kmem_free(channel, sizeof (*channel)); 644 goto registerfail_alloc; 645 } 646 647 e = dcopy_stats_init(channel); 648 if (e != DCOPY_SUCCESS) { 649 info->di_cb->cb_channel_free( 650 &channel->ch_channel_private); 651 kmem_free(channel, sizeof (*channel)); 652 goto registerfail_alloc; 653 } 654 655 e = dcopy_list_init(&channel->ch_poll_list, 656 sizeof (struct dcopy_cmd_priv_s), 657 offsetof(struct dcopy_cmd_priv_s, pr_poll_list_node)); 658 if (e != DCOPY_SUCCESS) { 659 dcopy_stats_fini(channel); 660 info->di_cb->cb_channel_free( 661 &channel->ch_channel_private); 662 kmem_free(channel, sizeof (*channel)); 663 goto registerfail_alloc; 664 } 665 666 dcopy_list_push(&device->dc_devchan_list, channel); 667 } 668 669 /* add the device to device list */ 670 dcopy_list_push(&dcopy_statep->d_device_list, device); 671 672 /* 673 * add the device's dma channels to the global channel list (where 674 * dcopy_alloc's come from) 675 */ 676 mutex_enter(&dcopy_statep->d_globalchan_list.dl_mutex); 677 mutex_enter(&dcopy_statep->d_device_list.dl_mutex); 678 channel = list_head(&device->dc_devchan_list.dl_list); 679 while (channel != NULL) { 680 list_insert_tail(&dcopy_statep->d_globalchan_list.dl_list, 681 channel); 682 dcopy_statep->d_globalchan_list.dl_cnt++; 683 channel = list_next(&device->dc_devchan_list.dl_list, channel); 684 } 685 mutex_exit(&dcopy_statep->d_device_list.dl_mutex); 686 mutex_exit(&dcopy_statep->d_globalchan_list.dl_mutex); 687 688 *handle = device; 689 690 /* last call-back into kernel for dcopy KAPI enabled */ 691 uioa_dcopy_enable(); 692 693 return (DCOPY_SUCCESS); 694 695 registerfail_alloc: 696 channel = list_head(&device->dc_devchan_list.dl_list); 697 while (channel != NULL) { 698 /* remove from the list */ 699 channel = dcopy_list_pop(&device->dc_devchan_list); 700 ASSERT(channel != NULL); 701 702 dcopy_list_fini(&channel->ch_poll_list); 703 dcopy_stats_fini(channel); 704 info->di_cb->cb_channel_free(&channel->ch_channel_private); 705 kmem_free(channel, sizeof (*channel)); 706 } 707 708 dcopy_list_fini(&device->dc_devchan_list); 709 registerfail_devchan: 710 kmem_free(device, sizeof (*device)); 711 712 return (DCOPY_FAILURE); 713 } 714 715 716 /* 717 * dcopy_device_unregister() 718 */ 719 /*ARGSUSED*/ 720 int 721 dcopy_device_unregister(dcopy_device_handle_t *handle) 722 { 723 struct dcopy_channel_s *channel; 724 dcopy_device_handle_t device; 725 boolean_t device_busy; 726 727 /* first call-back into kernel for dcopy KAPI disable */ 728 uioa_dcopy_disable(); 729 730 device = *handle; 731 device_busy = B_FALSE; 732 733 /* 734 * remove the devices dma channels from the global channel list (where 735 * dcopy_alloc's come from) 736 */ 737 mutex_enter(&dcopy_statep->d_globalchan_list.dl_mutex); 738 mutex_enter(&device->dc_devchan_list.dl_mutex); 739 channel = list_head(&device->dc_devchan_list.dl_list); 740 while (channel != NULL) { 741 /* 742 * if the channel has outstanding allocs, mark it as having 743 * to be removed and increment the number of channels which 744 * need to be removed in the device state too. 745 */ 746 if (channel->ch_ref_cnt != 0) { 747 channel->ch_removing = B_TRUE; 748 device_busy = B_TRUE; 749 device->dc_removing_cnt++; 750 } 751 dcopy_statep->d_globalchan_list.dl_cnt--; 752 list_remove(&dcopy_statep->d_globalchan_list.dl_list, channel); 753 channel = list_next(&device->dc_devchan_list.dl_list, channel); 754 } 755 mutex_exit(&device->dc_devchan_list.dl_mutex); 756 mutex_exit(&dcopy_statep->d_globalchan_list.dl_mutex); 757 758 /* 759 * if there are channels which still need to be removed, we will clean 760 * up the device state after they are freed up. 761 */ 762 if (device_busy) { 763 return (DCOPY_PENDING); 764 } 765 766 dcopy_device_cleanup(device, B_FALSE); 767 768 *handle = NULL; 769 return (DCOPY_SUCCESS); 770 } 771 772 773 /* 774 * dcopy_device_cleanup() 775 */ 776 static void 777 dcopy_device_cleanup(dcopy_device_handle_t device, boolean_t do_callback) 778 { 779 struct dcopy_channel_s *channel; 780 781 /* 782 * remove all the channels in the device list, free them, and clean up 783 * the state. 784 */ 785 mutex_enter(&dcopy_statep->d_device_list.dl_mutex); 786 channel = list_head(&device->dc_devchan_list.dl_list); 787 while (channel != NULL) { 788 device->dc_devchan_list.dl_cnt--; 789 list_remove(&device->dc_devchan_list.dl_list, channel); 790 dcopy_list_fini(&channel->ch_poll_list); 791 dcopy_stats_fini(channel); 792 channel->ch_cb->cb_channel_free(&channel->ch_channel_private); 793 kmem_free(channel, sizeof (*channel)); 794 channel = list_head(&device->dc_devchan_list.dl_list); 795 } 796 797 /* remove it from the list of devices */ 798 list_remove(&dcopy_statep->d_device_list.dl_list, device); 799 800 mutex_exit(&dcopy_statep->d_device_list.dl_mutex); 801 802 /* 803 * notify the DMA device driver that the device is free to be 804 * detached. 805 */ 806 if (do_callback) { 807 device->dc_cb->cb_unregister_complete( 808 device->dc_device_private, DCOPY_SUCCESS); 809 } 810 811 dcopy_list_fini(&device->dc_devchan_list); 812 kmem_free(device, sizeof (*device)); 813 } 814 815 816 /* 817 * dcopy_device_channel_notify() 818 */ 819 /*ARGSUSED*/ 820 void 821 dcopy_device_channel_notify(dcopy_handle_t handle, int status) 822 { 823 struct dcopy_channel_s *channel; 824 dcopy_list_t *poll_list; 825 dcopy_cmd_priv_t priv; 826 int e; 827 828 829 ASSERT(status == DCOPY_COMPLETION); 830 channel = handle; 831 832 poll_list = &channel->ch_poll_list; 833 834 /* 835 * when we get a completion notification from the device, go through 836 * all of the commands blocking on this channel and see if they have 837 * completed. Remove the command and wake up the block thread if they 838 * have. Once we hit a command which is still pending, we are done 839 * polling since commands in a channel complete in order. 840 */ 841 mutex_enter(&poll_list->dl_mutex); 842 if (poll_list->dl_cnt != 0) { 843 priv = list_head(&poll_list->dl_list); 844 while (priv != NULL) { 845 atomic_inc_64(&channel-> 846 ch_stat.cs_notify_poll.value.ui64); 847 e = channel->ch_cb->cb_cmd_poll( 848 channel->ch_channel_private, 849 priv->pr_cmd); 850 if (e == DCOPY_PENDING) { 851 atomic_inc_64(&channel-> 852 ch_stat.cs_notify_pending.value.ui64); 853 break; 854 } 855 856 poll_list->dl_cnt--; 857 list_remove(&poll_list->dl_list, priv); 858 859 mutex_enter(&priv->pr_mutex); 860 priv->pr_wait = B_FALSE; 861 cv_signal(&priv->pr_cv); 862 mutex_exit(&priv->pr_mutex); 863 864 priv = list_head(&poll_list->dl_list); 865 } 866 } 867 868 mutex_exit(&poll_list->dl_mutex); 869 } 870 871 872 /* 873 * dcopy_stats_init() 874 */ 875 static int 876 dcopy_stats_init(dcopy_handle_t channel) 877 { 878 #define CHANSTRSIZE 20 879 char chanstr[CHANSTRSIZE]; 880 dcopy_stats_t *stats; 881 int instance; 882 char *name; 883 884 885 stats = &channel->ch_stat; 886 name = (char *)ddi_driver_name(channel->ch_device->dc_info.di_dip); 887 instance = ddi_get_instance(channel->ch_device->dc_info.di_dip); 888 889 (void) snprintf(chanstr, CHANSTRSIZE, "channel%d", 890 (uint32_t)channel->ch_info.qc_chan_num); 891 892 channel->ch_kstat = kstat_create(name, instance, chanstr, "misc", 893 KSTAT_TYPE_NAMED, sizeof (dcopy_stats_t) / sizeof (kstat_named_t), 894 KSTAT_FLAG_VIRTUAL); 895 if (channel->ch_kstat == NULL) { 896 return (DCOPY_FAILURE); 897 } 898 channel->ch_kstat->ks_data = stats; 899 900 kstat_named_init(&stats->cs_bytes_xfer, "bytes_xfer", 901 KSTAT_DATA_UINT64); 902 kstat_named_init(&stats->cs_cmd_alloc, "cmd_alloc", 903 KSTAT_DATA_UINT64); 904 kstat_named_init(&stats->cs_cmd_post, "cmd_post", 905 KSTAT_DATA_UINT64); 906 kstat_named_init(&stats->cs_cmd_poll, "cmd_poll", 907 KSTAT_DATA_UINT64); 908 kstat_named_init(&stats->cs_notify_poll, "notify_poll", 909 KSTAT_DATA_UINT64); 910 kstat_named_init(&stats->cs_notify_pending, "notify_pending", 911 KSTAT_DATA_UINT64); 912 kstat_named_init(&stats->cs_id, "id", 913 KSTAT_DATA_UINT64); 914 kstat_named_init(&stats->cs_capabilities, "capabilities", 915 KSTAT_DATA_UINT64); 916 917 kstat_install(channel->ch_kstat); 918 919 channel->ch_stat.cs_id.value.ui64 = channel->ch_info.qc_id; 920 channel->ch_stat.cs_capabilities.value.ui64 = 921 channel->ch_info.qc_capabilities; 922 923 return (DCOPY_SUCCESS); 924 } 925 926 927 /* 928 * dcopy_stats_fini() 929 */ 930 static void 931 dcopy_stats_fini(dcopy_handle_t channel) 932 { 933 kstat_delete(channel->ch_kstat); 934 } 935 /* *** END OF DEVICE INTERFACE *** */