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>
76 #include <kern/kern_types.h>
78 #include <vm/vm_map.h>
79 #include <libkern/section_keywords.h>
82 * Routine: ipc_pset_alloc
84 * Allocate a port set.
86 * Nothing locked. If successful, the port set is returned
87 * locked. (The caller doesn't have a reference.)
89 * KERN_SUCCESS The port set is allocated.
90 * KERN_INVALID_TASK The space is dead.
91 * KERN_NO_SPACE No room for an entry in the space.
92 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
98 mach_port_name_t
*namep
,
102 mach_port_name_t name
;
104 uint64_t reserved_link
;
106 reserved_link
= waitq_link_reserve(NULL
);
108 kr
= ipc_object_alloc(space
, IOT_PORT_SET
,
109 MACH_PORT_TYPE_PORT_SET
, 0,
110 &name
, (ipc_object_t
*) &pset
);
111 if (kr
!= KERN_SUCCESS
) {
112 waitq_link_release(reserved_link
);
115 /* pset and space are locked */
117 ipc_mqueue_init(&pset
->ips_messages
, TRUE
/* set */, &reserved_link
);
118 is_write_unlock(space
);
120 waitq_link_release(reserved_link
);
128 * Routine: ipc_pset_alloc_name
130 * Allocate a port set, with a specific name.
132 * Nothing locked. If successful, the port set is returned
133 * locked. (The caller doesn't have a reference.)
135 * KERN_SUCCESS The port set is allocated.
136 * KERN_INVALID_TASK The space is dead.
137 * KERN_NAME_EXISTS The name already denotes a right.
138 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
144 mach_port_name_t name
,
149 uint64_t reserved_link
;
152 reserved_link
= waitq_link_reserve(NULL
);
154 kr
= ipc_object_alloc_name(space
, IOT_PORT_SET
,
155 MACH_PORT_TYPE_PORT_SET
, 0,
156 name
, (ipc_object_t
*) &pset
);
157 if (kr
!= KERN_SUCCESS
) {
158 waitq_link_release(reserved_link
);
163 ipc_mqueue_init(&pset
->ips_messages
, TRUE
/* set */, &reserved_link
);
165 waitq_link_release(reserved_link
);
173 * Routine: ipc_pset_alloc_special
175 * Allocate a port set in a special space.
176 * The new port set is returned with one ref.
177 * If unsuccessful, IPS_NULL is returned.
182 ipc_pset_alloc_special(
183 __assert_only ipc_space_t space
)
186 uint64_t reserved_link
;
188 assert(space
!= IS_NULL
);
189 assert(space
->is_table
== IE_NULL
);
190 assert(!is_active(space
));
192 reserved_link
= waitq_link_reserve(NULL
);
194 __IGNORE_WCASTALIGN(pset
= (ipc_pset_t
)io_alloc(IOT_PORT_SET
));
195 if (pset
== IPS_NULL
)
198 bzero((char *)pset
, sizeof(*pset
));
200 io_lock_init(&pset
->ips_object
);
201 pset
->ips_references
= 1;
202 pset
->ips_object
.io_bits
= io_makebits(TRUE
, IOT_PORT_SET
, 0);
204 ipc_mqueue_init(&pset
->ips_messages
, TRUE
/* set */, &reserved_link
);
206 waitq_link_release(reserved_link
);
213 * Routine: ipc_pset_member
215 * Checks to see if a port is a member of a pset
217 * Both port and port set are locked.
218 * The port must be active.
225 assert(ip_active(port
));
227 return (ipc_mqueue_member(&port
->ip_messages
, &pset
->ips_messages
));
232 * Routine: ipc_pset_add
234 * Puts a port into a port set.
236 * Both port and port set are locked and active.
237 * The owner of the port set is also receiver for the port.
244 uint64_t *reserved_link
,
245 uint64_t *reserved_prepost
)
249 assert(ips_active(pset
));
250 assert(ip_active(port
));
252 kr
= ipc_mqueue_add(&port
->ip_messages
, &pset
->ips_messages
,
253 reserved_link
, reserved_prepost
);
261 * Routine: ipc_pset_remove
263 * Removes a port from a port set.
264 * The port set loses a reference.
266 * Both port and port set are locked.
267 * The port must be active.
277 assert(ip_active(port
));
279 if (port
->ip_in_pset
== 0)
280 return KERN_NOT_IN_SET
;
282 kr
= ipc_mqueue_remove(&port
->ip_messages
, &pset
->ips_messages
);
288 * Routine: ipc_pset_remove_from_all
290 * Removes a port from all it's port sets.
292 * port is locked and active.
296 ipc_pset_remove_from_all(
299 if (port
->ip_in_pset
== 0)
300 return KERN_NOT_IN_SET
;
303 * Remove the port's mqueue from all sets
305 ipc_mqueue_remove_from_all(&port
->ip_messages
);
311 * Routine: ipc_pset_destroy
313 * Destroys a port_set.
315 * The port_set is locked and alive.
316 * The caller has a reference, which is consumed.
317 * Afterwards, the port_set is unlocked and dead.
324 assert(ips_active(pset
));
326 pset
->ips_object
.io_bits
&= ~IO_BITS_ACTIVE
;
329 * remove all the member message queues
330 * AND remove this message queue from any containing sets
332 ipc_mqueue_remove_all(&pset
->ips_messages
);
335 * Set all waiters on the portset running to
336 * discover the change.
338 imq_lock(&pset
->ips_messages
);
339 ipc_mqueue_changed(&pset
->ips_messages
);
340 imq_unlock(&pset
->ips_messages
);
342 ipc_mqueue_deinit(&pset
->ips_messages
);
345 ips_release(pset
); /* consume the ref our caller gave us */
348 /* Kqueue EVFILT_MACHPORT support */
350 #include <sys/event.h>
351 #include <sys/errno.h>
353 static int filt_machportattach(struct knote
*kn
, struct kevent_internal_s
*kev
);
354 static void filt_machportdetach(struct knote
*kn
);
355 static int filt_machport(struct knote
*kn
, long hint
);
356 static int filt_machporttouch(struct knote
*kn
, struct kevent_internal_s
*kev
);
357 static int filt_machportprocess(struct knote
*kn
, struct filt_process_s
*data
, struct kevent_internal_s
*kev
);
358 static unsigned filt_machportpeek(struct knote
*kn
);
359 SECURITY_READ_ONLY_EARLY(struct filterops
) machport_filtops
= {
361 .f_attach
= filt_machportattach
,
362 .f_detach
= filt_machportdetach
,
363 .f_event
= filt_machport
,
364 .f_touch
= filt_machporttouch
,
365 .f_process
= filt_machportprocess
,
366 .f_peek
= filt_machportpeek
,
372 __unused
struct kevent_internal_s
*kev
)
374 mach_port_name_t name
= (mach_port_name_t
)kn
->kn_kevent
.ident
;
375 uint64_t wq_link_id
= waitq_link_reserve(NULL
);
376 ipc_space_t space
= current_space();
385 kr
= ipc_right_lookup_read(space
, name
, &entry
);
386 if (kr
== KERN_SUCCESS
) {
387 /* space is read-locked and active */
389 if (entry
->ie_bits
& MACH_PORT_TYPE_PORT_SET
) {
392 __IGNORE_WCASTALIGN(pset
= (ipc_pset_t
)entry
->ie_object
);
393 mqueue
= &pset
->ips_messages
;
397 kn
->kn_ptr
.p_mqueue
= mqueue
;
400 * Bind the portset wait queue directly to knote/kqueue.
401 * This allows us to just use wait_queue foo to effect a wakeup,
402 * rather than having to call knote() from the Mach code on each
403 * message. We still attach the knote to the mqueue klist for
404 * NOTE_REVOKE purposes only.
406 error
= knote_link_waitq(kn
, &mqueue
->imq_wait_queue
, &wq_link_id
);
408 KNOTE_ATTACH(&mqueue
->imq_klist
, kn
);
413 kn
->kn_ptr
.p_mqueue
= IMQ_NULL
;
418 is_read_unlock(space
);
421 * linked knotes are marked stay-active and therefore don't
422 * need an indication of their fired state to be returned
423 * from the attach operation.
426 } else if (entry
->ie_bits
& MACH_PORT_TYPE_RECEIVE
) {
429 __IGNORE_WCASTALIGN(port
= (ipc_port_t
)entry
->ie_object
);
430 mqueue
= &port
->ip_messages
;
434 * attach knote to port and determine result
435 * If the filter requested direct message receipt,
436 * we may need to adjust the qos of the knote to
437 * reflect the requested and override qos of the
438 * first message in the queue.
441 kn
->kn_ptr
.p_mqueue
= mqueue
;
442 KNOTE_ATTACH(&mqueue
->imq_klist
, kn
);
443 if ((first
= ipc_kmsg_queue_first(&mqueue
->imq_messages
)) != IKM_NULL
) {
444 int sync_qos_override_index
= ipc_port_get_max_sync_qos_index(port
);
445 if (kn
->kn_sfflags
& MACH_RCV_MSG
)
446 knote_adjust_qos(kn
, first
->ikm_qos
, first
->ikm_qos_override
,
447 sync_qos_override_index
);
452 is_read_unlock(space
);
455 is_read_unlock(space
);
462 waitq_link_release(wq_link_id
);
464 /* bail out on errors */
466 kn
->kn_flags
|= EV_ERROR
;
474 /* NOT proud of these - we should have a stricter relationship between mqueue and ipc object */
475 #define mqueue_to_pset(mq) ((ipc_pset_t)((uintptr_t)mq-offsetof(struct ipc_pset, ips_messages)))
476 #define mqueue_to_port(mq) ((ipc_port_t)((uintptr_t)mq-offsetof(struct ipc_port, ip_messages)))
477 #define mqueue_to_object(mq) (((ipc_object_t)(mq)) - 1)
484 ipc_mqueue_t mqueue
= kn
->kn_ptr
.p_mqueue
;
485 ipc_object_t object
= mqueue_to_object(mqueue
);
488 KNOTE_DETACH(&mqueue
->imq_klist
, kn
);
489 kn
->kn_ptr
.p_mqueue
= IMQ_NULL
;
492 if (io_otype(object
) == IOT_PORT_SET
) {
494 * Unlink the portset wait queue from knote/kqueue.
495 * JMM - Does this need to be atomic under the mq lock?
497 (void)knote_unlink_waitq(kn
, &mqueue
->imq_wait_queue
);
503 * filt_machport - deliver events into the mach port filter
505 * Mach port message arrival events are currently only posted via the
506 * kqueue filter routine for ports. Port sets are marked stay-active
507 * and the wait queue code will break any kqueue waiters out to go
508 * poll the stay-queued knotes again.
510 * If there is a message at the head of the queue,
511 * we indicate that the knote should go active. If
512 * the message is to be direct-received, we adjust the
513 * QoS of the knote according the requested and override
514 * QoS of that first message.
516 * NOTE_REVOKE events are a legacy way to indicate that the port/portset
517 * was deallocated or left the current Mach portspace (modern technique
518 * is with an EV_VANISHED protocol). If we see NOTE_REVOKE, deliver an
519 * EV_EOF event for these changes (hopefully it will get delivered before
520 * the port name recycles to the same generation count and someone tries
521 * to re-register a kevent for it or the events are udata-specific -
522 * avoiding a conflict).
529 ipc_mqueue_t mqueue
= kn
->kn_ptr
.p_mqueue
;
533 /* mqueue locked by caller */
534 assert(imq_held(mqueue
));
536 if (hint
== NOTE_REVOKE
) {
537 kn
->kn_flags
|= EV_EOF
| EV_ONESHOT
;
539 } else if (imq_is_valid(mqueue
)) {
540 assert(!imq_is_set(mqueue
));
541 if ((first
= ipc_kmsg_queue_first(&mqueue
->imq_messages
)) != IKM_NULL
) {
542 ipc_port_t port
= ip_from_mq(mqueue
);
543 int sync_qos_override_index
= ipc_port_get_max_sync_qos_index(port
);
545 if (kn
->kn_sfflags
& MACH_RCV_MSG
)
546 knote_adjust_qos(kn
, first
->ikm_qos
, first
->ikm_qos_override
,
547 sync_qos_override_index
);
558 struct kevent_internal_s
*kev
)
560 ipc_mqueue_t mqueue
= kn
->kn_ptr
.p_mqueue
;
566 /* copy in new settings and save off new input fflags */
567 kn
->kn_sfflags
= kev
->fflags
;
568 kn
->kn_ext
[0] = kev
->ext
[0];
569 kn
->kn_ext
[1] = kev
->ext
[1];
570 if ((kn
->kn_status
& KN_UDATA_SPECIFIC
) == 0)
571 kn
->kn_udata
= kev
->udata
;
574 * If the mqueue is a valid port and there is a message
575 * that will be direct-received from the knote, update
576 * the knote qos based on the first message and trigger
577 * the event. If there are no more messages, reset the
578 * QoS to the value provided by the kevent.
580 if (imq_is_valid(mqueue
) && !imq_is_set(mqueue
) &&
581 (first
= ipc_kmsg_queue_first(&mqueue
->imq_messages
)) != IKM_NULL
) {
582 ipc_port_t port
= ip_from_mq(mqueue
);
583 int sync_qos_override_index
= ipc_port_get_max_sync_qos_index(port
);
585 if (kn
->kn_sfflags
& MACH_RCV_MSG
)
586 knote_adjust_qos(kn
, first
->ikm_qos
, first
->ikm_qos_override
,
587 sync_qos_override_index
);
589 } else if (kn
->kn_sfflags
& MACH_RCV_MSG
) {
591 MACH_MSG_PRIORITY_UNSPECIFIED
,
592 MACH_MSG_PRIORITY_UNSPECIFIED
,
593 THREAD_QOS_UNSPECIFIED
);
601 filt_machportprocess(
603 struct filt_process_s
*process_data
,
604 struct kevent_internal_s
*kev
)
606 ipc_mqueue_t mqueue
= kn
->kn_ptr
.p_mqueue
;
607 ipc_object_t object
= mqueue_to_object(mqueue
);
608 thread_t self
= current_thread();
609 boolean_t used_filtprocess_data
= FALSE
;
611 wait_result_t wresult
;
612 mach_msg_option_t option
;
613 mach_vm_address_t addr
;
614 mach_msg_size_t size
;
618 /* Capture current state */
619 *kev
= kn
->kn_kevent
;
621 /* If already deallocated/moved return one last EOF event */
622 if (kev
->flags
& EV_EOF
) {
628 * Only honor supported receive options. If no options are
629 * provided, just force a MACH_RCV_TOO_LARGE to detect the
630 * name of the port and sizeof the waiting message.
632 option
= kn
->kn_sfflags
& (MACH_RCV_MSG
|MACH_RCV_LARGE
|MACH_RCV_LARGE_IDENTITY
|
633 MACH_RCV_TRAILER_MASK
|MACH_RCV_VOUCHER
);
635 if (option
& MACH_RCV_MSG
) {
636 addr
= (mach_vm_address_t
) kn
->kn_ext
[0];
637 size
= (mach_msg_size_t
) kn
->kn_ext
[1];
640 * If the kevent didn't specify a buffer and length, carve a buffer
641 * from the filter processing data according to the flags.
643 if (size
== 0 && process_data
!= NULL
) {
644 used_filtprocess_data
= TRUE
;
646 addr
= (mach_vm_address_t
)process_data
->fp_data_out
;
647 size
= (mach_msg_size_t
)process_data
->fp_data_resid
;
648 option
|= (MACH_RCV_LARGE
| MACH_RCV_LARGE_IDENTITY
);
649 if (process_data
->fp_flags
& KEVENT_FLAG_STACK_DATA
)
650 option
|= MACH_RCV_STACK
;
653 /* just detect the port name (if a set) and size of the first message */
654 option
= MACH_RCV_LARGE
;
659 /* just use the reference from here on out */
660 io_reference(object
);
663 * Set up to receive a message or the notification of a
664 * too large message. But never allow this call to wait.
665 * If the user provided aditional options, like trailer
666 * options, pass those through here. But we don't support
667 * scatter lists through this interface.
669 self
->ith_object
= object
;
670 self
->ith_msg_addr
= addr
;
671 self
->ith_rsize
= size
;
673 self
->ith_option
= option
;
674 self
->ith_receiver_name
= MACH_PORT_NULL
;
675 self
->ith_continuation
= NULL
;
676 option
|= MACH_RCV_TIMEOUT
; // never wait
677 self
->ith_state
= MACH_RCV_IN_PROGRESS
;
678 self
->ith_knote
= kn
;
680 wresult
= ipc_mqueue_receive_on_thread(
684 0, /* immediate timeout */
685 THREAD_INTERRUPTIBLE
,
687 /* mqueue unlocked */
690 * If we timed out, or the process is exiting, just release the
691 * reference on the ipc_object and return zero.
693 if (wresult
== THREAD_RESTART
|| self
->ith_state
== MACH_RCV_TIMED_OUT
) {
698 assert(wresult
== THREAD_NOT_WAITING
);
699 assert(self
->ith_state
!= MACH_RCV_IN_PROGRESS
);
702 * If we weren't attempting to receive a message
703 * directly, we need to return the port name in
704 * the kevent structure.
706 if ((option
& MACH_RCV_MSG
) != MACH_RCV_MSG
) {
707 assert(self
->ith_state
== MACH_RCV_TOO_LARGE
);
708 assert(self
->ith_kmsg
== IKM_NULL
);
709 kev
->data
= self
->ith_receiver_name
;
715 * Attempt to receive the message directly, returning
716 * the results in the fflags field.
718 kev
->fflags
= mach_msg_receive_results(&size
);
720 /* kmsg and object reference consumed */
723 * if the user asked for the identity of ports containing a
724 * a too-large message, return it in the data field (as we
725 * do for messages we didn't try to receive).
727 if (kev
->fflags
== MACH_RCV_TOO_LARGE
) {
728 kev
->ext
[1] = self
->ith_msize
;
729 if (option
& MACH_RCV_LARGE_IDENTITY
)
730 kev
->data
= self
->ith_receiver_name
;
732 kev
->data
= MACH_PORT_NULL
;
735 kev
->data
= MACH_PORT_NULL
;
739 * If we used a data buffer carved out from the filt_process data,
740 * store the address used in the knote and adjust the residual and
741 * other parameters for future use.
743 if (used_filtprocess_data
) {
744 assert(process_data
->fp_data_resid
>= size
);
745 process_data
->fp_data_resid
-= size
;
746 if ((process_data
->fp_flags
& KEVENT_FLAG_STACK_DATA
) == 0) {
747 kev
->ext
[0] = process_data
->fp_data_out
;
748 process_data
->fp_data_out
+= size
;
750 assert(option
& MACH_RCV_STACK
);
751 kev
->ext
[0] = process_data
->fp_data_out
+
752 process_data
->fp_data_resid
;
757 * Apply message-based QoS values to output kevent as prescribed.
758 * The kev->qos field gets max(msg-qos, kn->kn_qos).
759 * The kev->ext[2] field gets (msg-qos << 32) | (override-qos).
761 * The mach_msg_receive_results() call saved off the message
762 * QoS values in the continuation save area on successful receive.
764 if (kev
->fflags
== MACH_MSG_SUCCESS
) {
765 kev
->qos
= mach_msg_priority_combine(self
->ith_qos
, kn
->kn_qos
);
766 kev
->ext
[2] = ((uint64_t)self
->ith_qos
<< 32) |
767 (uint64_t)self
->ith_qos_override
;
774 * Peek to see if the message queue associated with the knote has any
775 * events. This pre-hook is called when a filter uses the stay-
776 * on-queue mechanism (as the knote_link_waitq mechanism does for
777 * portsets) and someone calls select() against the containing kqueue.
779 * Just peek at the pre-post status of the portset's wait queue
780 * to determine if it has anything interesting. We can do it
781 * without holding the lock, as it is just a snapshot in time
782 * (if this is used as part of really waiting for events, we
783 * will catch changes in this status when the event gets posted
784 * up to the knote's kqueue).
787 filt_machportpeek(struct knote
*kn
)
789 ipc_mqueue_t mqueue
= kn
->kn_ptr
.p_mqueue
;
791 return (ipc_mqueue_set_peek(mqueue
));