2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
34 * All Rights Reserved.
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
46 * Carnegie Mellon requests users of this software to return to
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
59 * File: ipc/ipc_pset.c
63 * Functions to manipulate IPC port sets.
66 #include <mach/port.h>
67 #include <mach/kern_return.h>
68 #include <mach/message.h>
69 #include <ipc/ipc_mqueue.h>
70 #include <ipc/ipc_object.h>
71 #include <ipc/ipc_pset.h>
72 #include <ipc/ipc_right.h>
73 #include <ipc/ipc_space.h>
74 #include <ipc/ipc_port.h>
75 #include <ipc/ipc_print.h>
77 #include <kern/kern_types.h>
80 #include <vm/vm_map.h>
83 * Routine: ipc_pset_alloc
85 * Allocate a port set.
87 * Nothing locked. If successful, the port set is returned
88 * locked. (The caller doesn't have a reference.)
90 * KERN_SUCCESS The port set is allocated.
91 * KERN_INVALID_TASK The space is dead.
92 * KERN_NO_SPACE No room for an entry in the space.
93 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
99 mach_port_name_t
*namep
,
103 mach_port_name_t name
;
106 kr
= ipc_object_alloc(space
, IOT_PORT_SET
,
107 MACH_PORT_TYPE_PORT_SET
, 0,
108 &name
, (ipc_object_t
*) &pset
);
109 if (kr
!= KERN_SUCCESS
)
113 pset
->ips_local_name
= name
;
114 ipc_mqueue_init(&pset
->ips_messages
, TRUE
/* set */);
122 * Routine: ipc_pset_alloc_name
124 * Allocate a port set, with a specific name.
126 * Nothing locked. If successful, the port set is returned
127 * locked. (The caller doesn't have a reference.)
129 * KERN_SUCCESS The port set is allocated.
130 * KERN_INVALID_TASK The space is dead.
131 * KERN_NAME_EXISTS The name already denotes a right.
132 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
138 mach_port_name_t name
,
145 kr
= ipc_object_alloc_name(space
, IOT_PORT_SET
,
146 MACH_PORT_TYPE_PORT_SET
, 0,
147 name
, (ipc_object_t
*) &pset
);
148 if (kr
!= KERN_SUCCESS
)
152 pset
->ips_local_name
= name
;
153 ipc_mqueue_init(&pset
->ips_messages
, TRUE
/* set */);
160 * Routine: ipc_pset_member
162 * Checks to see if a port is a member of a pset
164 * Both port and port set are locked.
165 * The port must be active.
172 assert(ip_active(port
));
174 return (ipc_mqueue_member(&port
->ip_messages
, &pset
->ips_messages
));
179 * Routine: ipc_pset_add
181 * Puts a port into a port set.
183 * Both port and port set are locked and active.
184 * The owner of the port set is also receiver for the port.
194 assert(ips_active(pset
));
195 assert(ip_active(port
));
197 kr
= ipc_mqueue_add(&port
->ip_messages
, &pset
->ips_messages
);
199 if (kr
== KERN_SUCCESS
)
200 port
->ip_pset_count
++;
208 * Routine: ipc_pset_remove
210 * Removes a port from a port set.
211 * The port set loses a reference.
213 * Both port and port set are locked.
214 * The port must be active.
224 assert(ip_active(port
));
226 if (port
->ip_pset_count
== 0)
227 return KERN_NOT_IN_SET
;
229 kr
= ipc_mqueue_remove(&port
->ip_messages
, &pset
->ips_messages
);
231 if (kr
== KERN_SUCCESS
)
232 port
->ip_pset_count
--;
238 * Routine: ipc_pset_remove_from_all
240 * Removes a port from all it's port sets.
242 * port is locked and active.
246 ipc_pset_remove_from_all(
249 assert(ip_active(port
));
251 if (port
->ip_pset_count
== 0)
252 return KERN_NOT_IN_SET
;
255 * Remove the port's mqueue from all sets
257 ipc_mqueue_remove_from_all(&port
->ip_messages
);
258 port
->ip_pset_count
= 0;
264 * Routine: ipc_pset_destroy
266 * Destroys a port_set.
268 * The port_set is locked and alive.
269 * The caller has a reference, which is consumed.
270 * Afterwards, the port_set is unlocked and dead.
279 assert(ips_active(pset
));
281 pset
->ips_object
.io_bits
&= ~IO_BITS_ACTIVE
;
284 * remove all the member message queues
286 ipc_mqueue_remove_all(&pset
->ips_messages
);
289 * Set all waiters on the portset running to
290 * discover the change.
293 imq_lock(&pset
->ips_messages
);
294 ipc_mqueue_changed(&pset
->ips_messages
);
295 imq_unlock(&pset
->ips_messages
);
298 ips_release(pset
); /* consume the ref our caller gave us */
299 ips_check_unlock(pset
);
302 /* Kqueue EVFILT_MACHPORT support */
304 #include <sys/errno.h>
306 static int filt_machportattach(struct knote
*kn
);
307 static void filt_machportdetach(struct knote
*kn
);
308 static int filt_machport(struct knote
*kn
, long hint
);
309 static void filt_machporttouch(struct knote
*kn
, struct kevent64_s
*kev
, long type
);
310 static unsigned filt_machportpeek(struct knote
*kn
);
311 struct filterops machport_filtops
= {
312 .f_attach
= filt_machportattach
,
313 .f_detach
= filt_machportdetach
,
314 .f_event
= filt_machport
,
315 .f_touch
= filt_machporttouch
,
316 .f_peek
= filt_machportpeek
,
323 mach_port_name_t name
= (mach_port_name_t
)kn
->kn_kevent
.ident
;
324 ipc_pset_t pset
= IPS_NULL
;
328 kr
= ipc_object_translate(current_space(), name
,
329 MACH_PORT_RIGHT_PORT_SET
,
330 (ipc_object_t
*)&pset
);
331 if (kr
!= KERN_SUCCESS
) {
332 result
= (kr
== KERN_INVALID_NAME
? ENOENT
: ENOTSUP
);
335 /* We've got a lock on pset */
337 /* keep a reference for the knote */
338 kn
->kn_ptr
.p_pset
= pset
;
342 * Bind the portset wait queue directly to knote/kqueue.
343 * This allows us to just use wait_queue foo to effect a wakeup,
344 * rather than having to call knote() from the Mach code on each
347 result
= knote_link_wait_queue(kn
, &pset
->ips_messages
.imq_wait_queue
);
357 ipc_pset_t pset
= kn
->kn_ptr
.p_pset
;
360 * Unlink the portset wait queue from knote/kqueue,
361 * and release our reference on the portset.
364 knote_unlink_wait_queue(kn
, &pset
->ips_messages
.imq_wait_queue
);
365 ips_release(kn
->kn_ptr
.p_pset
);
366 kn
->kn_ptr
.p_pset
= IPS_NULL
;
367 ips_check_unlock(pset
);
375 mach_port_name_t name
= (mach_port_name_t
)kn
->kn_kevent
.ident
;
376 ipc_pset_t pset
= IPS_NULL
;
377 wait_result_t wresult
;
378 thread_t self
= current_thread();
380 mach_msg_option_t option
;
381 mach_msg_size_t size
;
383 /* never called from below */
387 * called from user context. Have to validate the
388 * name. If it changed, we have an EOF situation.
390 kr
= ipc_object_translate(current_space(), name
,
391 MACH_PORT_RIGHT_PORT_SET
,
392 (ipc_object_t
*)&pset
);
393 if (kr
!= KERN_SUCCESS
|| pset
!= kn
->kn_ptr
.p_pset
|| !ips_active(pset
)) {
395 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
396 if (pset
!= IPS_NULL
)
397 ips_check_unlock(pset
);
401 /* just use the reference from here on out */
406 * Only honor supported receive options. If no options are
407 * provided, just force a MACH_RCV_TOO_LARGE to detect the
408 * name of the port and sizeof the waiting message.
410 option
= kn
->kn_sfflags
& (MACH_RCV_MSG
|MACH_RCV_LARGE
|MACH_RCV_TRAILER_MASK
);
411 if (option
& MACH_RCV_MSG
) {
412 self
->ith_msg_addr
= (mach_vm_address_t
) kn
->kn_ext
[0];
413 size
= (mach_msg_size_t
)kn
->kn_ext
[1];
415 option
= MACH_RCV_LARGE
;
416 self
->ith_msg_addr
= 0;
421 * Set up to receive a message or the notification of a
422 * too large message. But never allow this call to wait.
423 * If the user provided aditional options, like trailer
424 * options, pass those through here. But we don't support
425 * scatter lists through this interface.
427 self
->ith_object
= (ipc_object_t
)pset
;
428 self
->ith_msize
= size
;
429 self
->ith_option
= option
;
430 self
->ith_scatter_list_size
= 0;
431 self
->ith_receiver_name
= MACH_PORT_NULL
;
432 self
->ith_continuation
= NULL
;
433 option
|= MACH_RCV_TIMEOUT
; // never wait
434 assert((self
->ith_state
= MACH_RCV_IN_PROGRESS
) == MACH_RCV_IN_PROGRESS
);
436 wresult
= ipc_mqueue_receive_on_thread(
440 0, /* immediate timeout */
441 THREAD_INTERRUPTIBLE
,
443 assert(wresult
== THREAD_NOT_WAITING
);
444 assert(self
->ith_state
!= MACH_RCV_IN_PROGRESS
);
447 * If we timed out, just release the reference on the
448 * portset and return zero.
450 if (self
->ith_state
== MACH_RCV_TIMED_OUT
) {
451 ipc_pset_release(pset
);
456 * If we weren't attempting to receive a message
457 * directly, we need to return the port name in
458 * the kevent structure.
460 if ((option
& MACH_RCV_MSG
) != MACH_RCV_MSG
) {
461 assert(self
->ith_state
== MACH_RCV_TOO_LARGE
);
462 assert(self
->ith_kmsg
== IKM_NULL
);
463 kn
->kn_data
= self
->ith_receiver_name
;
464 ipc_pset_release(pset
);
469 * Attempt to receive the message directly, returning
470 * the results in the fflags field.
472 assert(option
& MACH_RCV_MSG
);
473 kn
->kn_data
= MACH_PORT_NULL
;
474 kn
->kn_ext
[1] = self
->ith_msize
;
475 kn
->kn_fflags
= mach_msg_receive_results();
476 /* kmsg and pset reference consumed */
481 filt_machporttouch(struct knote
*kn
, struct kevent64_s
*kev
, long type
)
485 kn
->kn_sfflags
= kev
->fflags
;
486 kn
->kn_sdata
= kev
->data
;
489 *kev
= kn
->kn_kevent
;
490 if (kn
->kn_flags
& EV_CLEAR
) {
496 panic("filt_machporttouch() - invalid type (%ld)", type
);
502 * Peek to see if the portset associated with the knote has any
503 * events. This pre-hook is called when a filter uses the stay-
504 * on-queue mechanism (as the knote_link_wait_queue mechanism
507 * This is called with the kqueue that the knote belongs to still
508 * locked (thus holding a reference on the knote, but restricting
509 * also restricting our ability to take other locks).
511 * Just peek at the pre-post status of the portset's wait queue
512 * to determine if it has anything interesting. We can do it
513 * without holding the lock, as it is just a snapshot in time
514 * (if this is used as part of really waiting for events, we
515 * will catch changes in this status when the event gets posted
516 * up to the knote's kqueue).
519 filt_machportpeek(struct knote
*kn
)
521 ipc_pset_t pset
= kn
->kn_ptr
.p_pset
;
522 ipc_mqueue_t set_mq
= &pset
->ips_messages
;
524 return (ipc_mqueue_peek(set_mq
));
528 #include <mach_kdb.h>
531 #include <ddb/db_output.h>
533 #define printf kdbprintf
537 struct ipc_kmsg
*base
)
539 register int count
= 0;
542 struct ipc_kmsg
*kmsg
= base
;
545 while (kmsg
&& kmsg
->ikm_next
!= base
546 && kmsg
->ikm_next
!= IKM_BOGUS
){
547 kmsg
= kmsg
->ikm_next
;
555 * Routine: ipc_pset_print
557 * Pretty-print a port set for kdb.
563 printf("pset 0x%x\n", pset
);
567 ipc_object_print(&pset
->ips_object
);
568 iprintf("local_name = 0x%x\n", pset
->ips_local_name
);
569 iprintf("%d kmsgs => 0x%x",
570 ipc_list_count(pset
->ips_messages
.imq_messages
.ikmq_base
),
571 pset
->ips_messages
.imq_messages
.ikmq_base
);
572 printf(",rcvrs queue= 0x%x\n", &pset
->ips_messages
.imq_wait_queue
);
577 #endif /* MACH_KDB */