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