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