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