X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/2d21ac55c334faf3a56e5634905ed6987fc787d4..a1c7dba18ef36983396c282fe85292db066e39db:/osfmk/ipc/ipc_pset.c diff --git a/osfmk/ipc/ipc_pset.c b/osfmk/ipc/ipc_pset.c index bf805af55..533ee3f08 100644 --- a/osfmk/ipc/ipc_pset.c +++ b/osfmk/ipc/ipc_pset.c @@ -72,10 +72,12 @@ #include #include #include -#include #include #include + +#include + /* * Routine: ipc_pset_alloc * Purpose: @@ -105,10 +107,11 @@ ipc_pset_alloc( &name, (ipc_object_t *) &pset); if (kr != KERN_SUCCESS) return kr; - /* pset is locked */ + /* pset and space are locked */ pset->ips_local_name = name; ipc_mqueue_init(&pset->ips_messages, TRUE /* set */); + is_write_unlock(space); *namep = name; *psetp = pset; @@ -183,15 +186,16 @@ ipc_pset_member( kern_return_t ipc_pset_add( - ipc_pset_t pset, - ipc_port_t port) + ipc_pset_t pset, + ipc_port_t port, + wait_queue_link_t wql) { kern_return_t kr; assert(ips_active(pset)); assert(ip_active(port)); - kr = ipc_mqueue_add(&port->ip_messages, &pset->ips_messages); + kr = ipc_mqueue_add(&port->ip_messages, &pset->ips_messages, wql); if (kr == KERN_SUCCESS) port->ip_pset_count++; @@ -213,8 +217,9 @@ ipc_pset_add( kern_return_t ipc_pset_remove( - ipc_pset_t pset, - ipc_port_t port) + ipc_pset_t pset, + ipc_port_t port, + wait_queue_link_t *wqlp) { kern_return_t kr; @@ -223,7 +228,7 @@ ipc_pset_remove( if (port->ip_pset_count == 0) return KERN_NOT_IN_SET; - kr = ipc_mqueue_remove(&port->ip_messages, &pset->ips_messages); + kr = ipc_mqueue_remove(&port->ip_messages, &pset->ips_messages, wqlp); if (kr == KERN_SUCCESS) port->ip_pset_count--; @@ -241,7 +246,8 @@ ipc_pset_remove( kern_return_t ipc_pset_remove_from_all( - ipc_port_t port) + ipc_port_t port, + queue_t links) { assert(ip_active(port)); @@ -251,7 +257,7 @@ ipc_pset_remove_from_all( /* * Remove the port's mqueue from all sets */ - ipc_mqueue_remove_from_all(&port->ip_messages); + ipc_mqueue_remove_from_all(&port->ip_messages, links); port->ip_pset_count = 0; return KERN_SUCCESS; } @@ -272,6 +278,11 @@ ipc_pset_destroy( ipc_pset_t pset) { spl_t s; + queue_head_t link_data; + queue_t links = &link_data; + wait_queue_link_t wql; + + queue_init(links); assert(ips_active(pset)); @@ -280,67 +291,271 @@ ipc_pset_destroy( /* * remove all the member message queues */ - ipc_mqueue_remove_all(&pset->ips_messages); + ipc_mqueue_remove_all(&pset->ips_messages, links); + /* + * 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_unlock(pset); + ips_release(pset); /* consume the ref our caller gave us */ + + while(!queue_empty(links)) { + wql = (wait_queue_link_t) dequeue(links); + wait_queue_link_free(wql); + } - ips_release(pset); /* consume the ref our caller gave us */ - ips_check_unlock(pset); } -#include -#if MACH_KDB +/* 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 unsigned 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; + wait_queue_link_t wql = wait_queue_link_allocate(); + 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) { + wait_queue_link_free(wql); + return (kr == KERN_INVALID_NAME ? ENOENT : ENOTSUP); + } + /* We've got a lock on 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, wql); + if (result == 0) { + /* keep a reference for the knote */ + kn->kn_ptr.p_pset = pset; + ips_reference(pset); + ips_unlock(pset); + return 0; + } -#include + ips_unlock(pset); + wait_queue_link_free(wql); + return result; +} -#define printf kdbprintf +static void +filt_machportdetach( + struct knote *kn) +{ + ipc_pset_t pset = kn->kn_ptr.p_pset; + wait_queue_link_t wql = WAIT_QUEUE_LINK_NULL; -int -ipc_list_count( - struct ipc_kmsg *base) + /* + * Unlink the portset wait queue from knote/kqueue, + * and release our reference on the portset. + */ + ips_lock(pset); + (void)knote_unlink_wait_queue(kn, &pset->ips_messages.imq_wait_queue, &wql); + kn->kn_ptr.p_pset = IPS_NULL; + ips_unlock(pset); + ips_release(pset); + if (wql != WAIT_QUEUE_LINK_NULL) + wait_queue_link_free(wql); +} + +static int +filt_machport( + struct knote *kn, + __unused long hint) { - register int count = 0; + 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; - if (base) { - struct ipc_kmsg *kmsg = base; + /* never called from below */ + assert(hint == 0); - ++count; - while (kmsg && kmsg->ikm_next != base - && kmsg->ikm_next != IKM_BOGUS){ - kmsg = kmsg->ikm_next; - ++count; + /* + * 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_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_LARGE_IDENTITY| + MACH_RCV_TRAILER_MASK|MACH_RCV_VOUCHER); + 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; } - return(count); + + /* + * 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 + self->ith_state = 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) { + ips_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; + ips_release(pset); + return 1; + } + + /* + * Attempt to receive the message directly, returning + * the results in the fflags field. + */ + assert(option & MACH_RCV_MSG); + kn->kn_ext[1] = self->ith_msize; + kn->kn_data = MACH_PORT_NULL; + kn->kn_fflags = mach_msg_receive_results(); + /* kmsg and pset reference consumed */ + + /* + * if the user asked for the identity of ports containing a + * a too-large message, return it in the data field (as we + * do for messages we didn't try to receive). + */ + if ((kn->kn_fflags == MACH_RCV_TOO_LARGE) && + (option & MACH_RCV_LARGE_IDENTITY)) + kn->kn_data = self->ith_receiver_name; + + 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; + kn->kn_ext[0] = kev->ext[0]; + kn->kn_ext[1] = kev->ext[1]; + 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; + } } /* - * Routine: ipc_pset_print - * Purpose: - * Pretty-print a port set for kdb. + * 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). */ -void -ipc_pset_print( - ipc_pset_t pset) +static unsigned +filt_machportpeek(struct knote *kn) { - printf("pset 0x%x\n", pset); + ipc_pset_t pset = kn->kn_ptr.p_pset; + ipc_mqueue_t set_mq = &pset->ips_messages; - db_indent += 2; - - ipc_object_print(&pset->ips_object); - iprintf("local_name = 0x%x\n", pset->ips_local_name); - iprintf("%d kmsgs => 0x%x", - ipc_list_count(pset->ips_messages.imq_messages.ikmq_base), - pset->ips_messages.imq_messages.ikmq_base); - printf(",rcvrs queue= 0x%x\n", &pset->ips_messages.imq_wait_queue); - - db_indent -=2; + return (ipc_mqueue_set_peek(set_mq)); } - -#endif /* MACH_KDB */