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