X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/9bccf70c0258c7cac2dcb80011b2a964d884c552..b7266188b87f3620ec3f9f717e57194a7dd989fe:/osfmk/ipc/ipc_pset.c diff --git a/osfmk/ipc/ipc_pset.c b/osfmk/ipc/ipc_pset.c index 0befd406c..4e0dd2b68 100644 --- a/osfmk/ipc/ipc_pset.c +++ b/osfmk/ipc/ipc_pset.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_COPYRIGHT@ @@ -68,6 +74,11 @@ #include #include +#include +#include + +#include + /* * Routine: ipc_pset_alloc * Purpose: @@ -235,8 +246,6 @@ kern_return_t ipc_pset_remove_from_all( ipc_port_t port) { - ipc_pset_t pset; - assert(ip_active(port)); if (port->ip_pset_count == 0) @@ -276,18 +285,246 @@ ipc_pset_destroy( */ ipc_mqueue_remove_all(&pset->ips_messages); + /* + * Set all waiters on the portset running to + * discover the change. + */ s = splsched(); imq_lock(&pset->ips_messages); ipc_mqueue_changed(&pset->ips_messages); imq_unlock(&pset->ips_messages); splx(s); - /* XXXX Perhaps ought to verify ips_thread_pool is empty */ - ips_release(pset); /* consume the ref our caller gave us */ ips_check_unlock(pset); } +/* Kqueue EVFILT_MACHPORT support */ + +#include + +static int filt_machportattach(struct knote *kn); +static void filt_machportdetach(struct knote *kn); +static int filt_machport(struct knote *kn, long hint); +static void filt_machporttouch(struct knote *kn, struct kevent64_s *kev, long type); +static int filt_machportpeek(struct knote *kn); +struct filterops machport_filtops = { + .f_attach = filt_machportattach, + .f_detach = filt_machportdetach, + .f_event = filt_machport, + .f_touch = filt_machporttouch, + .f_peek = filt_machportpeek, +}; + +static int +filt_machportattach( + struct knote *kn) +{ + mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident; + ipc_pset_t pset = IPS_NULL; + int result = ENOSYS; + kern_return_t kr; + + kr = ipc_object_translate(current_space(), name, + MACH_PORT_RIGHT_PORT_SET, + (ipc_object_t *)&pset); + if (kr != KERN_SUCCESS) { + result = (kr == KERN_INVALID_NAME ? ENOENT : ENOTSUP); + goto done; + } + /* We've got a lock on pset */ + + /* keep a reference for the knote */ + kn->kn_ptr.p_pset = pset; + ips_reference(pset); + + /* + * Bind the portset wait queue directly to knote/kqueue. + * This allows us to just use wait_queue foo to effect a wakeup, + * rather than having to call knote() from the Mach code on each + * message. + */ + result = knote_link_wait_queue(kn, &pset->ips_messages.imq_wait_queue); + ips_unlock(pset); +done: + return result; +} + +static void +filt_machportdetach( + struct knote *kn) +{ + ipc_pset_t pset = kn->kn_ptr.p_pset; + + /* + * Unlink the portset wait queue from knote/kqueue, + * and release our reference on the portset. + */ + ips_lock(pset); + knote_unlink_wait_queue(kn, &pset->ips_messages.imq_wait_queue); + ips_release(kn->kn_ptr.p_pset); + kn->kn_ptr.p_pset = IPS_NULL; + ips_check_unlock(pset); +} + +static int +filt_machport( + struct knote *kn, + __unused long hint) +{ + mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident; + ipc_pset_t pset = IPS_NULL; + wait_result_t wresult; + thread_t self = current_thread(); + kern_return_t kr; + mach_msg_option_t option; + mach_msg_size_t size; + + /* never called from below */ + assert(hint == 0); + + /* + * called from user context. Have to validate the + * name. If it changed, we have an EOF situation. + */ + kr = ipc_object_translate(current_space(), name, + MACH_PORT_RIGHT_PORT_SET, + (ipc_object_t *)&pset); + if (kr != KERN_SUCCESS || pset != kn->kn_ptr.p_pset || !ips_active(pset)) { + kn->kn_data = 0; + kn->kn_flags |= (EV_EOF | EV_ONESHOT); + if (pset != IPS_NULL) + ips_check_unlock(pset); + return(1); + } + + /* just use the reference from here on out */ + ips_reference(pset); + ips_unlock(pset); + + /* + * Only honor supported receive options. If no options are + * provided, just force a MACH_RCV_TOO_LARGE to detect the + * name of the port and sizeof the waiting message. + */ + option = kn->kn_sfflags & (MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_TRAILER_MASK); + if (option & MACH_RCV_MSG) { + self->ith_msg_addr = (mach_vm_address_t) kn->kn_ext[0]; + size = (mach_msg_size_t)kn->kn_ext[1]; + } else { + option = MACH_RCV_LARGE; + self->ith_msg_addr = 0; + size = 0; + } + + /* + * Set up to receive a message or the notification of a + * too large message. But never allow this call to wait. + * If the user provided aditional options, like trailer + * options, pass those through here. But we don't support + * scatter lists through this interface. + */ + self->ith_object = (ipc_object_t)pset; + self->ith_msize = size; + self->ith_option = option; + self->ith_scatter_list_size = 0; + self->ith_receiver_name = MACH_PORT_NULL; + self->ith_continuation = NULL; + option |= MACH_RCV_TIMEOUT; // never wait + assert((self->ith_state = MACH_RCV_IN_PROGRESS) == MACH_RCV_IN_PROGRESS); + + wresult = ipc_mqueue_receive_on_thread( + &pset->ips_messages, + option, + size, /* max_size */ + 0, /* immediate timeout */ + THREAD_INTERRUPTIBLE, + self); + assert(wresult == THREAD_NOT_WAITING); + assert(self->ith_state != MACH_RCV_IN_PROGRESS); + + /* + * If we timed out, just release the reference on the + * portset and return zero. + */ + if (self->ith_state == MACH_RCV_TIMED_OUT) { + ipc_pset_release(pset); + return 0; + } + + /* + * If we weren't attempting to receive a message + * directly, we need to return the port name in + * the kevent structure. + */ + if ((option & MACH_RCV_MSG) != MACH_RCV_MSG) { + assert(self->ith_state == MACH_RCV_TOO_LARGE); + assert(self->ith_kmsg == IKM_NULL); + kn->kn_data = self->ith_receiver_name; + ipc_pset_release(pset); + return 1; + } + + /* + * Attempt to receive the message directly, returning + * the results in the fflags field. + */ + assert(option & MACH_RCV_MSG); + kn->kn_data = MACH_PORT_NULL; + kn->kn_ext[1] = self->ith_msize; + kn->kn_fflags = mach_msg_receive_results(); + /* kmsg and pset reference consumed */ + return 1; +} + +static void +filt_machporttouch(struct knote *kn, struct kevent64_s *kev, long type) +{ + switch (type) { + case EVENT_REGISTER: + kn->kn_sfflags = kev->fflags; + kn->kn_sdata = kev->data; + break; + case EVENT_PROCESS: + *kev = kn->kn_kevent; + if (kn->kn_flags & EV_CLEAR) { + kn->kn_data = 0; + kn->kn_fflags = 0; + } + break; + default: + panic("filt_machporttouch() - invalid type (%ld)", type); + break; + } +} + +/* + * Peek to see if the portset associated with the knote has any + * events. This pre-hook is called when a filter uses the stay- + * on-queue mechanism (as the knote_link_wait_queue mechanism + * does). + * + * This is called with the kqueue that the knote belongs to still + * locked (thus holding a reference on the knote, but restricting + * also restricting our ability to take other locks). + * + * Just peek at the pre-post status of the portset's wait queue + * to determine if it has anything interesting. We can do it + * without holding the lock, as it is just a snapshot in time + * (if this is used as part of really waiting for events, we + * will catch changes in this status when the event gets posted + * up to the knote's kqueue). + */ +static int +filt_machportpeek(struct knote *kn) +{ + ipc_pset_t pset = kn->kn_ptr.p_pset; + ipc_mqueue_t set_mq = &pset->ips_messages; + + return (ipc_mqueue_peek(set_mq)); +} + + #include #if MACH_KDB @@ -319,13 +556,10 @@ ipc_list_count( * Purpose: * Pretty-print a port set for kdb. */ - void ipc_pset_print( ipc_pset_t pset) { - extern int db_indent; - printf("pset 0x%x\n", pset); db_indent += 2;