#include <ipc/ipc_right.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_port.h>
-#include <ipc/ipc_print.h>
#include <kern/kern_types.h>
#include <kern/spl.h>
+
+#include <vm/vm_map.h>
+
/*
* Routine: ipc_pset_alloc
* Purpose:
&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;
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++;
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;
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--;
kern_return_t
ipc_pset_remove_from_all(
- ipc_port_t port)
+ ipc_port_t port,
+ queue_t links)
{
assert(ip_active(port));
/*
* 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;
}
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));
/*
* 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 <mach_kdb.h>
-#if MACH_KDB
+/* Kqueue EVFILT_MACHPORT support */
+
+#include <sys/errno.h>
+
+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 <ddb/db_output.h>
+ 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 */