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