xref: /trueos/sys/compat/mach/ipc/ipc_pset.c (revision f27c8b1c4248deb0e61ffbff04fcf11755f2bfa9)
1 /*
2  * Copyright 1991-1998 by Open Software Foundation, Inc.
3  *              All Rights Reserved
4  *
5  * Permission to use, copy, modify, and distribute this software and
6  * its documentation for any purpose and without fee is hereby granted,
7  * provided that the above copyright notice appears in all copies and
8  * that both the copyright notice and this permission notice appear in
9  * supporting documentation.
10  *
11  * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
12  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13  * FOR A PARTICULAR PURPOSE.
14  *
15  * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
16  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
17  * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
18  * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
19  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 /*
22  * MkLinux
23  */
24 /* CMU_HIST */
25 /*
26  * Revision 2.5  91/05/14  16:35:47  mrt
27  * 	Correcting copyright
28  *
29  * Revision 2.4  91/02/05  17:23:15  mrt
30  * 	Changed to new Mach copyright
31  * 	[91/02/01  15:50:24  mrt]
32  *
33  * Revision 2.3  90/11/05  14:29:47  rpd
34  * 	Use new ips_reference and ips_release.
35  * 	[90/10/29            rpd]
36  *
37  * Revision 2.2  90/06/02  14:51:19  rpd
38  * 	Created for new IPC.
39  * 	[90/03/26  21:01:53  rpd]
40  *
41  */
42 /* CMU_ENDHIST */
43 /*
44  * Mach Operating System
45  * Copyright (c) 1991,1990,1989 Carnegie Mellon University
46  * All Rights Reserved.
47  *
48  * Permission to use, copy, modify and distribute this software and its
49  * documentation is hereby granted, provided that both the copyright
50  * notice and this permission notice appear in all copies of the
51  * software, derivative works or modified versions, and any portions
52  * thereof, and that both notices appear in supporting documentation.
53  *
54  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
55  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
56  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
57  *
58  * Carnegie Mellon requests users of this software to return to
59  *
60  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
61  *  School of Computer Science
62  *  Carnegie Mellon University
63  *  Pittsburgh PA 15213-3890
64  *
65  * any improvements or extensions that they make and grant Carnegie Mellon
66  * the rights to redistribute these changes.
67  */
68 /*
69  */
70 /*
71  *	File:	ipc/ipc_pset.c
72  *	Author:	Rich Draves
73  *	Date:	1989
74  *
75  *	Functions to manipulate IPC port sets.
76  */
77 
78 #include <sys/cdefs.h>
79 #include <sys/types.h>
80 #include <sys/event.h>
81 
82 
83 
84 #define MACH_INTERNAL
85 #include <sys/mach/port.h>
86 #include <sys/mach/kern_return.h>
87 #include <sys/mach/message.h>
88 #include <sys/mach/ipc/ipc_kmsg.h>
89 #include <sys/mach/ipc/ipc_mqueue.h>
90 #include <sys/mach/ipc/ipc_object.h>
91 #include <sys/mach/ipc/ipc_port.h>
92 #include <sys/mach/ipc/ipc_pset.h>
93 #include <sys/mach/ipc/ipc_right.h>
94 #include <sys/mach/ipc/ipc_space.h>
95 #include <sys/mach/ipc/ipc_print.h>
96 
97 #include <sys/mach/thread.h>
98 
99 static void
kn_sx_lock(void * arg)100 kn_sx_lock(void *arg)
101 {
102 	struct sx *lock = arg;
103 
104 	sx_xlock(lock);
105 }
106 
107 static void
kn_sx_unlock(void * arg)108 kn_sx_unlock(void *arg)
109 {
110 	struct sx *lock = arg;
111 
112 	sx_xunlock(lock);
113 }
114 
115 static void
sx_assert_locked(void * arg)116 sx_assert_locked(void *arg)
117 {
118 	sx_assert((struct sx *)arg, SX_LOCKED);
119 }
120 
121 static void
sx_assert_unlocked(void * arg)122 sx_assert_unlocked(void *arg)
123 {
124 	sx_assert((struct sx *)arg, SX_UNLOCKED);
125 }
126 
127 void
io_validate(ipc_object_t io)128 io_validate(ipc_object_t io)
129 {
130 	ipc_port_t port;
131 	ipc_pset_t pset;
132 
133 	if (io_otype(io) == IOT_PORT) {
134 		port = (ipc_port_t)io;
135 		MPASS(port->ip_pset == NULL);
136 		assert(!ip_active(port));
137 	} else {
138 		pset = (ipc_pset_t)io;
139 		MPASS(TAILQ_EMPTY(&pset->ips_ports));
140 	}
141 
142 }
143 
144 /*
145  * Forward declarations
146  */
147 void ipc_pset_add(
148 	ipc_pset_t	pset,
149 	ipc_port_t	port);
150 
151 /*
152  *	Routine:	ipc_pset_alloc
153  *	Purpose:
154  *		Allocate a port set.
155  *	Conditions:
156  *		Nothing locked.  If successful, the port set is returned
157  *		locked.  (The caller doesn't have a reference.)
158  *	Returns:
159  *		KERN_SUCCESS		The port set is allocated.
160  *		KERN_INVALID_TASK	The space is dead.
161  *		KERN_NO_SPACE		No room for an entry in the space.
162  *		KERN_RESOURCE_SHORTAGE	Couldn't allocate memory.
163  */
164 
165 kern_return_t
ipc_pset_alloc(ipc_space_t space,mach_port_name_t * namep,ipc_pset_t * psetp)166 ipc_pset_alloc(
167 	ipc_space_t	space,
168 	mach_port_name_t	*namep,
169 	ipc_pset_t	*psetp)
170 {
171 	ipc_pset_t pset;
172 	mach_port_name_t name;
173 	kern_return_t kr;
174 
175 	kr = ipc_object_alloc(space, IOT_PORT_SET,
176 			      MACH_PORT_TYPE_PORT_SET,
177 			      &name, (ipc_object_t *) &pset);
178 	if (kr != KERN_SUCCESS)
179 		return kr;
180 	/* pset is locked */
181 
182 	pset->ips_local_name = name;
183 	TAILQ_INIT(&pset->ips_ports);
184 	sx_init(&pset->ips_note_lock, "pset knote lock");
185 	knlist_init(&pset->ips_note, &pset->ips_note_lock,
186 				kn_sx_lock, kn_sx_unlock, sx_assert_locked, sx_assert_unlocked);
187 	thread_pool_init(&pset->ips_thread_pool);
188 	*namep = name;
189 	*psetp = pset;
190 	return KERN_SUCCESS;
191 }
192 
193 /*
194  *	Routine:	ipc_pset_alloc_name
195  *	Purpose:
196  *		Allocate a port set, with a specific name.
197  *	Conditions:
198  *		Nothing locked.  If successful, the port set is returned
199  *		locked.  (The caller doesn't have a reference.)
200  *	Returns:
201  *		KERN_SUCCESS		The port set is allocated.
202  *		KERN_INVALID_TASK	The space is dead.
203  *		KERN_NAME_EXISTS	The name already denotes a right.
204  *		KERN_RESOURCE_SHORTAGE	Couldn't allocate memory.
205  */
206 
207 kern_return_t
ipc_pset_alloc_name(ipc_space_t space,mach_port_name_t name,ipc_pset_t * psetp)208 ipc_pset_alloc_name(
209 	ipc_space_t	space,
210 	mach_port_name_t	name,
211 	ipc_pset_t	*psetp)
212 {
213 	ipc_pset_t pset;
214 	kern_return_t kr;
215 
216 
217 	kr = ipc_object_alloc_name(space, IOT_PORT_SET,
218 				   MACH_PORT_TYPE_PORT_SET,
219 				   name, (ipc_object_t *) &pset);
220 	if (kr != KERN_SUCCESS)
221 		return kr;
222 	/* pset is locked */
223 
224 	pset->ips_local_name = name;
225 	thread_pool_init(&pset->ips_thread_pool);
226 	*psetp = pset;
227 	return KERN_SUCCESS;
228 }
229 
230 /*
231  *	Routine:	ipc_pset_add
232  *	Purpose:
233  *		Puts a port into a port set.
234  *		The port set gains a reference.
235  *	Conditions:
236  *		Both port and port set are locked and active.
237  *		The port isn't already in a set.
238  *		The owner of the port set is also receiver for the port.
239  */
240 
241 void
ipc_pset_add(ipc_pset_t pset,ipc_port_t port)242 ipc_pset_add(
243 	ipc_pset_t	pset,
244 	ipc_port_t	port)
245 {
246 	assert(ips_active(pset));
247 	assert(ip_active(port));
248 	assert(port->ip_pset == IPS_NULL);
249 
250 	port->ip_pset = pset;
251 	ips_reference(pset);
252 	TAILQ_INSERT_TAIL(&pset->ips_ports, port, ip_next);
253 }
254 
255 /*
256  *	Routine:	ipc_pset_remove
257  *	Purpose:
258  *		Removes a port from a port set.
259  *		The port set loses a reference.
260  *	Conditions:
261  *		Both port and port set are locked.
262  *		The port must be active.
263  */
264 
265 void
ipc_pset_remove(ipc_pset_t pset,ipc_port_t port)266 ipc_pset_remove(
267 	ipc_pset_t	pset,
268 	ipc_port_t	port)
269 {
270 	assert(ip_active(port));
271 	assert(port->ip_pset == pset);
272 
273 	port->ip_pset = IPS_NULL;
274 	TAILQ_REMOVE(&pset->ips_ports, port, ip_next);
275 }
276 
277 /*
278  *	Routine:	ipc_pset_move
279  *	Purpose:
280  *		If nset is IPS_NULL, removes port
281  *		from the port set it is in.  Otherwise, adds
282  *		port to nset, removing it from any set
283  *		it might already be in.
284  *	Conditions:
285  *		The space is read-locked.
286  *	Returns:
287  *		KERN_SUCCESS		Moved the port.
288  *		KERN_NOT_IN_SET		nset is null and port isn't in a set.
289  */
290 
291 kern_return_t
ipc_pset_move(ipc_space_t space,ipc_port_t port,ipc_pset_t nset)292 ipc_pset_move(
293 	ipc_space_t	space,
294 	ipc_port_t	port,
295 	ipc_pset_t	nset)
296 {
297 	ipc_pset_t oset;
298 	int active;
299 	int knotify;
300 	/*
301 	 *	While we've got the space locked, it holds refs for
302 	 *	the port and nset (because of the entries).  Also,
303 	 *	they must be alive.  While we've got port locked, it
304 	 *	holds a ref for oset, which might not be alive.
305 	 */
306 
307 	ip_lock(port);
308 	assert(ip_active(port));
309 
310 	knotify = FALSE;
311 	oset = port->ip_pset;
312 
313 	if (oset == nset) {
314 		/* the port is already in the new set:  a noop */
315 
316 		is_read_unlock(space);
317 	} else if (oset == IPS_NULL) {
318 		/* just add port to the new set */
319 
320 		ips_lock(nset);
321 		assert(ips_active(nset));
322 		is_read_unlock(space);
323 
324 		ipc_pset_add(nset, port);
325 		if (port->ip_msgcount != 0)
326 			knotify = TRUE;
327 		ips_unlock(nset);
328 	} else if (nset == IPS_NULL) {
329 		/* just remove port from the old set */
330 
331 		is_read_unlock(space);
332 		ips_lock(oset);
333 
334 		ipc_pset_remove(oset, port);
335 		active = ips_active(oset);
336 		ips_unlock(oset);
337 		ips_release(oset);
338 		if (!active)
339 			oset = IPS_NULL; /* trigger KERN_NOT_IN_SET */
340 	} else {
341 		/* atomically move port from oset to nset */
342 
343 		if (oset < nset) {
344 			ips_lock(oset);
345 			ips_lock(nset);
346 		} else {
347 			ips_lock(nset);
348 			ips_lock(oset);
349 		}
350 
351 		is_read_unlock(space);
352 		assert(ips_active(nset));
353 
354 		ipc_pset_remove(oset, port);
355 		ipc_pset_add(nset, port);
356 
357 		if (port->ip_msgcount != 0)
358 			knotify = TRUE;
359 
360 		ips_unlock(nset);
361 		ips_unlock(oset);	/* KERN_NOT_IN_SET not a possibility */
362 		ips_release(oset);
363 	}
364 
365 	ip_unlock(port);
366 
367 	if (knotify == TRUE)
368 		ipc_pset_signal(nset);
369 	return (((nset == IPS_NULL) && (oset == IPS_NULL)) ?
370 		KERN_NOT_IN_SET : KERN_SUCCESS);
371 }
372 
373 
374 
375 /*
376  *	Routine:	ipc_pset_changed
377  *	Purpose:
378  *		Wake up receivers waiting on pset.
379  *	Conditions:
380  *		The pset is locked.
381  */
382 
383 static void
ipc_pset_changed(ipc_pset_t pset,mach_msg_return_t mr)384 ipc_pset_changed(
385 	ipc_pset_t		pset,
386 	mach_msg_return_t	mr)
387 {
388 	ipc_thread_t th;
389 
390 	while ((th = thread_pool_get_act((ipc_object_t)pset, 0)) != ITH_NULL) {
391 		th->ith_state = mr;
392 		thread_go(th);
393 	}
394 }
395 
396 /*
397  *	Routine:	ipc_pset_destroy
398  *	Purpose:
399  *		Destroys a port_set.
400  *
401  *		Doesn't remove members from the port set;
402  *		that happens lazily.  As members are removed,
403  *		their messages are removed from the queue.
404  *	Conditions:
405  *		The port_set is locked and alive.
406  *		The caller has a reference, which is consumed.
407  *		Afterwards, the port_set is unlocked and dead.
408  */
409 
410 void
ipc_pset_destroy(ipc_pset_t pset)411 ipc_pset_destroy(
412 	ipc_pset_t	pset)
413 {
414 	ipc_port_t port;
415 
416 	pset->ips_object.io_bits &= ~IO_BITS_ACTIVE;
417 	while (!TAILQ_EMPTY(&pset->ips_ports)) {
418 		port = TAILQ_FIRST(&pset->ips_ports);
419 		MPASS(port->ip_pset == pset);
420 		if (ip_lock_try(port) == 0) {
421 			ips_unlock(pset);
422 			ip_lock(port);
423 			ips_lock(pset);
424 		}
425 		TAILQ_REMOVE(&pset->ips_ports, port, ip_next);
426 		port->ip_pset = NULL;
427 		ip_unlock(port);
428 		ips_release(pset);
429 	}
430 	ipc_pset_changed(pset, MACH_RCV_PORT_DIED);
431 	ips_unlock(pset);
432 	ips_release(pset);	/* consume the ref our caller gave us */
433 }
434 
435 /**
436  *
437  * KQ handling
438  */
439 
440 #include <sys/file.h>
441 #include <sys/selinfo.h>
442 #include <sys/eventvar.h>
443 
444 void 	knote_enqueue(struct knote *kn);
445 
446 #define KQ_LOCK(kq) do {						\
447 	mtx_lock(&(kq)->kq_lock);					\
448 } while (0)
449 #define KQ_UNLOCK(kq) do {						\
450 	mtx_unlock(&(kq)->kq_lock);					\
451 } while (0)
452 
453 void
ipc_pset_signal(ipc_pset_t pset)454 ipc_pset_signal(ipc_pset_t pset)
455 {
456 	struct kqueue *kq, *kq_prev;
457 	struct knote *kn;
458 	struct knlist *list;
459 
460 	sx_slock(&pset->ips_note_lock);
461 	if (KNLIST_EMPTY(&pset->ips_note)) {
462 		sx_sunlock(&pset->ips_note_lock);
463 		return;
464 	}
465 	list = &pset->ips_note;
466 	kq = kq_prev = NULL;
467 	SLIST_FOREACH(kn, &list->kl_list, kn_selnext) {
468 		kq = kn->kn_kq;
469 		if (kq != kq_prev) {
470 			if (kq_prev)
471 				KQ_UNLOCK(kq_prev);
472 			KQ_LOCK(kq);
473 		}
474 		(kn)->kn_status |= KN_ACTIVE;
475 		if (((kn)->kn_status & (KN_QUEUED | KN_DISABLED)) == 0)
476 			knote_enqueue(kn);
477 		kq_prev = kq;
478 	}
479 	MPASS(kq != NULL);
480 	KQ_UNLOCK(kq);
481 	sx_sunlock(&pset->ips_note_lock);
482 }
483 
484 
485 static int      filt_machportattach(struct knote *kn);
486 static void     filt_machportdetach(struct knote *kn);
487 static int      filt_machport(struct knote *kn, long hint);
488 struct filterops machport_filtops = {
489 	.f_isfd = 1,
490 	.f_attach = filt_machportattach,
491 	.f_detach = filt_machportdetach,
492 	.f_event = filt_machport,
493 };
494 
495 static int
filt_machportattach(struct knote * kn)496 filt_machportattach(struct knote *kn)
497 {
498 	mach_port_name_t	name = (mach_port_name_t)kn->kn_kevent.ident;
499 	ipc_pset_t			pset = IPS_NULL;
500 	ipc_entry_t			entry;
501 	kern_return_t		kr;
502 	struct knlist		*note;
503 
504 	kr = ipc_object_translate(current_space(), name, MACH_PORT_RIGHT_PORT_SET,
505 							  (ipc_object_t *)&pset);
506 
507 	if (kr != KERN_SUCCESS)
508 		return (kr == KERN_INVALID_NAME ? ENOENT : ENOTSUP);
509 	note = &pset->ips_note;
510 	ips_unlock(pset);
511 
512 	/* need the actual entry for knote */
513 	if ((entry = ipc_entry_lookup(current_space(), name)) == NULL)
514 		return (ENOENT);
515 	KASSERT(entry->ie_object == (ipc_object_t)pset, ("entry->ie_object == pset"));
516 
517 	kn->kn_fp = entry->ie_fp;
518 	knlist_add(note, kn, 0);
519 	return (0);
520 }
521 
522 extern void kdb_backtrace(void);
523 
524 static void
filt_machportdetach(struct knote * kn)525 filt_machportdetach(struct knote *kn)
526 {
527 	mach_port_name_t	name = (mach_port_name_t)kn->kn_kevent.ident;
528 	ipc_pset_t		pset = IPS_NULL;
529 	ipc_entry_t entry = NULL;;
530 
531 
532 
533 	if (kn->kn_fp->f_type != DTYPE_MACH_IPC)
534 		goto fail;
535 
536 	entry = kn->kn_fp->f_data;
537 	if ((entry->ie_bits & MACH_PORT_TYPE_PORT_SET) == 0)
538 		goto fail;
539 	if ((pset = (ipc_pset_t)entry->ie_object) == NULL)
540 		goto fail;
541 
542 
543 	knlist_remove(&pset->ips_note, kn, 0);
544 	return;
545 fail:
546 	kdb_backtrace();
547 	printf("kqdetach fail for: %d pset: %p entry: %p\n", name, pset, entry);
548 }
549 
550 
551 static int
filt_machport(struct knote * kn,long hint)552 filt_machport(struct knote *kn, long hint)
553 {
554 
555 	mach_port_name_t        name = (mach_port_name_t)kn->kn_kevent.ident;
556 	ipc_entry_t				entry = kn->kn_fp->f_data;
557 	ipc_pset_t              pset = (ipc_pset_t) entry->ie_object;
558 	thread_t				self = current_thread();
559 	kern_return_t           kr;
560 	mach_msg_option_t	option;
561 	mach_msg_size_t		size;
562 
563 	if (hint == EV_EOF) {
564 		kn->kn_data = 0;
565 		kn->kn_flags |= (EV_EOF | EV_ONESHOT);
566 		return (1);
567 	} else if (hint == 0) {
568 
569 		kr = ipc_object_translate(current_space(), name, MACH_PORT_RIGHT_PORT_SET,
570 								  (ipc_object_t *)&pset);
571 		if (kr != KERN_SUCCESS || !ips_active(pset)) {
572 			kdb_backtrace();
573 			printf("%s: filt_machport kr=%d ips_active=%d name=%d\n", curproc->p_comm, kr, !!ips_active(pset), name);
574 			kn->kn_data = 0;
575 			kn->kn_flags |= (EV_EOF | EV_ONESHOT);
576 			return (1);
577 		}
578 
579 		ips_reference(pset);
580 
581 		if (pset != (ipc_pset_t)entry->ie_object)
582 			ips_unlock(pset);
583 
584 	} else
585 		panic("invalid hint %ld\n", hint);
586 
587 
588 	option = kn->kn_sfflags & (MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_LARGE_IDENTITY|
589 				   MACH_RCV_TRAILER_MASK|MACH_RCV_VOUCHER);
590 
591 	if (option & MACH_RCV_MSG) {
592 		self->ith_msg_addr = (mach_vm_address_t) kn->kn_ext[0];
593 		size = (mach_msg_size_t)kn->kn_ext[1];
594 		printf("%s:%d: filt_machport option: %d \n", curproc->p_comm, curproc->p_pid, option);
595 	} else {
596 		option = MACH_RCV_LARGE;
597 		self->ith_msg_addr = 0;
598 		size = 0;
599 	}
600 
601 	self->ith_object = (ipc_object_t)pset;
602 	self->ith_msize = size;
603 	self->ith_option = option;
604 	self->ith_scatter_list_size = 0;
605 	self->ith_receiver_name = MACH_PORT_NAME_NULL;
606 	option |= MACH_RCV_TIMEOUT;
607 	self->ith_state = MACH_RCV_IN_PROGRESS;
608 
609 
610 	ips_lock(pset);
611 	kr = ipc_mqueue_pset_receive(MACH_PORT_TYPE_PORT_SET, option, size,
612 							0/* immediate timeout */, self);
613 
614 	ips_unlock(pset);
615 	assert(kr == THREAD_NOT_WAITING);
616 	assert(self->ith_state != MACH_RCV_IN_PROGRESS);
617 	ips_release(pset);
618 
619 	if (self->ith_state == MACH_RCV_TIMED_OUT) {
620 		return (0);
621 	}
622 	if ((option & MACH_RCV_MSG) != MACH_RCV_MSG) {
623 		assert(self->ith_state == MACH_RCV_TOO_LARGE);
624 		assert(self->ith_kmsg == IKM_NULL);
625 		kn->kn_data = self->ith_receiver_name;
626 #ifdef __LP64__
627 		printf("%s:%d, receiver_name %ld\n", curproc->p_comm, curproc->p_pid, kn->kn_data);
628 #endif
629 		return (1);
630 	}
631 
632 	assert(option & MACH_RCV_MSG);
633 	kn->kn_ext[1] = self->ith_msize;
634 	kn->kn_data = MACH_PORT_NAME_NULL;
635 #ifdef __LP64__
636 	printf("%s:%d receive result size: %d to: %lx \n", curproc->p_comm, curproc->p_pid, self->ith_msize, self->ith_msg_addr);
637 #endif
638 	kn->kn_fflags = mach_msg_receive_results(self);
639 
640     if ((kn->kn_fflags == MACH_RCV_TOO_LARGE) &&
641 	    (option & MACH_RCV_LARGE_IDENTITY))
642 	    kn->kn_data = self->ith_receiver_name;
643 
644 	return (1);
645 }
646 
647 #if	MACH_KDB
648 #include <mach_kdb.h>
649 
650 #include <ddb/db_output.h>
651 
652 #define	printf	kdbprintf
653 
654 int
ipc_list_count(struct ipc_kmsg * base)655 ipc_list_count(
656 	struct ipc_kmsg *base)
657 {
658 	register int count = 0;
659 
660 	if (base) {
661 		struct ipc_kmsg *kmsg = base;
662 
663 		++count;
664 		while (kmsg && kmsg->ikm_next != base
665 			    && kmsg->ikm_next != IKM_BOGUS){
666 			kmsg = kmsg->ikm_next;
667 			++count;
668 		}
669 	}
670 	return(count);
671 }
672 
673 /*
674  *	Routine:	ipc_pset_print
675  *	Purpose:
676  *		Pretty-print a port set for kdb.
677  */
678 
679 void
ipc_pset_print(ipc_pset_t pset)680 ipc_pset_print(
681 	ipc_pset_t	pset)
682 {
683 	extern int indent;
684 
685 	printf("pset 0x%x\n", pset);
686 
687 	indent += 2;
688 
689 	ipc_object_print(&pset->ips_object);
690 	iprintf("local_name = 0x%x\n", pset->ips_local_name);
691 	iprintf("%d kmsgs => 0x%x",
692 		ipc_list_count(pset->ips_messages.imq_messages.ikmq_base),
693 		pset->ips_messages.imq_messages.ikmq_base);
694 	printf(",rcvrs = 0x%x\n", pset->ips_messages.imq_threads.ithq_base);
695 
696 	indent -=2;
697 }
698 
699 #endif	/* MACH_KDB */
700