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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 /* 29 * Domain Services Module System Specific Code. 30 * 31 * The Domain Services (DS) module is responsible for communication 32 * with external service entities. It provides a kernel API for clients to 33 * publish capabilities and handles the low level communication and 34 * version negotiation required to export those capabilities to any 35 * interested service entity. Once a capability has been successfully 36 * registered with a service entity, the DS module facilitates all 37 * data transfers between the service entity and the client providing 38 * that particular capability. 39 * 40 * This file provides the system interfaces that are required for 41 * the ds.c module, which is common to both Solaris and VBSC (linux). 42 */ 43 44 #include <sys/modctl.h> 45 #include <sys/ksynch.h> 46 #include <sys/taskq.h> 47 #include <sys/disp.h> 48 #include <sys/cmn_err.h> 49 #include <sys/note.h> 50 #include <sys/mach_descrip.h> 51 #include <sys/mdesc.h> 52 #include <sys/mdeg.h> 53 #include <sys/ldc.h> 54 #include <sys/ds.h> 55 #include <sys/ds_impl.h> 56 57 /* 58 * All DS ports in the system 59 * 60 * The list of DS ports is read in from the MD when the DS module is 61 * initialized and is never modified. This eliminates the need for 62 * locking to access the port array itself. Access to the individual 63 * ports are synchronized at the port level. 64 */ 65 ds_port_t ds_ports[DS_MAX_PORTS]; 66 ds_portset_t ds_allports; /* all DS ports in the system */ 67 68 /* 69 * Table of registered services 70 * 71 * Locking: Accesses to the table of services are synchronized using 72 * a mutex lock. The reader lock must be held when looking up service 73 * information in the table. The writer lock must be held when any 74 * service information is being modified. 75 */ 76 ds_svcs_t ds_svcs; 77 78 /* 79 * Taskq for internal task processing 80 */ 81 static taskq_t *ds_taskq; 82 83 /* 84 * The actual required number of parallel threads is not expected 85 * to be very large. Use the maximum number of CPUs in the system 86 * as a rough upper bound. 87 */ 88 #define DS_MAX_TASKQ_THR NCPU 89 #define DS_DISPATCH(fn, arg) taskq_dispatch(ds_taskq, fn, arg, TQ_SLEEP) 90 91 ds_domain_hdl_t ds_my_domain_hdl = DS_DHDL_INVALID; 92 char *ds_my_domain_name = NULL; 93 94 #ifdef DEBUG 95 /* 96 * Debug Flag 97 */ 98 uint_t ds_debug = 0; 99 #endif /* DEBUG */ 100 101 /* initialization functions */ 102 static void ds_init(void); 103 static void ds_fini(void); 104 static int ds_ports_init(void); 105 static int ds_ports_fini(void); 106 107 /* port utilities */ 108 static int ds_port_add(md_t *mdp, mde_cookie_t port, mde_cookie_t chan); 109 110 /* log functions */ 111 static void ds_log_init(void); 112 static void ds_log_fini(void); 113 static int ds_log_remove(void); 114 static void ds_log_purge(void *arg); 115 116 static struct modlmisc modlmisc = { 117 &mod_miscops, 118 "Domain Services 1.9" 119 }; 120 121 static struct modlinkage modlinkage = { 122 MODREV_1, 123 (void *)&modlmisc, 124 NULL 125 }; 126 127 int 128 _init(void) 129 { 130 int rv; 131 132 /* 133 * Perform all internal setup before initializing 134 * the DS ports. This ensures that events can be 135 * processed as soon as the port comes up. 136 */ 137 ds_init(); 138 139 /* force attach channel nexus */ 140 (void) i_ddi_attach_hw_nodes("cnex"); 141 142 if ((rv = ds_ports_init()) != 0) { 143 cmn_err(CE_WARN, "Domain Services initialization failed"); 144 ds_fini(); 145 return (rv); 146 } 147 148 if ((rv = mod_install(&modlinkage)) != 0) { 149 (void) ds_ports_fini(); 150 ds_fini(); 151 } 152 153 return (rv); 154 } 155 156 int 157 _info(struct modinfo *modinfop) 158 { 159 return (mod_info(&modlinkage, modinfop)); 160 } 161 162 int 163 _fini(void) 164 { 165 int rv; 166 167 if ((rv = mod_remove(&modlinkage)) == 0) { 168 (void) ds_ports_fini(); 169 ds_fini(); 170 } 171 172 return (rv); 173 } 174 175 static void 176 ds_fini(void) 177 { 178 /* 179 * Flip the enabled switch to make sure that no 180 * incoming events get dispatched while things 181 * are being torn down. 182 */ 183 ds_enabled = B_FALSE; 184 185 /* 186 * Destroy the taskq. 187 */ 188 taskq_destroy(ds_taskq); 189 190 /* 191 * Destroy the message log. 192 */ 193 ds_log_fini(); 194 195 /* 196 * Deallocate the table of registered services 197 */ 198 199 /* clear out all entries */ 200 mutex_enter(&ds_svcs.lock); 201 (void) ds_walk_svcs(ds_svc_free, NULL); 202 mutex_exit(&ds_svcs.lock); 203 204 /* destroy the table itself */ 205 DS_FREE(ds_svcs.tbl, ds_svcs.maxsvcs * sizeof (ds_svc_t *)); 206 mutex_destroy(&ds_svcs.lock); 207 bzero(&ds_svcs, sizeof (ds_svcs)); 208 } 209 210 /* 211 * Initialize the list of ports based on the MD. 212 */ 213 static int 214 ds_ports_init(void) 215 { 216 int idx; 217 int rv = 0; 218 md_t *mdp; 219 int num_nodes; 220 int listsz; 221 mde_cookie_t rootnode; 222 mde_cookie_t dsnode; 223 mde_cookie_t *portp = NULL; 224 mde_cookie_t *chanp = NULL; 225 int nport; 226 int nchan; 227 228 if ((mdp = md_get_handle()) == NULL) { 229 cmn_err(CE_WARN, "Unable to initialize machine description"); 230 return (-1); 231 } 232 233 num_nodes = md_node_count(mdp); 234 ASSERT(num_nodes > 0); 235 236 listsz = num_nodes * sizeof (mde_cookie_t); 237 238 /* allocate temporary storage for MD scans */ 239 portp = kmem_zalloc(listsz, KM_SLEEP); 240 chanp = kmem_zalloc(listsz, KM_SLEEP); 241 242 rootnode = md_root_node(mdp); 243 ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE); 244 245 /* 246 * The root of the search for DS port nodes is the 247 * DS node. Perform a scan to find that node. 248 */ 249 nport = md_scan_dag(mdp, rootnode, md_find_name(mdp, DS_MD_ROOT_NAME), 250 md_find_name(mdp, "fwd"), portp); 251 252 if (nport <= 0) { 253 DS_DBG_MD(CE_NOTE, "No '%s' node in MD", DS_MD_ROOT_NAME); 254 goto done; 255 } 256 257 /* expecting only one DS node */ 258 if (nport != 1) { 259 DS_DBG_MD(CE_NOTE, "Expected one '%s' node in the MD, found %d", 260 DS_MD_ROOT_NAME, nport); 261 } 262 263 dsnode = portp[0]; 264 265 /* find all the DS ports in the MD */ 266 nport = md_scan_dag(mdp, dsnode, md_find_name(mdp, DS_MD_PORT_NAME), 267 md_find_name(mdp, "fwd"), portp); 268 269 if (nport <= 0) { 270 DS_DBG_MD(CE_NOTE, "No '%s' nodes in MD", DS_MD_PORT_NAME); 271 goto done; 272 } 273 274 /* 275 * Initialize all the ports found in the MD. 276 */ 277 for (idx = 0; idx < nport; idx++) { 278 279 /* get the channels for this port */ 280 nchan = md_scan_dag(mdp, portp[idx], 281 md_find_name(mdp, DS_MD_CHAN_NAME), 282 md_find_name(mdp, "fwd"), chanp); 283 284 if (nchan <= 0) { 285 cmn_err(CE_WARN, "No '%s' node for DS port", 286 DS_MD_CHAN_NAME); 287 rv = -1; 288 goto done; 289 } 290 291 /* expecting only one channel */ 292 if (nchan != 1) { 293 DS_DBG_MD(CE_NOTE, "Expected one '%s' node for DS " 294 " port, found %d", DS_MD_CHAN_NAME, nchan); 295 } 296 297 if (ds_port_add(mdp, portp[idx], chanp[0]) != 0) { 298 rv = -1; 299 goto done; 300 } 301 } 302 303 done: 304 if (rv != 0) 305 (void) ds_ports_fini(); 306 307 DS_FREE(portp, listsz); 308 DS_FREE(chanp, listsz); 309 310 (void) md_fini_handle(mdp); 311 312 return (rv); 313 } 314 315 static int 316 ds_ports_fini(void) 317 { 318 int idx; 319 320 /* 321 * Tear down each initialized port. 322 */ 323 for (idx = 0; idx < DS_MAX_PORTS; idx++) { 324 if (DS_PORT_IN_SET(ds_allports, idx)) { 325 (void) ds_remove_port(idx, 1); 326 } 327 } 328 329 return (0); 330 } 331 332 static int 333 ds_port_add(md_t *mdp, mde_cookie_t port, mde_cookie_t chan) 334 { 335 uint64_t port_id; 336 uint64_t ldc_id; 337 uint8_t *ldcidsp; 338 int len; 339 340 /* get the ID for this port */ 341 if (md_get_prop_val(mdp, port, "id", &port_id) != 0) { 342 cmn_err(CE_WARN, "%s: port 'id' property not found", 343 __func__); 344 return (-1); 345 } 346 347 /* sanity check the port id */ 348 if (port_id > DS_MAX_PORT_ID) { 349 cmn_err(CE_WARN, "%s: port ID %ld out of range", 350 __func__, port_id); 351 return (-1); 352 } 353 354 /* get the channel ID for this port */ 355 if (md_get_prop_val(mdp, chan, "id", &ldc_id) != 0) { 356 cmn_err(CE_WARN, "ds@%lx: %s: no channel 'id' property", 357 port_id, __func__); 358 return (-1); 359 } 360 361 if (ds_add_port(port_id, ldc_id, DS_DHDL_INVALID, NULL, 1) != 0) 362 return (-1); 363 364 /* 365 * Identify the SP Port. The SP port is the only one with 366 * the "ldc-ids" property, and is only on the primary domain. 367 */ 368 if (ds_sp_port_id == DS_PORTID_INVALID && 369 md_get_prop_data(mdp, port, "ldc-ids", &ldcidsp, &len) == 0) { 370 ds_sp_port_id = port_id; 371 } 372 373 return (0); 374 } 375 376 void 377 ds_set_my_dom_hdl_name(ds_domain_hdl_t dhdl, char *name) 378 { 379 ds_my_domain_hdl = dhdl; 380 if (ds_my_domain_name != NULL) { 381 DS_FREE(ds_my_domain_name, strlen(ds_my_domain_name)+1); 382 ds_my_domain_name = NULL; 383 } 384 if (name != NULL) { 385 ds_my_domain_name = ds_strdup(name); 386 } 387 } 388 389 void 390 ds_init() 391 { 392 ds_common_init(); 393 394 /* 395 * Create taskq for internal processing threads. This 396 * includes processing incoming request messages and 397 * sending out of band registration messages. 398 */ 399 ds_taskq = taskq_create("ds_taskq", 1, minclsyspri, 1, 400 DS_MAX_TASKQ_THR, TASKQ_PREPOPULATE | TASKQ_DYNAMIC); 401 402 /* 403 * Initialize the message log. 404 */ 405 ds_log_init(); 406 } 407 408 int 409 ds_sys_dispatch_func(void (func)(void *), void *arg) 410 { 411 return (DS_DISPATCH(func, arg) == NULL); 412 } 413 414 /* 415 * Drain event queue, if necessary. 416 */ 417 void 418 ds_sys_drain_events(ds_port_t *port) 419 { 420 _NOTE(ARGUNUSED(port)) 421 } 422 423 /* 424 * System specific port initalization. 425 */ 426 void 427 ds_sys_port_init(ds_port_t *port) 428 { 429 _NOTE(ARGUNUSED(port)) 430 } 431 432 /* 433 * System specific port teardown. 434 */ 435 void 436 ds_sys_port_fini(ds_port_t *port) 437 { 438 _NOTE(ARGUNUSED(port)) 439 } 440 441 /* 442 * System specific LDC channel initialization. 443 */ 444 void 445 ds_sys_ldc_init(ds_port_t *port) 446 { 447 int rv; 448 char ebuf[DS_EBUFSIZE]; 449 450 ASSERT(MUTEX_HELD(&port->lock)); 451 452 if ((rv = ldc_open(port->ldc.hdl)) != 0) { 453 cmn_err(CE_WARN, "ds@%lx: %s: ldc_open: %s", 454 PORTID(port), __func__, ds_errno_to_str(rv, ebuf)); 455 return; 456 } 457 458 (void) ldc_up(port->ldc.hdl); 459 460 (void) ldc_status(port->ldc.hdl, &port->ldc.state); 461 462 DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: initial LDC state 0x%x", 463 PORTID(port), __func__, port->ldc.state); 464 465 port->state = DS_PORT_LDC_INIT; 466 } 467 468 /* 469 * DS message log 470 * 471 * Locking: The message log is protected by a single mutex. This 472 * protects all fields in the log structure itself as well as 473 * everything in the entry structures on both the log and the 474 * free list. 475 */ 476 static struct log { 477 ds_log_entry_t *head; /* head of the log */ 478 ds_log_entry_t *freelist; /* head of the free list */ 479 size_t size; /* size of the log in bytes */ 480 uint32_t nentry; /* number of entries */ 481 kmutex_t lock; /* log lock */ 482 } ds_log; 483 484 /* log soft limit */ 485 uint_t ds_log_sz = DS_LOG_DEFAULT_SZ; 486 487 /* initial pool of log entry structures */ 488 static ds_log_entry_t ds_log_entry_pool[DS_LOG_NPOOL]; 489 490 /* 491 * Logging Support 492 */ 493 static void 494 ds_log_init(void) 495 { 496 ds_log_entry_t *new; 497 498 /* initialize global lock */ 499 mutex_init(&ds_log.lock, NULL, MUTEX_DRIVER, NULL); 500 501 mutex_enter(&ds_log.lock); 502 503 /* initialize the log */ 504 ds_log.head = NULL; 505 ds_log.size = 0; 506 ds_log.nentry = 0; 507 508 /* initialize the free list */ 509 for (new = ds_log_entry_pool; new < DS_LOG_POOL_END; new++) { 510 new->next = ds_log.freelist; 511 ds_log.freelist = new; 512 } 513 514 mutex_exit(&ds_log.lock); 515 516 DS_DBG_LOG(CE_NOTE, "ds_log initialized: size=%d bytes, " 517 " limit=%d bytes, ninit=%ld", ds_log_sz, DS_LOG_LIMIT, 518 DS_LOG_NPOOL); 519 } 520 521 static void 522 ds_log_fini(void) 523 { 524 ds_log_entry_t *next; 525 526 mutex_enter(&ds_log.lock); 527 528 /* clear out the log */ 529 while (ds_log.nentry > 0) 530 (void) ds_log_remove(); 531 532 /* 533 * Now all the entries are on the free list. 534 * Clear out the free list, deallocating any 535 * entry that was dynamically allocated. 536 */ 537 while (ds_log.freelist != NULL) { 538 next = ds_log.freelist->next; 539 540 if (!DS_IS_POOL_ENTRY(ds_log.freelist)) { 541 kmem_free(ds_log.freelist, sizeof (ds_log_entry_t)); 542 } 543 544 ds_log.freelist = next; 545 } 546 547 mutex_exit(&ds_log.lock); 548 549 mutex_destroy(&ds_log.lock); 550 } 551 552 static ds_log_entry_t * 553 ds_log_entry_alloc(void) 554 { 555 ds_log_entry_t *new = NULL; 556 557 ASSERT(MUTEX_HELD(&ds_log.lock)); 558 559 if (ds_log.freelist != NULL) { 560 new = ds_log.freelist; 561 ds_log.freelist = ds_log.freelist->next; 562 } 563 564 if (new == NULL) { 565 /* free list was empty */ 566 new = kmem_zalloc(sizeof (ds_log_entry_t), KM_SLEEP); 567 } 568 569 ASSERT(new); 570 571 return (new); 572 } 573 574 static void 575 ds_log_entry_free(ds_log_entry_t *entry) 576 { 577 ASSERT(MUTEX_HELD(&ds_log.lock)); 578 579 if (entry == NULL) 580 return; 581 582 if (entry->data != NULL) { 583 kmem_free(entry->data, entry->datasz); 584 entry->data = NULL; 585 } 586 587 /* place entry on the free list */ 588 entry->next = ds_log.freelist; 589 ds_log.freelist = entry; 590 } 591 592 /* 593 * Add a message to the end of the log 594 */ 595 static int 596 ds_log_add(ds_log_entry_t *new) 597 { 598 ASSERT(MUTEX_HELD(&ds_log.lock)); 599 600 if (ds_log.head == NULL) { 601 602 new->prev = new; 603 new->next = new; 604 605 ds_log.head = new; 606 } else { 607 ds_log_entry_t *head = ds_log.head; 608 ds_log_entry_t *tail = ds_log.head->prev; 609 610 new->next = head; 611 new->prev = tail; 612 tail->next = new; 613 head->prev = new; 614 } 615 616 /* increase the log size, including the metadata size */ 617 ds_log.size += DS_LOG_ENTRY_SZ(new); 618 ds_log.nentry++; 619 620 DS_DBG_LOG(CE_NOTE, "ds_log: added %ld data bytes, %ld total bytes", 621 new->datasz, DS_LOG_ENTRY_SZ(new)); 622 623 return (0); 624 } 625 626 /* 627 * Remove an entry from the head of the log 628 */ 629 static int 630 ds_log_remove(void) 631 { 632 ds_log_entry_t *head; 633 634 ASSERT(MUTEX_HELD(&ds_log.lock)); 635 636 head = ds_log.head; 637 638 /* empty list */ 639 if (head == NULL) 640 return (0); 641 642 if (head->next == ds_log.head) { 643 /* one element list */ 644 ds_log.head = NULL; 645 } else { 646 head->next->prev = head->prev; 647 head->prev->next = head->next; 648 ds_log.head = head->next; 649 } 650 651 DS_DBG_LOG(CE_NOTE, "ds_log: removed %ld data bytes, %ld total bytes", 652 head->datasz, DS_LOG_ENTRY_SZ(head)); 653 654 ds_log.size -= DS_LOG_ENTRY_SZ(head); 655 ds_log.nentry--; 656 657 ds_log_entry_free(head); 658 659 return (0); 660 } 661 662 /* 663 * Replace the data in the entry at the front of the list with then 664 * new data. This has the effect of removing the oldest entry and 665 * adding the new entry. 666 */ 667 static int 668 ds_log_replace(int32_t dest, uint8_t *msg, size_t sz) 669 { 670 ds_log_entry_t *head; 671 672 ASSERT(MUTEX_HELD(&ds_log.lock)); 673 674 head = ds_log.head; 675 676 DS_DBG_LOG(CE_NOTE, "ds_log: replaced %ld data bytes (%ld total) with " 677 " %ld data bytes (%ld total)", head->datasz, 678 DS_LOG_ENTRY_SZ(head), sz, sz + sizeof (ds_log_entry_t)); 679 680 ds_log.size -= DS_LOG_ENTRY_SZ(head); 681 682 kmem_free(head->data, head->datasz); 683 684 head->data = msg; 685 head->datasz = sz; 686 head->timestamp = ddi_get_time(); 687 head->dest = dest; 688 689 ds_log.size += DS_LOG_ENTRY_SZ(head); 690 691 ds_log.head = head->next; 692 693 return (0); 694 } 695 696 static void 697 ds_log_purge(void *arg) 698 { 699 _NOTE(ARGUNUSED(arg)) 700 701 mutex_enter(&ds_log.lock); 702 703 DS_DBG_LOG(CE_NOTE, "ds_log: purging oldest log entries"); 704 705 while ((ds_log.nentry) && (ds_log.size >= ds_log_sz)) { 706 (void) ds_log_remove(); 707 } 708 709 mutex_exit(&ds_log.lock); 710 } 711 712 int 713 ds_log_add_msg(int32_t dest, uint8_t *msg, size_t sz) 714 { 715 int rv = 0; 716 void *data; 717 718 mutex_enter(&ds_log.lock); 719 720 /* allocate a local copy of the data */ 721 data = kmem_alloc(sz, KM_SLEEP); 722 bcopy(msg, data, sz); 723 724 /* check if the log is larger than the soft limit */ 725 if ((ds_log.nentry) && ((ds_log.size + sz) >= ds_log_sz)) { 726 /* 727 * The log is larger than the soft limit. 728 * Swap the oldest entry for the newest. 729 */ 730 DS_DBG_LOG(CE_NOTE, "%s: replacing oldest entry with new entry", 731 __func__); 732 (void) ds_log_replace(dest, data, sz); 733 } else { 734 /* 735 * Still have headroom under the soft limit. 736 * Add the new entry to the log. 737 */ 738 ds_log_entry_t *new; 739 740 new = ds_log_entry_alloc(); 741 742 /* fill in message data */ 743 new->data = data; 744 new->datasz = sz; 745 new->timestamp = ddi_get_time(); 746 new->dest = dest; 747 748 rv = ds_log_add(new); 749 } 750 751 /* check if the log is larger than the hard limit */ 752 if ((ds_log.nentry > 1) && (ds_log.size >= DS_LOG_LIMIT)) { 753 /* 754 * Wakeup the thread to remove entries 755 * from the log until it is smaller than 756 * the soft limit. 757 */ 758 DS_DBG_LOG(CE_NOTE, "%s: log exceeded %d bytes, scheduling" 759 " a purge...", __func__, DS_LOG_LIMIT); 760 761 if (DS_DISPATCH(ds_log_purge, NULL) == NULL) { 762 cmn_err(CE_NOTE, "%s: purge thread failed to start", 763 __func__); 764 } 765 } 766 767 mutex_exit(&ds_log.lock); 768 769 return (rv); 770 } 771 772 int 773 ds_add_port(uint64_t port_id, uint64_t ldc_id, ds_domain_hdl_t dhdl, 774 char *dom_name, int verbose) 775 { 776 ds_port_t *newport; 777 778 /* sanity check the port id */ 779 if (port_id > DS_MAX_PORT_ID) { 780 cmn_err(CE_WARN, "%s: port ID %ld out of range", 781 __func__, port_id); 782 return (EINVAL); 783 } 784 785 DS_DBG_MD(CE_NOTE, "%s: adding port ds@%ld, LDC: 0x%lx, dhdl: 0x%lx " 786 "name: '%s'", __func__, port_id, ldc_id, dhdl, 787 dom_name == NULL ? "NULL" : dom_name); 788 789 /* get the port structure from the array of ports */ 790 newport = &ds_ports[port_id]; 791 792 /* check for a duplicate port in the MD */ 793 if (newport->state != DS_PORT_FREE) { 794 if (verbose) { 795 cmn_err(CE_WARN, "ds@%lx: %s: port already exists", 796 port_id, __func__); 797 } 798 if (newport->domain_hdl == DS_DHDL_INVALID) { 799 newport->domain_hdl = dhdl; 800 } 801 if (newport->domain_name == NULL && dom_name != NULL) { 802 newport->domain_name = ds_strdup(dom_name); 803 } 804 return (EBUSY); 805 } 806 807 /* initialize the port */ 808 newport->id = port_id; 809 newport->ldc.id = ldc_id; 810 newport->domain_hdl = dhdl; 811 if (dom_name) { 812 newport->domain_name = ds_strdup(dom_name); 813 } else 814 newport->domain_name = NULL; 815 ds_port_common_init(newport); 816 817 return (0); 818 } 819 820 /* ARGSUSED */ 821 int 822 ds_remove_port(uint64_t port_id, int is_fini) 823 { 824 ds_port_t *port; 825 826 if (port_id >= DS_MAX_PORTS || !DS_PORT_IN_SET(ds_allports, port_id)) { 827 DS_DBG_MD(CE_NOTE, "%s: invalid port %lx", __func__, 828 port_id); 829 return (EINVAL); 830 } 831 832 DS_DBG_MD(CE_NOTE, "%s: removing port ds@%lx", __func__, port_id); 833 834 port = &ds_ports[port_id]; 835 836 mutex_enter(&port->lock); 837 838 if (port->state >= DS_PORT_LDC_INIT) { 839 /* shut down the LDC for this port */ 840 (void) ds_ldc_fini(port); 841 } 842 843 if (port->domain_name) { 844 DS_FREE(port->domain_name, strlen(port->domain_name) + 1); 845 port->domain_name = NULL; 846 } 847 port->domain_hdl = DS_DHDL_INVALID; 848 849 /* clean up the port structure */ 850 ds_port_common_fini(port); 851 852 mutex_exit(&port->lock); 853 return (0); 854 } 855 856 /* 857 * Interface for ds_service_lookup in lds driver. 858 */ 859 int 860 ds_service_lookup(ds_svc_hdl_t hdl, char **servicep, uint_t *is_client) 861 { 862 ds_svc_t *svc; 863 864 mutex_enter(&ds_svcs.lock); 865 if ((svc = ds_get_svc(hdl)) == NULL) { 866 mutex_exit(&ds_svcs.lock); 867 DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__, 868 (u_longlong_t)hdl); 869 return (ENXIO); 870 } 871 *servicep = svc->cap.svc_id; 872 *is_client = svc->flags & DSSF_ISCLIENT; 873 mutex_exit(&ds_svcs.lock); 874 return (0); 875 } 876 877 /* 878 * Interface for ds_domain_lookup in lds driver. 879 */ 880 int 881 ds_domain_lookup(ds_svc_hdl_t hdl, ds_domain_hdl_t *dhdlp) 882 { 883 ds_svc_t *svc; 884 885 mutex_enter(&ds_svcs.lock); 886 if ((svc = ds_get_svc(hdl)) == NULL) { 887 mutex_exit(&ds_svcs.lock); 888 DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__, 889 (u_longlong_t)hdl); 890 return (ENXIO); 891 } 892 if (svc->port == NULL) 893 *dhdlp = ds_my_domain_hdl; 894 else 895 *dhdlp = svc->port->domain_hdl; 896 mutex_exit(&ds_svcs.lock); 897 return (0); 898 } 899 900 /* 901 * Interface for ds_hdl_isready in lds driver. 902 */ 903 int 904 ds_hdl_isready(ds_svc_hdl_t hdl, uint_t *is_ready) 905 { 906 ds_svc_t *svc; 907 908 mutex_enter(&ds_svcs.lock); 909 if ((svc = ds_get_svc(hdl)) == NULL) { 910 mutex_exit(&ds_svcs.lock); 911 DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__, 912 (u_longlong_t)hdl); 913 return (ENXIO); 914 } 915 *is_ready = (svc->state == DS_SVC_ACTIVE); 916 mutex_exit(&ds_svcs.lock); 917 return (0); 918 } 919 920 /* 921 * Interface for ds_dom_name_to_hdl in lds driver. 922 */ 923 int 924 ds_dom_name_to_hdl(char *domain_name, ds_domain_hdl_t *dhdlp) 925 { 926 int i; 927 ds_port_t *port; 928 929 if (domain_name == NULL) { 930 return (ENXIO); 931 } 932 if (ds_my_domain_name != NULL && 933 strcmp(ds_my_domain_name, domain_name) == 0) { 934 *dhdlp = ds_my_domain_hdl; 935 return (0); 936 } 937 for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) { 938 if (port->state != DS_PORT_FREE && 939 port->domain_name != NULL && 940 strcmp(port->domain_name, domain_name) == 0) { 941 *dhdlp = port->domain_hdl; 942 return (0); 943 } 944 } 945 return (ENXIO); 946 } 947 948 /* 949 * Interface for ds_dom_hdl_to_name in lds driver. 950 */ 951 int 952 ds_dom_hdl_to_name(ds_domain_hdl_t dhdl, char **domain_namep) 953 { 954 int i; 955 ds_port_t *port; 956 957 if (dhdl == ds_my_domain_hdl) { 958 if (ds_my_domain_name != NULL) { 959 *domain_namep = ds_my_domain_name; 960 return (0); 961 } 962 return (ENXIO); 963 } 964 for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) { 965 if (port->state != DS_PORT_FREE && 966 port->domain_hdl == dhdl) { 967 *domain_namep = port->domain_name; 968 return (0); 969 } 970 } 971 return (ENXIO); 972 } 973 974 /* 975 * Unregister all handles related to device open instance. 976 */ 977 void 978 ds_unreg_all(int instance) 979 { 980 int idx; 981 ds_svc_t *svc; 982 ds_svc_hdl_t hdl; 983 984 DS_DBG_USR(CE_NOTE, "%s: entered", __func__); 985 986 /* walk every table entry */ 987 mutex_enter(&ds_svcs.lock); 988 for (idx = 0; idx < ds_svcs.maxsvcs; idx++) { 989 svc = ds_svcs.tbl[idx]; 990 if (DS_SVC_ISFREE(svc)) 991 continue; 992 if ((svc->flags & DSSF_ISUSER) != 0 && svc->drvi == instance) { 993 hdl = svc->hdl; 994 mutex_exit(&ds_svcs.lock); 995 (void) ds_unreg_hdl(hdl); 996 mutex_enter(&ds_svcs.lock); 997 DS_DBG_USR(CE_NOTE, "%s: ds_unreg_hdl(0x%llx):", 998 __func__, (u_longlong_t)hdl); 999 } 1000 } 1001 mutex_exit(&ds_svcs.lock); 1002 } 1003 1004 /* 1005 * Special callbacks to allow the lds module revision-independent access 1006 * to service structure data in the callback routines. This assumes that 1007 * we put a special "cookie" in the arg argument passed to those 1008 * routines (for now, a ptr to the svc structure, but it could be a svc 1009 * table index or something that we could get back to the svc table entry). 1010 */ 1011 void 1012 ds_cbarg_get_hdl(ds_cb_arg_t arg, ds_svc_hdl_t *hdlp) 1013 { 1014 ds_svc_t *svc = (ds_svc_t *)arg; 1015 1016 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1017 *hdlp = svc->hdl; 1018 } 1019 1020 void 1021 ds_cbarg_get_flags(ds_cb_arg_t arg, uint32_t *flagsp) 1022 { 1023 ds_svc_t *svc = (ds_svc_t *)arg; 1024 1025 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1026 *flagsp = svc->flags; 1027 } 1028 1029 void 1030 ds_cbarg_get_drv_info(ds_cb_arg_t arg, int *drvip) 1031 { 1032 ds_svc_t *svc = (ds_svc_t *)arg; 1033 1034 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1035 *drvip = svc->drvi; 1036 } 1037 1038 void 1039 ds_cbarg_get_drv_per_svc_ptr(ds_cb_arg_t arg, void **dpspp) 1040 { 1041 ds_svc_t *svc = (ds_svc_t *)arg; 1042 1043 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1044 *dpspp = svc->drv_psp; 1045 } 1046 1047 void 1048 ds_cbarg_get_domain(ds_cb_arg_t arg, ds_domain_hdl_t *dhdlp) 1049 { 1050 ds_svc_t *svc = (ds_svc_t *)arg; 1051 1052 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1053 if (svc->port == NULL) 1054 *dhdlp = ds_my_domain_hdl; 1055 else 1056 *dhdlp = svc->port->domain_hdl; 1057 } 1058 1059 void 1060 ds_cbarg_get_service_id(ds_cb_arg_t arg, char **servicep) 1061 { 1062 ds_svc_t *svc = (ds_svc_t *)arg; 1063 1064 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1065 *servicep = svc->cap.svc_id; 1066 } 1067 1068 void 1069 ds_cbarg_set_drv_per_svc_ptr(ds_cb_arg_t arg, void *dpsp) 1070 { 1071 ds_svc_t *svc = (ds_svc_t *)arg; 1072 1073 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1074 svc->drv_psp = dpsp; 1075 } 1076 1077 void 1078 ds_cbarg_set_cookie(ds_svc_t *svc) 1079 { 1080 svc->ops.cb_arg = (ds_cb_arg_t)(svc); 1081 } 1082 1083 int 1084 ds_hdl_get_cbarg(ds_svc_hdl_t hdl, ds_cb_arg_t *cbargp) 1085 { 1086 ds_svc_t *svc; 1087 1088 mutex_enter(&ds_svcs.lock); 1089 if ((svc = ds_get_svc(hdl)) != NULL && 1090 (svc->flags & DSSF_ISUSER) != 0) { 1091 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1092 *cbargp = svc->ops.cb_arg; 1093 mutex_exit(&ds_svcs.lock); 1094 return (0); 1095 } 1096 mutex_exit(&ds_svcs.lock); 1097 return (ENXIO); 1098 } 1099 1100 int 1101 ds_is_my_hdl(ds_svc_hdl_t hdl, int instance) 1102 { 1103 ds_svc_t *svc; 1104 int rv = 0; 1105 1106 mutex_enter(&ds_svcs.lock); 1107 if ((svc = ds_get_svc(hdl)) == NULL) { 1108 DS_DBG_USR(CE_NOTE, "%s: invalid hdl: 0x%llx\n", __func__, 1109 (u_longlong_t)hdl); 1110 rv = ENXIO; 1111 } else if (instance == DS_INVALID_INSTANCE) { 1112 if ((svc->flags & DSSF_ISUSER) != 0) { 1113 DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n", 1114 __func__, (u_longlong_t)hdl); 1115 rv = EACCES; 1116 } 1117 } else if ((svc->flags & DSSF_ISUSER) == 0 || svc->drvi != instance) { 1118 DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n", __func__, 1119 (u_longlong_t)hdl); 1120 rv = EACCES; 1121 } 1122 mutex_exit(&ds_svcs.lock); 1123 return (rv); 1124 } 1125