|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * The contents of this file constitute Original Code as defined in and ! 7: * are subject to the Apple Public Source License Version 1.1 (the ! 8: * "License"). You may not use this file except in compliance with the ! 9: * License. Please obtain a copy of the License at ! 10: * http://www.apple.com/publicsource and read it before using this file. ! 11: * ! 12: * This Original Code and all software distributed under the License are ! 13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 17: * License for the specific language governing rights and limitations ! 18: * under the License. ! 19: * ! 20: * @APPLE_LICENSE_HEADER_END@ ! 21: */ ! 22: /* ! 23: * @OSF_FREE_COPYRIGHT@ ! 24: */ ! 25: /* ! 26: * Mach Operating System ! 27: * Copyright (c) 1991,1990,1989 Carnegie Mellon University ! 28: * All Rights Reserved. ! 29: * ! 30: * Permission to use, copy, modify and distribute this software and its ! 31: * documentation is hereby granted, provided that both the copyright ! 32: * notice and this permission notice appear in all copies of the ! 33: * software, derivative works or modified versions, and any portions ! 34: * thereof, and that both notices appear in supporting documentation. ! 35: * ! 36: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" ! 37: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR ! 38: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. ! 39: * ! 40: * Carnegie Mellon requests users of this software to return to ! 41: * ! 42: * Software Distribution Coordinator or [email protected] ! 43: * School of Computer Science ! 44: * Carnegie Mellon University ! 45: * Pittsburgh PA 15213-3890 ! 46: * ! 47: * any improvements or extensions that they make and grant Carnegie Mellon ! 48: * the rights to redistribute these changes. ! 49: */ ! 50: /* ! 51: */ ! 52: /* ! 53: * File: ipc/ipc_port.c ! 54: * Author: Rich Draves ! 55: * Date: 1989 ! 56: * ! 57: * Functions to manipulate IPC ports. ! 58: */ ! 59: ! 60: #include <dipc.h> ! 61: #include <norma_vm.h> ! 62: #include <mach_kdb.h> ! 63: #include <zone_debug.h> ! 64: #include <mach_assert.h> ! 65: ! 66: #include <mach/port.h> ! 67: #include <mach/kern_return.h> ! 68: #include <kern/lock.h> ! 69: #include <kern/ipc_kobject.h> ! 70: #include <kern/ipc_subsystem.h> ! 71: #include <kern/thread.h> ! 72: #include <kern/thread_pool.h> ! 73: #include <kern/misc_protos.h> ! 74: #include <kern/wait_queue.h> ! 75: #include <ipc/ipc_entry.h> ! 76: #include <ipc/ipc_space.h> ! 77: #include <ipc/ipc_object.h> ! 78: #include <ipc/ipc_port.h> ! 79: #include <ipc/ipc_pset.h> ! 80: #include <ipc/ipc_mqueue.h> ! 81: #include <ipc/ipc_notify.h> ! 82: #include <ipc/ipc_print.h> ! 83: #include <ipc/ipc_table.h> ! 84: ! 85: #if MACH_KDB ! 86: #include <machine/db_machdep.h> ! 87: #include <ddb/db_command.h> ! 88: #include <ddb/db_expr.h> ! 89: #endif /* MACH_KDB */ ! 90: ! 91: #include <string.h> ! 92: ! 93: decl_mutex_data(, ipc_port_multiple_lock_data) ! 94: decl_mutex_data(, ipc_port_timestamp_lock_data) ! 95: ipc_port_timestamp_t ipc_port_timestamp_data; ! 96: ! 97: #if MACH_ASSERT ! 98: void ipc_port_init_debug( ! 99: ipc_port_t port); ! 100: #endif /* MACH_ASSERT */ ! 101: ! 102: #if MACH_KDB && ZONE_DEBUG ! 103: /* Forwards */ ! 104: void print_type_ports(unsigned, unsigned); ! 105: void print_ports(void); ! 106: #endif /* MACH_KDB && ZONE_DEBUG */ ! 107: ! 108: /* ! 109: * Routine: ipc_port_timestamp ! 110: * Purpose: ! 111: * Retrieve a timestamp value. ! 112: */ ! 113: ! 114: ipc_port_timestamp_t ! 115: ipc_port_timestamp(void) ! 116: { ! 117: ipc_port_timestamp_t timestamp; ! 118: ! 119: ipc_port_timestamp_lock(); ! 120: timestamp = ipc_port_timestamp_data++; ! 121: ipc_port_timestamp_unlock(); ! 122: ! 123: return timestamp; ! 124: } ! 125: ! 126: /* ! 127: * Routine: ipc_port_dnrequest ! 128: * Purpose: ! 129: * Try to allocate a dead-name request slot. ! 130: * If successful, returns the request index. ! 131: * Otherwise returns zero. ! 132: * Conditions: ! 133: * The port is locked and active. ! 134: * Returns: ! 135: * KERN_SUCCESS A request index was found. ! 136: * KERN_NO_SPACE No index allocated. ! 137: */ ! 138: ! 139: kern_return_t ! 140: ipc_port_dnrequest( ! 141: ipc_port_t port, ! 142: mach_port_name_t name, ! 143: ipc_port_t soright, ! 144: ipc_port_request_index_t *indexp) ! 145: { ! 146: ipc_port_request_t ipr, table; ! 147: ipc_port_request_index_t index; ! 148: ! 149: assert(ip_active(port)); ! 150: assert(name != MACH_PORT_NULL); ! 151: assert(soright != IP_NULL); ! 152: ! 153: table = port->ip_dnrequests; ! 154: if (table == IPR_NULL) ! 155: return KERN_NO_SPACE; ! 156: ! 157: index = table->ipr_next; ! 158: if (index == 0) ! 159: return KERN_NO_SPACE; ! 160: ! 161: ipr = &table[index]; ! 162: assert(ipr->ipr_name == MACH_PORT_NULL); ! 163: ! 164: table->ipr_next = ipr->ipr_next; ! 165: ipr->ipr_name = name; ! 166: ipr->ipr_soright = soright; ! 167: ! 168: *indexp = index; ! 169: return KERN_SUCCESS; ! 170: } ! 171: ! 172: /* ! 173: * Routine: ipc_port_dngrow ! 174: * Purpose: ! 175: * Grow a port's table of dead-name requests. ! 176: * Conditions: ! 177: * The port must be locked and active. ! 178: * Nothing else locked; will allocate memory. ! 179: * Upon return the port is unlocked. ! 180: * Returns: ! 181: * KERN_SUCCESS Grew the table. ! 182: * KERN_SUCCESS Somebody else grew the table. ! 183: * KERN_SUCCESS The port died. ! 184: * KERN_RESOURCE_SHORTAGE Couldn't allocate new table. ! 185: * KERN_NO_SPACE Couldn't grow to desired size ! 186: */ ! 187: ! 188: kern_return_t ! 189: ipc_port_dngrow( ! 190: ipc_port_t port, ! 191: int target_size) ! 192: { ! 193: ipc_table_size_t its; ! 194: ipc_port_request_t otable, ntable; ! 195: ! 196: assert(ip_active(port)); ! 197: ! 198: otable = port->ip_dnrequests; ! 199: if (otable == IPR_NULL) ! 200: its = &ipc_table_dnrequests[0]; ! 201: else ! 202: its = otable->ipr_size + 1; ! 203: ! 204: if (target_size != ITS_SIZE_NONE) { ! 205: if ((otable != IPR_NULL) && ! 206: (target_size <= otable->ipr_size->its_size)) { ! 207: ip_unlock(port); ! 208: return KERN_SUCCESS; ! 209: } ! 210: while ((its->its_size) && (its->its_size < target_size)) { ! 211: its++; ! 212: } ! 213: if (its->its_size == 0) { ! 214: ip_unlock(port); ! 215: return KERN_NO_SPACE; ! 216: } ! 217: } ! 218: ! 219: ip_reference(port); ! 220: ip_unlock(port); ! 221: ! 222: if ((its->its_size == 0) || ! 223: ((ntable = it_dnrequests_alloc(its)) == IPR_NULL)) { ! 224: ipc_port_release(port); ! 225: return KERN_RESOURCE_SHORTAGE; ! 226: } ! 227: ! 228: ip_lock(port); ! 229: ip_release(port); ! 230: ! 231: /* ! 232: * Check that port is still active and that nobody else ! 233: * has slipped in and grown the table on us. Note that ! 234: * just checking port->ip_dnrequests == otable isn't ! 235: * sufficient; must check ipr_size. ! 236: */ ! 237: ! 238: if (ip_active(port) && ! 239: (port->ip_dnrequests == otable) && ! 240: ((otable == IPR_NULL) || (otable->ipr_size+1 == its))) { ! 241: ipc_table_size_t oits; ! 242: ipc_table_elems_t osize, nsize; ! 243: ipc_port_request_index_t free, i; ! 244: ! 245: /* copy old table to new table */ ! 246: ! 247: if (otable != IPR_NULL) { ! 248: oits = otable->ipr_size; ! 249: osize = oits->its_size; ! 250: free = otable->ipr_next; ! 251: ! 252: (void) memcpy((void *)(ntable + 1), ! 253: (const void *)(otable + 1), ! 254: (osize - 1) * sizeof(struct ipc_port_request)); ! 255: } else { ! 256: osize = 1; ! 257: free = 0; ! 258: } ! 259: ! 260: nsize = its->its_size; ! 261: assert(nsize > osize); ! 262: ! 263: /* add new elements to the new table's free list */ ! 264: ! 265: for (i = osize; i < nsize; i++) { ! 266: ipc_port_request_t ipr = &ntable[i]; ! 267: ! 268: ipr->ipr_name = MACH_PORT_NULL; ! 269: ipr->ipr_next = free; ! 270: free = i; ! 271: } ! 272: ! 273: ntable->ipr_next = free; ! 274: ntable->ipr_size = its; ! 275: port->ip_dnrequests = ntable; ! 276: ip_unlock(port); ! 277: ! 278: if (otable != IPR_NULL) { ! 279: it_dnrequests_free(oits, otable); ! 280: } ! 281: } else { ! 282: ip_check_unlock(port); ! 283: it_dnrequests_free(its, ntable); ! 284: } ! 285: ! 286: return KERN_SUCCESS; ! 287: } ! 288: ! 289: /* ! 290: * Routine: ipc_port_dncancel ! 291: * Purpose: ! 292: * Cancel a dead-name request and return the send-once right. ! 293: * Conditions: ! 294: * The port must locked and active. ! 295: */ ! 296: ! 297: ipc_port_t ! 298: ipc_port_dncancel( ! 299: ipc_port_t port, ! 300: mach_port_name_t name, ! 301: ipc_port_request_index_t index) ! 302: { ! 303: ipc_port_request_t ipr, table; ! 304: ipc_port_t dnrequest; ! 305: ! 306: assert(ip_active(port)); ! 307: assert(name != MACH_PORT_NULL); ! 308: assert(index != 0); ! 309: ! 310: table = port->ip_dnrequests; ! 311: assert(table != IPR_NULL); ! 312: ! 313: ipr = &table[index]; ! 314: dnrequest = ipr->ipr_soright; ! 315: assert(ipr->ipr_name == name); ! 316: ! 317: /* return ipr to the free list inside the table */ ! 318: ! 319: ipr->ipr_name = MACH_PORT_NULL; ! 320: ipr->ipr_next = table->ipr_next; ! 321: table->ipr_next = index; ! 322: ! 323: return dnrequest; ! 324: } ! 325: ! 326: /* ! 327: * Routine: ipc_port_pdrequest ! 328: * Purpose: ! 329: * Make a port-deleted request, returning the ! 330: * previously registered send-once right. ! 331: * Just cancels the previous request if notify is IP_NULL. ! 332: * Conditions: ! 333: * The port is locked and active. It is unlocked. ! 334: * Consumes a ref for notify (if non-null), and ! 335: * returns previous with a ref (if non-null). ! 336: */ ! 337: ! 338: void ! 339: ipc_port_pdrequest( ! 340: ipc_port_t port, ! 341: ipc_port_t notify, ! 342: ipc_port_t *previousp) ! 343: { ! 344: ipc_port_t previous; ! 345: ! 346: assert(ip_active(port)); ! 347: ! 348: previous = port->ip_pdrequest; ! 349: port->ip_pdrequest = notify; ! 350: ip_unlock(port); ! 351: ! 352: *previousp = previous; ! 353: } ! 354: ! 355: /* ! 356: * Routine: ipc_port_nsrequest ! 357: * Purpose: ! 358: * Make a no-senders request, returning the ! 359: * previously registered send-once right. ! 360: * Just cancels the previous request if notify is IP_NULL. ! 361: * Conditions: ! 362: * The port is locked and active. It is unlocked. ! 363: * Consumes a ref for notify (if non-null), and ! 364: * returns previous with a ref (if non-null). ! 365: */ ! 366: ! 367: void ! 368: ipc_port_nsrequest( ! 369: ipc_port_t port, ! 370: mach_port_mscount_t sync, ! 371: ipc_port_t notify, ! 372: ipc_port_t *previousp) ! 373: { ! 374: ipc_port_t previous; ! 375: mach_port_mscount_t mscount; ! 376: ! 377: assert(ip_active(port)); ! 378: ! 379: previous = port->ip_nsrequest; ! 380: mscount = port->ip_mscount; ! 381: ! 382: if ((port->ip_srights == 0) && (sync <= mscount) && ! 383: (notify != IP_NULL)) { ! 384: port->ip_nsrequest = IP_NULL; ! 385: ip_unlock(port); ! 386: ipc_notify_no_senders(notify, mscount); ! 387: } else { ! 388: port->ip_nsrequest = notify; ! 389: ip_unlock(port); ! 390: } ! 391: ! 392: *previousp = previous; ! 393: } ! 394: ! 395: ! 396: /* ! 397: * Routine: ipc_port_clear_receiver ! 398: * Purpose: ! 399: * Prepares a receive right for transmission/destruction. ! 400: * Conditions: ! 401: * The port is locked and active. ! 402: */ ! 403: ! 404: void ! 405: ipc_port_clear_receiver( ! 406: ipc_port_t port) ! 407: { ! 408: assert(ip_active(port)); ! 409: ! 410: /* ! 411: * pull ourselves from any sets. ! 412: */ ! 413: if (port->ip_pset_count != 0) { ! 414: ipc_pset_remove_all(port); ! 415: port->ip_pset_count = 0; ! 416: } ! 417: ! 418: /* ! 419: * Send anyone waiting on the port's queue directly away. ! 420: * Also clear the mscount and seqno. ! 421: */ ! 422: imq_lock(&port->ip_messages); ! 423: ipc_mqueue_changed(&port->ip_messages); ! 424: ipc_port_set_mscount(port, 0); ! 425: port->ip_messages.imq_seqno = 0; ! 426: imq_unlock(&port->ip_messages); ! 427: } ! 428: ! 429: /* ! 430: * Routine: ipc_port_init ! 431: * Purpose: ! 432: * Initializes a newly-allocated port. ! 433: * Doesn't touch the ip_object fields. ! 434: */ ! 435: ! 436: void ! 437: ipc_port_init( ! 438: ipc_port_t port, ! 439: ipc_space_t space, ! 440: mach_port_name_t name) ! 441: { ! 442: /* port->ip_kobject doesn't have to be initialized */ ! 443: ! 444: port->ip_receiver = space; ! 445: port->ip_receiver_name = name; ! 446: ! 447: port->ip_mscount = 0; ! 448: port->ip_srights = 0; ! 449: port->ip_sorights = 0; ! 450: ! 451: port->ip_nsrequest = IP_NULL; ! 452: port->ip_pdrequest = IP_NULL; ! 453: port->ip_dnrequests = IPR_NULL; ! 454: ! 455: port->ip_pset_count = 0; ! 456: ! 457: thread_pool_init(&port->ip_thread_pool); ! 458: ! 459: port->ip_subsystem = RPC_SUBSYSTEM_NULL; ! 460: ! 461: port->ip_flags = 0; ! 462: ! 463: /* ! 464: * Turn no more senders detection on ! 465: * for all ports. Eventually, this ! 466: * default will go away, and nms ! 467: * detection will be enabled depending ! 468: * on how the port is allocated. XXX ! 469: */ ! 470: IP_SET_NMS(port); ! 471: ! 472: #if MACH_ASSERT ! 473: ipc_port_init_debug(port); ! 474: #endif /* MACH_ASSERT */ ! 475: ! 476: ipc_mqueue_init(&port->ip_messages, FALSE /* set */); ! 477: } ! 478: ! 479: /* ! 480: * Routine: ipc_port_alloc ! 481: * Purpose: ! 482: * Allocate a port. ! 483: * Conditions: ! 484: * Nothing locked. If successful, the port is returned ! 485: * locked. (The caller doesn't have a reference.) ! 486: * Returns: ! 487: * KERN_SUCCESS The port is allocated. ! 488: * KERN_INVALID_TASK The space is dead. ! 489: * KERN_NO_SPACE No room for an entry in the space. ! 490: * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. ! 491: */ ! 492: ! 493: kern_return_t ! 494: ipc_port_alloc( ! 495: ipc_space_t space, ! 496: mach_port_name_t *namep, ! 497: ipc_port_t *portp) ! 498: { ! 499: ipc_port_t port; ! 500: mach_port_name_t name; ! 501: kern_return_t kr; ! 502: ! 503: kr = ipc_object_alloc(space, IOT_PORT, ! 504: MACH_PORT_TYPE_RECEIVE, 0, ! 505: &name, (ipc_object_t *) &port); ! 506: if (kr != KERN_SUCCESS) ! 507: return kr; ! 508: ! 509: /* port is locked */ ! 510: ! 511: ipc_port_init(port, space, name); ! 512: ! 513: *namep = name; ! 514: *portp = port; ! 515: ! 516: return KERN_SUCCESS; ! 517: } ! 518: ! 519: /* ! 520: * Routine: ipc_port_alloc_name ! 521: * Purpose: ! 522: * Allocate a port, with a specific name. ! 523: * Conditions: ! 524: * Nothing locked. If successful, the port is returned ! 525: * locked. (The caller doesn't have a reference.) ! 526: * Returns: ! 527: * KERN_SUCCESS The port is allocated. ! 528: * KERN_INVALID_TASK The space is dead. ! 529: * KERN_NAME_EXISTS The name already denotes a right. ! 530: * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. ! 531: */ ! 532: ! 533: kern_return_t ! 534: ipc_port_alloc_name( ! 535: ipc_space_t space, ! 536: mach_port_name_t name, ! 537: ipc_port_t *portp) ! 538: { ! 539: ipc_port_t port; ! 540: kern_return_t kr; ! 541: ! 542: kr = ipc_object_alloc_name(space, IOT_PORT, ! 543: MACH_PORT_TYPE_RECEIVE, 0, ! 544: name, (ipc_object_t *) &port); ! 545: if (kr != KERN_SUCCESS) ! 546: return kr; ! 547: ! 548: /* port is locked */ ! 549: ! 550: ipc_port_init(port, space, name); ! 551: ! 552: *portp = port; ! 553: ! 554: return KERN_SUCCESS; ! 555: } ! 556: ! 557: /* ! 558: * Generate dead name notifications. Called from ipc_port_destroy. ! 559: * Port is unlocked but still has reference(s); ! 560: * dnrequests was taken from port while the port ! 561: * was locked but the port now has port->ip_dnrequests set to IPR_NULL. ! 562: */ ! 563: void ! 564: ipc_port_dnnotify( ! 565: ipc_port_t port, ! 566: ipc_port_request_t dnrequests) ! 567: { ! 568: ipc_table_size_t its = dnrequests->ipr_size; ! 569: ipc_table_elems_t size = its->its_size; ! 570: ipc_port_request_index_t index; ! 571: ! 572: for (index = 1; index < size; index++) { ! 573: ipc_port_request_t ipr = &dnrequests[index]; ! 574: mach_port_name_t name = ipr->ipr_name; ! 575: ipc_port_t soright; ! 576: ! 577: if (name == MACH_PORT_NULL) ! 578: continue; ! 579: ! 580: soright = ipr->ipr_soright; ! 581: assert(soright != IP_NULL); ! 582: ! 583: ipc_notify_dead_name(soright, name); ! 584: } ! 585: ! 586: it_dnrequests_free(its, dnrequests); ! 587: } ! 588: ! 589: /* ! 590: * Routine: ipc_port_destroy ! 591: * Purpose: ! 592: * Destroys a port. Cleans up queued messages. ! 593: * ! 594: * If the port has a backup, it doesn't get destroyed, ! 595: * but is sent in a port-destroyed notification to the backup. ! 596: * Conditions: ! 597: * The port is locked and alive; nothing else locked. ! 598: * The caller has a reference, which is consumed. ! 599: * Afterwards, the port is unlocked and dead. ! 600: */ ! 601: ! 602: void ! 603: ipc_port_destroy( ! 604: ipc_port_t port) ! 605: { ! 606: ipc_port_t pdrequest, nsrequest; ! 607: ipc_mqueue_t mqueue; ! 608: ipc_kmsg_queue_t kmqueue; ! 609: ipc_kmsg_t kmsg; ! 610: ipc_port_request_t dnrequests; ! 611: thread_pool_t thread_pool; ! 612: ! 613: assert(ip_active(port)); ! 614: /* port->ip_receiver_name is garbage */ ! 615: /* port->ip_receiver/port->ip_destination is garbage */ ! 616: assert(port->ip_pset_count == 0); ! 617: assert(port->ip_mscount == 0); ! 618: ! 619: /* first check for a backup port */ ! 620: ! 621: pdrequest = port->ip_pdrequest; ! 622: if (pdrequest != IP_NULL) { ! 623: /* we assume the ref for pdrequest */ ! 624: port->ip_pdrequest = IP_NULL; ! 625: ! 626: /* make port be in limbo */ ! 627: port->ip_receiver_name = MACH_PORT_NULL; ! 628: port->ip_destination = IP_NULL; ! 629: ip_unlock(port); ! 630: ! 631: if (!ipc_port_check_circularity(port, pdrequest)) { ! 632: /* consumes our refs for port and pdrequest */ ! 633: ipc_notify_port_destroyed(pdrequest, port); ! 634: return; ! 635: } else { ! 636: /* consume pdrequest and destroy port */ ! 637: ipc_port_release_sonce(pdrequest); ! 638: } ! 639: ! 640: ip_lock(port); ! 641: assert(ip_active(port)); ! 642: assert(port->ip_pset_count == 0); ! 643: assert(port->ip_mscount == 0); ! 644: assert(port->ip_pdrequest == IP_NULL); ! 645: assert(port->ip_receiver_name == MACH_PORT_NULL); ! 646: assert(port->ip_destination == IP_NULL); ! 647: ! 648: /* fall through and destroy the port */ ! 649: } ! 650: ! 651: /* once port is dead, we don't need to keep it locked */ ! 652: ! 653: port->ip_object.io_bits &= ~IO_BITS_ACTIVE; ! 654: port->ip_timestamp = ipc_port_timestamp(); ! 655: ! 656: /* save for later */ ! 657: dnrequests = port->ip_dnrequests; ! 658: port->ip_dnrequests = IPR_NULL; ! 659: ip_unlock(port); ! 660: ! 661: /* wakeup any threads waiting on this pool port for an activation */ ! 662: if ((thread_pool = &port->ip_thread_pool) != THREAD_POOL_NULL) ! 663: thread_pool_wakeup(thread_pool); ! 664: ! 665: /* throw away no-senders request */ ! 666: ! 667: nsrequest = port->ip_nsrequest; ! 668: if (nsrequest != IP_NULL) ! 669: ipc_notify_send_once(nsrequest); /* consumes ref */ ! 670: ! 671: /* destroy any queued messages */ ! 672: mqueue = &port->ip_messages; ! 673: ipc_mqueue_destroy(mqueue); ! 674: ! 675: /* generate dead-name notifications */ ! 676: if (dnrequests != IPR_NULL) { ! 677: ipc_port_dnnotify(port, dnrequests); ! 678: } ! 679: ! 680: ipc_kobject_destroy(port); ! 681: ! 682: if (port->ip_subsystem != RPC_SUBSYSTEM_NULL) { ! 683: subsystem_deallocate((subsystem_t) port->ip_kobject); ! 684: } ! 685: ! 686: /* XXXX Perhaps should verify that ip_thread_pool is empty! */ ! 687: ! 688: ipc_port_release(port); /* consume caller's ref */ ! 689: } ! 690: ! 691: /* ! 692: * Routine: ipc_port_check_circularity ! 693: * Purpose: ! 694: * Check if queueing "port" in a message for "dest" ! 695: * would create a circular group of ports and messages. ! 696: * ! 697: * If no circularity (FALSE returned), then "port" ! 698: * is changed from "in limbo" to "in transit". ! 699: * ! 700: * That is, we want to set port->ip_destination == dest, ! 701: * but guaranteeing that this doesn't create a circle ! 702: * port->ip_destination->ip_destination->... == port ! 703: * Conditions: ! 704: * No ports locked. References held for "port" and "dest". ! 705: */ ! 706: ! 707: boolean_t ! 708: ipc_port_check_circularity( ! 709: ipc_port_t port, ! 710: ipc_port_t dest) ! 711: { ! 712: ipc_port_t base; ! 713: ! 714: assert(port != IP_NULL); ! 715: assert(dest != IP_NULL); ! 716: ! 717: if (port == dest) ! 718: return TRUE; ! 719: base = dest; ! 720: ! 721: /* ! 722: * First try a quick check that can run in parallel. ! 723: * No circularity if dest is not in transit. ! 724: */ ! 725: ! 726: ip_lock(port); ! 727: if (ip_lock_try(dest)) { ! 728: if (!ip_active(dest) || ! 729: (dest->ip_receiver_name != MACH_PORT_NULL) || ! 730: (dest->ip_destination == IP_NULL)) ! 731: goto not_circular; ! 732: ! 733: /* dest is in transit; further checking necessary */ ! 734: ! 735: ip_unlock(dest); ! 736: } ! 737: ip_unlock(port); ! 738: ! 739: ipc_port_multiple_lock(); /* massive serialization */ ! 740: ! 741: /* ! 742: * Search for the end of the chain (a port not in transit), ! 743: * acquiring locks along the way. ! 744: */ ! 745: ! 746: for (;;) { ! 747: ip_lock(base); ! 748: ! 749: if (!ip_active(base) || ! 750: (base->ip_receiver_name != MACH_PORT_NULL) || ! 751: (base->ip_destination == IP_NULL)) ! 752: break; ! 753: ! 754: base = base->ip_destination; ! 755: } ! 756: ! 757: /* all ports in chain from dest to base, inclusive, are locked */ ! 758: ! 759: if (port == base) { ! 760: /* circularity detected! */ ! 761: ! 762: ipc_port_multiple_unlock(); ! 763: ! 764: /* port (== base) is in limbo */ ! 765: ! 766: assert(ip_active(port)); ! 767: assert(port->ip_receiver_name == MACH_PORT_NULL); ! 768: assert(port->ip_destination == IP_NULL); ! 769: ! 770: while (dest != IP_NULL) { ! 771: ipc_port_t next; ! 772: ! 773: /* dest is in transit or in limbo */ ! 774: ! 775: assert(ip_active(dest)); ! 776: assert(dest->ip_receiver_name == MACH_PORT_NULL); ! 777: ! 778: next = dest->ip_destination; ! 779: ip_unlock(dest); ! 780: dest = next; ! 781: } ! 782: ! 783: return TRUE; ! 784: } ! 785: ! 786: /* ! 787: * The guarantee: lock port while the entire chain is locked. ! 788: * Once port is locked, we can take a reference to dest, ! 789: * add port to the chain, and unlock everything. ! 790: */ ! 791: ! 792: ip_lock(port); ! 793: ipc_port_multiple_unlock(); ! 794: ! 795: not_circular: ! 796: ! 797: /* port is in limbo */ ! 798: ! 799: assert(ip_active(port)); ! 800: assert(port->ip_receiver_name == MACH_PORT_NULL); ! 801: assert(port->ip_destination == IP_NULL); ! 802: ! 803: ip_reference(dest); ! 804: port->ip_destination = dest; ! 805: ! 806: /* now unlock chain */ ! 807: ! 808: while (port != base) { ! 809: ipc_port_t next; ! 810: ! 811: /* port is in transit */ ! 812: ! 813: assert(ip_active(port)); ! 814: assert(port->ip_receiver_name == MACH_PORT_NULL); ! 815: assert(port->ip_destination != IP_NULL); ! 816: ! 817: next = port->ip_destination; ! 818: ip_unlock(port); ! 819: port = next; ! 820: } ! 821: ! 822: /* base is not in transit */ ! 823: ! 824: assert(!ip_active(base) || ! 825: (base->ip_receiver_name != MACH_PORT_NULL) || ! 826: (base->ip_destination == IP_NULL)); ! 827: ip_unlock(base); ! 828: ! 829: return FALSE; ! 830: } ! 831: ! 832: /* ! 833: * Routine: ipc_port_lookup_notify ! 834: * Purpose: ! 835: * Make a send-once notify port from a receive right. ! 836: * Returns IP_NULL if name doesn't denote a receive right. ! 837: * Conditions: ! 838: * The space must be locked (read or write) and active. ! 839: * Being the active space, we can rely on thread server_id ! 840: * context to give us the proper server level sub-order ! 841: * within the space. ! 842: */ ! 843: ! 844: ipc_port_t ! 845: ipc_port_lookup_notify( ! 846: ipc_space_t space, ! 847: mach_port_name_t name) ! 848: { ! 849: ipc_port_t port; ! 850: ipc_entry_t entry; ! 851: ! 852: assert(space->is_active); ! 853: ! 854: entry = ipc_entry_lookup(space, name); ! 855: if (entry == IE_NULL) ! 856: return IP_NULL; ! 857: if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0) ! 858: return IP_NULL; ! 859: ! 860: port = (ipc_port_t) entry->ie_object; ! 861: assert(port != IP_NULL); ! 862: ! 863: ip_lock(port); ! 864: assert(ip_active(port)); ! 865: assert(port->ip_receiver_name == name); ! 866: assert(port->ip_receiver == space); ! 867: ! 868: ip_reference(port); ! 869: port->ip_sorights++; ! 870: ip_unlock(port); ! 871: ! 872: return port; ! 873: } ! 874: ! 875: /* ! 876: * Routine: ipc_port_make_send ! 877: * Purpose: ! 878: * Make a naked send right from a receive right. ! 879: * Conditions: ! 880: * The port is not locked but it is active. ! 881: */ ! 882: ! 883: ipc_port_t ! 884: ipc_port_make_send( ! 885: ipc_port_t port) ! 886: { ! 887: assert(IP_VALID(port)); ! 888: ! 889: ip_lock(port); ! 890: assert(ip_active(port)); ! 891: port->ip_mscount++; ! 892: port->ip_srights++; ! 893: ip_reference(port); ! 894: ip_unlock(port); ! 895: ! 896: return port; ! 897: } ! 898: ! 899: /* ! 900: * Routine: ipc_port_copy_send ! 901: * Purpose: ! 902: * Make a naked send right from another naked send right. ! 903: * IP_NULL -> IP_NULL ! 904: * IP_DEAD -> IP_DEAD ! 905: * dead port -> IP_DEAD ! 906: * live port -> port + ref ! 907: * Conditions: ! 908: * Nothing locked except possibly a space. ! 909: */ ! 910: ! 911: ipc_port_t ! 912: ipc_port_copy_send( ! 913: ipc_port_t port) ! 914: { ! 915: ipc_port_t sright; ! 916: ! 917: if (!IP_VALID(port)) ! 918: return port; ! 919: ! 920: ip_lock(port); ! 921: if (ip_active(port)) { ! 922: assert(port->ip_srights > 0); ! 923: ! 924: ip_reference(port); ! 925: port->ip_srights++; ! 926: sright = port; ! 927: } else ! 928: sright = IP_DEAD; ! 929: ip_unlock(port); ! 930: ! 931: return sright; ! 932: } ! 933: ! 934: /* ! 935: * Routine: ipc_port_copyout_send ! 936: * Purpose: ! 937: * Copyout a naked send right (possibly null/dead), ! 938: * or if that fails, destroy the right. ! 939: * Conditions: ! 940: * Nothing locked. ! 941: */ ! 942: ! 943: mach_port_name_t ! 944: ipc_port_copyout_send( ! 945: ipc_port_t sright, ! 946: ipc_space_t space) ! 947: { ! 948: mach_port_name_t name; ! 949: ! 950: if (IP_VALID(sright)) { ! 951: kern_return_t kr; ! 952: ! 953: kr = ipc_object_copyout(space, (ipc_object_t) sright, ! 954: MACH_MSG_TYPE_PORT_SEND, TRUE, &name); ! 955: if (kr != KERN_SUCCESS) { ! 956: ipc_port_release_send(sright); ! 957: ! 958: if (kr == KERN_INVALID_CAPABILITY) ! 959: name = MACH_PORT_DEAD; ! 960: else ! 961: name = MACH_PORT_NULL; ! 962: } ! 963: } else ! 964: name = (mach_port_name_t) sright; ! 965: ! 966: return name; ! 967: } ! 968: ! 969: /* ! 970: * Routine: ipc_port_release_send ! 971: * Purpose: ! 972: * Release a (valid) naked send right. ! 973: * Consumes a ref for the port. ! 974: * Conditions: ! 975: * Nothing locked. ! 976: */ ! 977: ! 978: void ! 979: ipc_port_release_send( ! 980: ipc_port_t port) ! 981: { ! 982: ipc_port_t nsrequest = IP_NULL; ! 983: mach_port_mscount_t mscount; ! 984: ! 985: assert(IP_VALID(port)); ! 986: ! 987: ip_lock(port); ! 988: ip_release(port); ! 989: ! 990: if (!ip_active(port)) { ! 991: ip_check_unlock(port); ! 992: return; ! 993: } ! 994: ! 995: assert(port->ip_srights > 0); ! 996: ! 997: if (--port->ip_srights == 0 && ! 998: port->ip_nsrequest != IP_NULL) { ! 999: nsrequest = port->ip_nsrequest; ! 1000: port->ip_nsrequest = IP_NULL; ! 1001: mscount = port->ip_mscount; ! 1002: ip_unlock(port); ! 1003: ipc_notify_no_senders(nsrequest, mscount); ! 1004: /* ! 1005: * Check that there are no other locks taken, because ! 1006: * [norma_]ipc_notify_no_senders routines may block. ! 1007: */ ! 1008: check_simple_locks(); ! 1009: } else ! 1010: ip_unlock(port); ! 1011: } ! 1012: ! 1013: /* ! 1014: * Routine: ipc_port_make_sonce ! 1015: * Purpose: ! 1016: * Make a naked send-once right from a receive right. ! 1017: * Conditions: ! 1018: * The port is not locked but it is active. ! 1019: */ ! 1020: ! 1021: ipc_port_t ! 1022: ipc_port_make_sonce( ! 1023: ipc_port_t port) ! 1024: { ! 1025: assert(IP_VALID(port)); ! 1026: ! 1027: ip_lock(port); ! 1028: assert(ip_active(port)); ! 1029: port->ip_sorights++; ! 1030: ip_reference(port); ! 1031: ip_unlock(port); ! 1032: ! 1033: return port; ! 1034: } ! 1035: ! 1036: /* ! 1037: * Routine: ipc_port_release_sonce ! 1038: * Purpose: ! 1039: * Release a naked send-once right. ! 1040: * Consumes a ref for the port. ! 1041: * ! 1042: * In normal situations, this is never used. ! 1043: * Send-once rights are only consumed when ! 1044: * a message (possibly a send-once notification) ! 1045: * is sent to them. ! 1046: * Conditions: ! 1047: * Nothing locked except possibly a space. ! 1048: */ ! 1049: ! 1050: void ! 1051: ipc_port_release_sonce( ! 1052: ipc_port_t port) ! 1053: { ! 1054: assert(IP_VALID(port)); ! 1055: ! 1056: ip_lock(port); ! 1057: ! 1058: assert(port->ip_sorights > 0); ! 1059: ! 1060: port->ip_sorights--; ! 1061: ! 1062: ip_release(port); ! 1063: ! 1064: if (!ip_active(port)) { ! 1065: ip_check_unlock(port); ! 1066: return; ! 1067: } ! 1068: ! 1069: ip_unlock(port); ! 1070: } ! 1071: ! 1072: /* ! 1073: * Routine: ipc_port_release_receive ! 1074: * Purpose: ! 1075: * Release a naked (in limbo or in transit) receive right. ! 1076: * Consumes a ref for the port; destroys the port. ! 1077: * Conditions: ! 1078: * Nothing locked. ! 1079: */ ! 1080: ! 1081: void ! 1082: ipc_port_release_receive( ! 1083: ipc_port_t port) ! 1084: { ! 1085: ipc_port_t dest; ! 1086: ! 1087: assert(IP_VALID(port)); ! 1088: ! 1089: ip_lock(port); ! 1090: assert(ip_active(port)); ! 1091: assert(port->ip_receiver_name == MACH_PORT_NULL); ! 1092: dest = port->ip_destination; ! 1093: ! 1094: ipc_port_destroy(port); /* consumes ref, unlocks */ ! 1095: ! 1096: if (dest != IP_NULL) ! 1097: ipc_port_release(dest); ! 1098: } ! 1099: ! 1100: /* ! 1101: * Routine: ipc_port_alloc_special ! 1102: * Purpose: ! 1103: * Allocate a port in a special space. ! 1104: * The new port is returned with one ref. ! 1105: * If unsuccessful, IP_NULL is returned. ! 1106: * Conditions: ! 1107: * Nothing locked. ! 1108: */ ! 1109: ! 1110: ipc_port_t ! 1111: ipc_port_alloc_special( ! 1112: ipc_space_t space) ! 1113: { ! 1114: ipc_port_t port; ! 1115: ! 1116: port = (ipc_port_t) io_alloc(IOT_PORT); ! 1117: if (port == IP_NULL) ! 1118: return IP_NULL; ! 1119: ! 1120: bzero((char *)port, sizeof(*port)); ! 1121: io_lock_init(&port->ip_object); ! 1122: port->ip_references = 1; ! 1123: port->ip_object.io_bits = io_makebits(TRUE, IOT_PORT, 0); ! 1124: ! 1125: ipc_port_init(port, space, 1); ! 1126: ! 1127: return port; ! 1128: } ! 1129: ! 1130: /* ! 1131: * Routine: ipc_port_dealloc_special ! 1132: * Purpose: ! 1133: * Deallocate a port in a special space. ! 1134: * Consumes one ref for the port. ! 1135: * Conditions: ! 1136: * Nothing locked. ! 1137: */ ! 1138: ! 1139: void ! 1140: ipc_port_dealloc_special( ! 1141: ipc_port_t port, ! 1142: ipc_space_t space) ! 1143: { ! 1144: ip_lock(port); ! 1145: assert(ip_active(port)); ! 1146: assert(port->ip_receiver_name != MACH_PORT_NULL); ! 1147: assert(port->ip_receiver == space); ! 1148: ! 1149: /* ! 1150: * We clear ip_receiver_name and ip_receiver to simplify ! 1151: * the ipc_space_kernel check in ipc_mqueue_send. ! 1152: */ ! 1153: ! 1154: port->ip_receiver_name = MACH_PORT_NULL; ! 1155: port->ip_receiver = IS_NULL; ! 1156: ! 1157: /* relevant part of ipc_port_clear_receiver */ ! 1158: ipc_port_set_mscount(port, 0); ! 1159: port->ip_messages.imq_seqno = 0; ! 1160: ! 1161: ipc_port_destroy(port); ! 1162: } ! 1163: ! 1164: ! 1165: #if MACH_ASSERT ! 1166: /* ! 1167: * Keep a list of all allocated ports. ! 1168: * Allocation is intercepted via ipc_port_init; ! 1169: * deallocation is intercepted via io_free. ! 1170: */ ! 1171: queue_head_t port_alloc_queue; ! 1172: decl_mutex_data(,port_alloc_queue_lock) ! 1173: ! 1174: unsigned long port_count = 0; ! 1175: unsigned long port_count_warning = 20000; ! 1176: unsigned long port_timestamp = 0; ! 1177: ! 1178: void db_port_stack_trace( ! 1179: ipc_port_t port); ! 1180: void db_ref( ! 1181: int refs); ! 1182: int db_port_walk( ! 1183: unsigned int verbose, ! 1184: unsigned int display, ! 1185: unsigned int ref_search, ! 1186: unsigned int ref_target); ! 1187: ! 1188: /* ! 1189: * Initialize global state needed for run-time ! 1190: * port debugging. ! 1191: */ ! 1192: void ! 1193: ipc_port_debug_init(void) ! 1194: { ! 1195: queue_init(&port_alloc_queue); ! 1196: mutex_init(&port_alloc_queue_lock, ETAP_IPC_PORT_ALLOCQ); ! 1197: } ! 1198: ! 1199: ! 1200: /* ! 1201: * Initialize all of the debugging state in a port. ! 1202: * Insert the port into a global list of all allocated ports. ! 1203: */ ! 1204: void ! 1205: ipc_port_init_debug( ! 1206: ipc_port_t port) ! 1207: { ! 1208: unsigned int i; ! 1209: ! 1210: port->ip_thread = (unsigned long) current_thread(); ! 1211: port->ip_timetrack = port_timestamp++; ! 1212: for (i = 0; i < IP_CALLSTACK_MAX; ++i) ! 1213: port->ip_callstack[i] = 0; ! 1214: for (i = 0; i < IP_NSPARES; ++i) ! 1215: port->ip_spares[i] = 0; ! 1216: ! 1217: /* ! 1218: * Machine-dependent routine to fill in an ! 1219: * array with up to IP_CALLSTACK_MAX levels ! 1220: * of return pc information. ! 1221: */ ! 1222: machine_callstack(&port->ip_callstack[0], IP_CALLSTACK_MAX); ! 1223: ! 1224: #if 0 ! 1225: mutex_lock(&port_alloc_queue_lock); ! 1226: ++port_count; ! 1227: if (port_count_warning > 0 && port_count >= port_count_warning) ! 1228: assert(port_count < port_count_warning); ! 1229: queue_enter(&port_alloc_queue, port, ipc_port_t, ip_port_links); ! 1230: mutex_unlock(&port_alloc_queue_lock); ! 1231: #endif ! 1232: } ! 1233: ! 1234: ! 1235: /* ! 1236: * Remove a port from the queue of allocated ports. ! 1237: * This routine should be invoked JUST prior to ! 1238: * deallocating the actual memory occupied by the port. ! 1239: */ ! 1240: void ! 1241: ipc_port_track_dealloc( ! 1242: ipc_port_t port) ! 1243: { ! 1244: #if 0 ! 1245: mutex_lock(&port_alloc_queue_lock); ! 1246: assert(port_count > 0); ! 1247: --port_count; ! 1248: queue_remove(&port_alloc_queue, port, ipc_port_t, ip_port_links); ! 1249: mutex_unlock(&port_alloc_queue_lock); ! 1250: #endif ! 1251: } ! 1252: ! 1253: #endif /* MACH_ASSERT */ ! 1254: ! 1255: ! 1256: #if MACH_KDB ! 1257: ! 1258: #include <ddb/db_output.h> ! 1259: #include <ddb/db_print.h> ! 1260: ! 1261: #define printf kdbprintf ! 1262: extern int db_indent; ! 1263: ! 1264: int ! 1265: db_port_queue_print( ! 1266: ipc_port_t port); ! 1267: ! 1268: /* ! 1269: * ipc_entry_print - pretty-print an ipc_entry ! 1270: */ ! 1271: static void ipc_entry_print(struct ipc_entry *, char *); /* forward */ ! 1272: ! 1273: static void ipc_entry_print(struct ipc_entry *iep, char *tag) ! 1274: { ! 1275: ipc_entry_bits_t bits = iep->ie_bits; ! 1276: ! 1277: iprintf("%s @", tag); ! 1278: printf(" 0x%x, bits=%x object=%x\n", iep, bits, iep->ie_object); ! 1279: db_indent += 2; ! 1280: iprintf("urefs=%x ", IE_BITS_UREFS(bits)); ! 1281: printf("type=%x gen=%x\n", IE_BITS_TYPE(bits), IE_BITS_GEN(bits)); ! 1282: db_indent -= 2; ! 1283: } ! 1284: ! 1285: /* ! 1286: * Routine: ipc_port_print ! 1287: * Purpose: ! 1288: * Pretty-print a port for kdb. ! 1289: */ ! 1290: int ipc_port_print_long = 0; /* set for more detail */ ! 1291: ! 1292: void ! 1293: ipc_port_print( ! 1294: ipc_port_t port, ! 1295: boolean_t have_addr, ! 1296: db_expr_t count, ! 1297: char *modif) ! 1298: { ! 1299: extern int db_indent; ! 1300: db_addr_t task; ! 1301: int task_id; ! 1302: int nmsgs; ! 1303: int verbose = 0; ! 1304: #if MACH_ASSERT ! 1305: int i, needs_db_indent, items_printed; ! 1306: #endif /* MACH_ASSERT */ ! 1307: ! 1308: if (db_option(modif, 'l') || db_option(modif, 'v')) ! 1309: ++verbose; ! 1310: ! 1311: printf("port 0x%x\n", port); ! 1312: ! 1313: db_indent += 2; ! 1314: ! 1315: ipc_object_print(&port->ip_object); ! 1316: ! 1317: if (ipc_port_print_long) { ! 1318: iprintf("pool=0x%x", port->ip_thread_pool); ! 1319: printf("\n"); ! 1320: } ! 1321: ! 1322: if (!ip_active(port)) { ! 1323: iprintf("timestamp=0x%x", port->ip_timestamp); ! 1324: } else if (port->ip_receiver_name == MACH_PORT_NULL) { ! 1325: iprintf("destination=0x%x (", port->ip_destination); ! 1326: if (port->ip_destination != MACH_PORT_NULL && ! 1327: (task = db_task_from_space(port->ip_destination-> ! 1328: ip_receiver, &task_id))) ! 1329: printf("task%d at 0x%x", task_id, task); ! 1330: else ! 1331: printf("unknown"); ! 1332: printf(")"); ! 1333: } else { ! 1334: iprintf("receiver=0x%x (", port->ip_receiver); ! 1335: if (port->ip_receiver == ipc_space_kernel) ! 1336: printf("kernel"); ! 1337: else if (port->ip_receiver == ipc_space_reply) ! 1338: printf("reply"); ! 1339: else if (port->ip_receiver == default_pager_space) ! 1340: printf("default_pager"); ! 1341: else if (task = db_task_from_space(port->ip_receiver, &task_id)) ! 1342: printf("task%d at 0x%x", task_id, task); ! 1343: else ! 1344: printf("unknown"); ! 1345: printf(")"); ! 1346: } ! 1347: printf(", receiver_name=0x%x", port->ip_receiver_name); ! 1348: printf("%s\n", IP_NMS(port) ? ", NMS tracking" : ""); ! 1349: ! 1350: iprintf("mscount=%d", port->ip_mscount); ! 1351: printf(", srights=%d", port->ip_srights); ! 1352: printf(", sorights=%d\n", port->ip_sorights); ! 1353: ! 1354: iprintf("nsrequest=0x%x", port->ip_nsrequest); ! 1355: printf(", pdrequest=0x%x", port->ip_pdrequest); ! 1356: printf(", dnrequests=0x%x\n", port->ip_dnrequests); ! 1357: ! 1358: iprintf("pset_count=0x%x", port->ip_pset_count); ! 1359: printf(", seqno=%d", port->ip_messages.imq_seqno); ! 1360: printf(", msgcount=%d", port->ip_messages.imq_msgcount); ! 1361: printf(", qlimit=%d\n", port->ip_messages.imq_qlimit); ! 1362: ! 1363: iprintf("kmsgs=0x%x", port->ip_messages.imq_messages.ikmq_base); ! 1364: printf(", rcvrs queue=0x%x", port->ip_messages.imq_wait_queue); ! 1365: printf(", kobj=0x%x\n", port->ip_kobject); ! 1366: ! 1367: iprintf("flags=0x%x", port->ip_flags); ! 1368: ! 1369: #if MACH_ASSERT ! 1370: /* don't bother printing callstack or queue links */ ! 1371: iprintf("ip_thread=0x%x, ip_timetrack=0x%x\n", ! 1372: port->ip_thread, port->ip_timetrack); ! 1373: items_printed = 0; ! 1374: needs_db_indent = 1; ! 1375: for (i = 0; i < IP_NSPARES; ++i) { ! 1376: if (port->ip_spares[i] != 0) { ! 1377: if (needs_db_indent) { ! 1378: iprintf(""); ! 1379: needs_db_indent = 0; ! 1380: } ! 1381: printf("%sip_spares[%d] = %d", ! 1382: items_printed ? ", " : "", i, ! 1383: port->ip_spares[i]); ! 1384: if (++items_printed >= 4) { ! 1385: needs_db_indent = 1; ! 1386: printf("\n"); ! 1387: items_printed = 0; ! 1388: } ! 1389: } ! 1390: } ! 1391: #endif /* MACH_ASSERT */ ! 1392: ! 1393: if (verbose) { ! 1394: iprintf("kmsg queue contents:\n"); ! 1395: db_indent += 2; ! 1396: nmsgs = db_port_queue_print(port); ! 1397: db_indent -= 2; ! 1398: iprintf("...total kmsgs: %d\n", nmsgs); ! 1399: } ! 1400: ! 1401: db_indent -=2; ! 1402: } ! 1403: ! 1404: ipc_port_t ! 1405: ipc_name_to_data( ! 1406: task_t task, ! 1407: mach_port_name_t name) ! 1408: { ! 1409: ipc_space_t space; ! 1410: ipc_entry_t entry; ! 1411: ! 1412: if (task == TASK_NULL) { ! 1413: db_printf("port_name_to_data: task is null\n"); ! 1414: return (0); ! 1415: } ! 1416: if ((space = task->itk_space) == 0) { ! 1417: db_printf("port_name_to_data: task->itk_space is null\n"); ! 1418: return (0); ! 1419: } ! 1420: if (!space->is_active) { ! 1421: db_printf("port_name_to_data: task->itk_space not active\n"); ! 1422: return (0); ! 1423: } ! 1424: if ((entry = ipc_entry_lookup(space, name)) == 0) { ! 1425: db_printf("port_name_to_data: lookup yields zero\n"); ! 1426: return (0); ! 1427: } ! 1428: return ((ipc_port_t)entry->ie_object); ! 1429: } ! 1430: ! 1431: #if ZONE_DEBUG ! 1432: void ! 1433: print_type_ports(type, dead) ! 1434: unsigned type; ! 1435: unsigned dead; ! 1436: { ! 1437: ipc_port_t port; ! 1438: int n; ! 1439: ! 1440: n = 0; ! 1441: for (port = (ipc_port_t)first_element(ipc_object_zones[IOT_PORT]); ! 1442: port; ! 1443: port = (ipc_port_t)next_element(ipc_object_zones[IOT_PORT], ! 1444: (vm_offset_t)port)) ! 1445: if (ip_kotype(port) == type && ! 1446: (!dead || !ip_active(port))) { ! 1447: if (++n % 5) ! 1448: printf("0x%x\t", port); ! 1449: else ! 1450: printf("0x%x\n", port); ! 1451: } ! 1452: if (n % 5) ! 1453: printf("\n"); ! 1454: } ! 1455: ! 1456: void ! 1457: print_ports(void) ! 1458: { ! 1459: ipc_port_t port; ! 1460: int total_port_count; ! 1461: int space_null_count; ! 1462: int space_kernel_count; ! 1463: int space_reply_count; ! 1464: int space_pager_count; ! 1465: int space_other_count; ! 1466: ! 1467: struct { ! 1468: int total_count; ! 1469: int dead_count; ! 1470: } port_types[IKOT_MAX_TYPE]; ! 1471: ! 1472: total_port_count = 0; ! 1473: ! 1474: bzero((char *)&port_types[0], sizeof(port_types)); ! 1475: space_null_count = 0; ! 1476: space_kernel_count = 0; ! 1477: space_reply_count = 0; ! 1478: space_pager_count = 0; ! 1479: space_other_count = 0; ! 1480: ! 1481: for (port = (ipc_port_t)first_element(ipc_object_zones[IOT_PORT]); ! 1482: port; ! 1483: port = (ipc_port_t)next_element(ipc_object_zones[IOT_PORT], ! 1484: (vm_offset_t)port)) { ! 1485: total_port_count++; ! 1486: if (ip_kotype(port) >= IKOT_MAX_TYPE) { ! 1487: port_types[IKOT_UNKNOWN].total_count++; ! 1488: if (!io_active(&port->ip_object)) ! 1489: port_types[IKOT_UNKNOWN].dead_count++; ! 1490: } else { ! 1491: port_types[ip_kotype(port)].total_count++; ! 1492: if (!io_active(&port->ip_object)) ! 1493: port_types[ip_kotype(port)].dead_count++; ! 1494: } ! 1495: ! 1496: if (!port->ip_receiver) ! 1497: space_null_count++; ! 1498: else if (port->ip_receiver == ipc_space_kernel) ! 1499: space_kernel_count++; ! 1500: else if (port->ip_receiver == ipc_space_reply) ! 1501: space_reply_count++; ! 1502: else if (port->ip_receiver == default_pager_space) ! 1503: space_pager_count++; ! 1504: else ! 1505: space_other_count++; ! 1506: } ! 1507: printf("\n%7d total ports\n\n", total_port_count); ! 1508: ! 1509: #define PRINT_ONE_PORT_TYPE(name) \ ! 1510: printf("%7d %s", port_types[IKOT_##name].total_count, # name); \ ! 1511: if (port_types[IKOT_##name].dead_count) \ ! 1512: printf(" (%d dead ports)", port_types[IKOT_##name].dead_count);\ ! 1513: printf("\n"); ! 1514: ! 1515: PRINT_ONE_PORT_TYPE(NONE); ! 1516: PRINT_ONE_PORT_TYPE(THREAD); ! 1517: PRINT_ONE_PORT_TYPE(TASK); ! 1518: PRINT_ONE_PORT_TYPE(HOST); ! 1519: PRINT_ONE_PORT_TYPE(HOST_PRIV); ! 1520: PRINT_ONE_PORT_TYPE(PROCESSOR); ! 1521: PRINT_ONE_PORT_TYPE(PSET); ! 1522: PRINT_ONE_PORT_TYPE(PSET_NAME); ! 1523: PRINT_ONE_PORT_TYPE(PAGING_REQUEST); ! 1524: PRINT_ONE_PORT_TYPE(XMM_OBJECT); ! 1525: PRINT_ONE_PORT_TYPE(DEVICE); ! 1526: PRINT_ONE_PORT_TYPE(XMM_PAGER); ! 1527: PRINT_ONE_PORT_TYPE(XMM_KERNEL); ! 1528: PRINT_ONE_PORT_TYPE(XMM_REPLY); ! 1529: PRINT_ONE_PORT_TYPE(CLOCK); ! 1530: PRINT_ONE_PORT_TYPE(CLOCK_CTRL); ! 1531: PRINT_ONE_PORT_TYPE(MASTER_DEVICE); ! 1532: PRINT_ONE_PORT_TYPE(UNKNOWN); ! 1533: printf("\nipc_space:\n\n"); ! 1534: printf("NULL KERNEL REPLY PAGER OTHER\n"); ! 1535: printf("%d %d %d %d %d\n", ! 1536: space_null_count, ! 1537: space_kernel_count, ! 1538: space_reply_count, ! 1539: space_pager_count, ! 1540: space_other_count ! 1541: ); ! 1542: } ! 1543: ! 1544: #endif /* ZONE_DEBUG */ ! 1545: ! 1546: ! 1547: /* ! 1548: * Print out all the kmsgs in a queue. Aggregate kmsgs with ! 1549: * identical message ids into a single entry. Count up the ! 1550: * amount of inline and out-of-line data consumed by each ! 1551: * and every kmsg. ! 1552: * ! 1553: */ ! 1554: ! 1555: #define KMSG_MATCH_FIELD(kmsg) ((unsigned int) kmsg->ikm_header.msgh_id) ! 1556: #define DKQP_LONG(kmsg) FALSE ! 1557: char *dkqp_long_format = "(%3d) <%10d> 0x%x %10d %10d\n"; ! 1558: char *dkqp_format = "(%3d) <%10d> 0x%x %10d %10d\n"; ! 1559: ! 1560: int ! 1561: db_kmsg_queue_print( ! 1562: ipc_kmsg_t kmsg); ! 1563: int ! 1564: db_kmsg_queue_print( ! 1565: ipc_kmsg_t kmsg) ! 1566: { ! 1567: ipc_kmsg_t ikmsg, first_kmsg; ! 1568: register int icount; ! 1569: mach_msg_id_t cur_id; ! 1570: unsigned int inline_total, ool_total; ! 1571: int nmsgs; ! 1572: ! 1573: iprintf("Count msgh_id kmsg addr inline bytes ool bytes\n"); ! 1574: inline_total = ool_total = (vm_size_t) 0; ! 1575: cur_id = KMSG_MATCH_FIELD(kmsg); ! 1576: for (icount = 0, nmsgs = 0, first_kmsg = ikmsg = kmsg; ! 1577: kmsg != IKM_NULL && (kmsg != first_kmsg || nmsgs == 0); ! 1578: kmsg = kmsg->ikm_next) { ! 1579: ++nmsgs; ! 1580: if (!(KMSG_MATCH_FIELD(kmsg) == cur_id)) { ! 1581: iprintf(DKQP_LONG(kmsg) ? dkqp_long_format:dkqp_format, ! 1582: icount, cur_id, ikmsg, inline_total,ool_total); ! 1583: cur_id = KMSG_MATCH_FIELD(kmsg); ! 1584: icount = 1; ! 1585: ikmsg = kmsg; ! 1586: inline_total = ool_total = 0; ! 1587: } else { ! 1588: icount++; ! 1589: } ! 1590: if (DKQP_LONG(kmsg)) ! 1591: inline_total += kmsg->ikm_size; ! 1592: else ! 1593: inline_total += kmsg->ikm_header.msgh_size; ! 1594: } ! 1595: iprintf(DKQP_LONG(kmsg) ? dkqp_long_format : dkqp_format, ! 1596: icount, cur_id, ikmsg, inline_total, ool_total); ! 1597: return nmsgs; ! 1598: } ! 1599: ! 1600: ! 1601: /* ! 1602: * Process all of the messages on a port - prints out the ! 1603: * number of occurences of each message type, and the first ! 1604: * kmsg with a particular msgh_id. ! 1605: */ ! 1606: int ! 1607: db_port_queue_print( ! 1608: ipc_port_t port) ! 1609: { ! 1610: ipc_kmsg_t kmsg; ! 1611: ! 1612: if (ipc_kmsg_queue_empty(&port->ip_messages.imq_messages)) ! 1613: return 0; ! 1614: kmsg = ipc_kmsg_queue_first(&port->ip_messages.imq_messages); ! 1615: return db_kmsg_queue_print(kmsg); ! 1616: } ! 1617: ! 1618: ! 1619: #if MACH_ASSERT ! 1620: #include <ddb/db_sym.h> ! 1621: #include <ddb/db_access.h> ! 1622: ! 1623: #define FUNC_NULL ((void (*)) 0) ! 1624: #define MAX_REFS 5 /* bins for tracking ref counts */ ! 1625: ! 1626: /* ! 1627: * Translate port's cache of call stack pointers ! 1628: * into symbolic names. ! 1629: */ ! 1630: void ! 1631: db_port_stack_trace( ! 1632: ipc_port_t port) ! 1633: { ! 1634: unsigned int i; ! 1635: ! 1636: for (i = 0; i < IP_CALLSTACK_MAX; ++i) { ! 1637: iprintf("[%d] 0x%x\t", i, port->ip_callstack[i]); ! 1638: if (port->ip_callstack[i] != 0 && ! 1639: DB_VALID_KERN_ADDR(port->ip_callstack[i])) ! 1640: db_printsym(port->ip_callstack[i], DB_STGY_PROC); ! 1641: printf("\n"); ! 1642: } ! 1643: } ! 1644: ! 1645: ! 1646: typedef struct port_item { ! 1647: unsigned long item; ! 1648: unsigned long count; ! 1649: } port_item; ! 1650: ! 1651: ! 1652: #define ITEM_MAX 400 ! 1653: typedef struct port_track { ! 1654: char *name; ! 1655: unsigned long max; ! 1656: unsigned long warning; ! 1657: port_item items[ITEM_MAX]; ! 1658: } port_track; ! 1659: ! 1660: port_track port_callers; /* match against calling addresses */ ! 1661: port_track port_threads; /* match against allocating threads */ ! 1662: port_track port_spaces; /* match against ipc spaces */ ! 1663: ! 1664: void port_track_init( ! 1665: port_track *trackp, ! 1666: char *name); ! 1667: void port_item_add( ! 1668: port_track *trackp, ! 1669: unsigned long item); ! 1670: void port_track_sort( ! 1671: port_track *trackp); ! 1672: void port_track_print( ! 1673: port_track *trackp, ! 1674: void (*func)(port_item *)); ! 1675: void port_callers_print( ! 1676: port_item *p); ! 1677: ! 1678: void ! 1679: port_track_init( ! 1680: port_track *trackp, ! 1681: char *name) ! 1682: { ! 1683: port_item *i; ! 1684: ! 1685: trackp->max = trackp->warning = 0; ! 1686: trackp->name = name; ! 1687: for (i = trackp->items; i < trackp->items + ITEM_MAX; ++i) ! 1688: i->item = i->count = 0; ! 1689: } ! 1690: ! 1691: ! 1692: void ! 1693: port_item_add( ! 1694: port_track *trackp, ! 1695: unsigned long item) ! 1696: { ! 1697: port_item *limit, *i; ! 1698: ! 1699: limit = trackp->items + trackp->max; ! 1700: for (i = trackp->items; i < limit; ++i) ! 1701: if (i->item == item) { ! 1702: i->count++; ! 1703: return; ! 1704: } ! 1705: if (trackp->max >= ITEM_MAX) { ! 1706: if (trackp->warning++ == 0) ! 1707: iprintf("%s: no room\n", trackp->name); ! 1708: return; ! 1709: } ! 1710: i->item = item; ! 1711: i->count = 1; ! 1712: trackp->max++; ! 1713: } ! 1714: ! 1715: ! 1716: /* ! 1717: * Simple (and slow) bubble sort. ! 1718: */ ! 1719: void ! 1720: port_track_sort( ! 1721: port_track *trackp) ! 1722: { ! 1723: port_item *limit, *p; ! 1724: port_item temp; ! 1725: boolean_t unsorted; ! 1726: ! 1727: limit = trackp->items + trackp->max - 1; ! 1728: do { ! 1729: unsorted = FALSE; ! 1730: for (p = trackp->items; p < limit - 1; ++p) { ! 1731: if (p->count < (p+1)->count) { ! 1732: temp = *p; ! 1733: *p = *(p+1); ! 1734: *(p+1) = temp; ! 1735: unsorted = TRUE; ! 1736: } ! 1737: } ! 1738: } while (unsorted == TRUE); ! 1739: } ! 1740: ! 1741: ! 1742: void ! 1743: port_track_print( ! 1744: port_track *trackp, ! 1745: void (*func)(port_item *)) ! 1746: { ! 1747: port_item *limit, *p; ! 1748: ! 1749: limit = trackp->items + trackp->max; ! 1750: iprintf("%s:\n", trackp->name); ! 1751: for (p = trackp->items; p < limit; ++p) { ! 1752: if (func != FUNC_NULL) ! 1753: (*func)(p); ! 1754: else ! 1755: iprintf("0x%x\t%8d\n", p->item, p->count); ! 1756: } ! 1757: } ! 1758: ! 1759: ! 1760: void ! 1761: port_callers_print( ! 1762: port_item *p) ! 1763: { ! 1764: iprintf("0x%x\t%8d\t", p->item, p->count); ! 1765: db_printsym(p->item, DB_STGY_PROC); ! 1766: printf("\n"); ! 1767: } ! 1768: ! 1769: ! 1770: /* ! 1771: * Show all ports with a given reference count. ! 1772: */ ! 1773: void ! 1774: db_ref( ! 1775: int refs) ! 1776: { ! 1777: db_port_walk(1, 1, 1, refs); ! 1778: } ! 1779: ! 1780: ! 1781: /* ! 1782: * Examine all currently allocated ports. ! 1783: * Options: ! 1784: * verbose display suspicious ports ! 1785: * display print out each port encountered ! 1786: * ref_search restrict examination to ports with ! 1787: * a specified reference count ! 1788: * ref_target reference count for ref_search ! 1789: */ ! 1790: int ! 1791: db_port_walk( ! 1792: unsigned int verbose, ! 1793: unsigned int display, ! 1794: unsigned int ref_search, ! 1795: unsigned int ref_target) ! 1796: { ! 1797: ipc_port_t port; ! 1798: unsigned int ref_overflow, refs, i, ref_inactive_overflow; ! 1799: unsigned int no_receiver, no_match; ! 1800: unsigned int ref_counts[MAX_REFS]; ! 1801: unsigned int inactive[MAX_REFS]; ! 1802: unsigned int ipc_ports = 0; ! 1803: unsigned int proxies = 0, principals = 0; ! 1804: ! 1805: iprintf("Allocated port count is %d\n", port_count); ! 1806: no_receiver = no_match = ref_overflow = 0; ! 1807: ref_inactive_overflow = 0; ! 1808: for (i = 0; i < MAX_REFS; ++i) { ! 1809: ref_counts[i] = 0; ! 1810: inactive[i] = 0; ! 1811: } ! 1812: port_track_init(&port_callers, "port callers"); ! 1813: port_track_init(&port_threads, "port threads"); ! 1814: port_track_init(&port_spaces, "port spaces"); ! 1815: if (ref_search) ! 1816: iprintf("Walking ports of ref_count=%d.\n", ref_target); ! 1817: else ! 1818: iprintf("Walking all ports.\n"); ! 1819: ! 1820: queue_iterate(&port_alloc_queue, port, ipc_port_t, ip_port_links) { ! 1821: char *port_type; ! 1822: ! 1823: port_type = " IPC port"; ! 1824: if (ip_active(port)) ! 1825: ipc_ports++; ! 1826: ! 1827: refs = port->ip_references; ! 1828: if (ref_search && refs != ref_target) ! 1829: continue; ! 1830: ! 1831: if (refs >= MAX_REFS) { ! 1832: if (ip_active(port)) ! 1833: ++ref_overflow; ! 1834: else ! 1835: ++ref_inactive_overflow; ! 1836: } else { ! 1837: if (refs == 0 && verbose) ! 1838: iprintf("%s 0x%x has ref count of zero!\n", ! 1839: port_type, port); ! 1840: if (ip_active(port)) ! 1841: ref_counts[refs]++; ! 1842: else ! 1843: inactive[refs]++; ! 1844: } ! 1845: port_item_add(&port_threads, (unsigned long) port->ip_thread); ! 1846: for (i = 0; i < IP_CALLSTACK_MAX; ++i) { ! 1847: if (port->ip_callstack[i] != 0 && ! 1848: DB_VALID_KERN_ADDR(port->ip_callstack[i])) ! 1849: port_item_add(&port_callers, ! 1850: port->ip_callstack[i]); ! 1851: } ! 1852: if (!ip_active(port)) { ! 1853: if (verbose) ! 1854: iprintf("%s 0x%x, inactive, refcnt %d\n", ! 1855: port_type, port, refs); ! 1856: continue; ! 1857: } ! 1858: ! 1859: if (port->ip_receiver_name == MACH_PORT_NULL) { ! 1860: iprintf("%s 0x%x, no receiver, refcnt %d\n", ! 1861: port, refs); ! 1862: ++no_receiver; ! 1863: continue; ! 1864: } ! 1865: if (port->ip_receiver == ipc_space_kernel || ! 1866: port->ip_receiver == ipc_space_reply || ! 1867: ipc_entry_lookup(port->ip_receiver, ! 1868: port->ip_receiver_name) ! 1869: != IE_NULL) { ! 1870: port_item_add(&port_spaces, ! 1871: (unsigned long)port->ip_receiver); ! 1872: if (display) { ! 1873: iprintf( "%s 0x%x time 0x%x ref_cnt %d\n", ! 1874: port_type, port, ! 1875: port->ip_timetrack, refs); ! 1876: } ! 1877: continue; ! 1878: } ! 1879: iprintf("%s 0x%x, rcvr 0x%x, name 0x%x, ref %d, no match\n", ! 1880: port_type, port, port->ip_receiver, ! 1881: port->ip_receiver_name, refs); ! 1882: ++no_match; ! 1883: } ! 1884: iprintf("Active port type summary:\n"); ! 1885: iprintf("\tlocal IPC %6d\n", ipc_ports); ! 1886: iprintf("summary:\tcallers %d threads %d spaces %d\n", ! 1887: port_callers.max, port_threads.max, port_spaces.max); ! 1888: ! 1889: iprintf("\tref_counts:\n"); ! 1890: for (i = 0; i < MAX_REFS; ++i) ! 1891: iprintf("\t ref_counts[%d] = %d\n", i, ref_counts[i]); ! 1892: ! 1893: iprintf("\t%d ports w/o receivers, %d w/o matches\n", ! 1894: no_receiver, no_match); ! 1895: ! 1896: iprintf("\tinactives:"); ! 1897: if ( ref_inactive_overflow || inactive[0] || inactive[1] || ! 1898: inactive[2] || inactive[3] || inactive[4] ) ! 1899: printf(" [0]=%d [1]=%d [2]=%d [3]=%d [4]=%d [5+]=%d\n", ! 1900: inactive[0], inactive[1], inactive[2], ! 1901: inactive[3], inactive[4], ref_inactive_overflow); ! 1902: else ! 1903: printf(" No inactive ports.\n"); ! 1904: ! 1905: port_track_sort(&port_spaces); ! 1906: port_track_print(&port_spaces, FUNC_NULL); ! 1907: port_track_sort(&port_threads); ! 1908: port_track_print(&port_threads, FUNC_NULL); ! 1909: port_track_sort(&port_callers); ! 1910: port_track_print(&port_callers, port_callers_print); ! 1911: return 0; ! 1912: } ! 1913: ! 1914: ! 1915: #endif /* MACH_ASSERT */ ! 1916: ! 1917: #endif /* MACH_KDB */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.