]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/ipc/ipc_mqueue.c
xnu-2422.115.4.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_mqueue.c
index 406b5ae932c7bb768c3b608bf2c154828402813b..4bcb66adc9fddf38f177f921f3bb3b14a107570d 100644 (file)
@@ -159,13 +159,14 @@ ipc_mqueue_member(
 
 kern_return_t
 ipc_mqueue_remove(
-       ipc_mqueue_t     mqueue,
-       ipc_mqueue_t     set_mqueue)
+       ipc_mqueue_t      mqueue,
+       ipc_mqueue_t      set_mqueue,
+       wait_queue_link_t *wqlp)
 {
        wait_queue_t     mq_waitq = &mqueue->imq_wait_queue;
        wait_queue_set_t set_waitq = &set_mqueue->imq_set_queue;
 
-       return wait_queue_unlink(mq_waitq, set_waitq);
+       return wait_queue_unlink_nofree(mq_waitq, set_waitq, wqlp);
 }
 
 /*
@@ -177,11 +178,12 @@ ipc_mqueue_remove(
  */
 void
 ipc_mqueue_remove_from_all(
-       ipc_mqueue_t    mqueue)
+       ipc_mqueue_t    mqueue,
+       queue_t         links)
 {
        wait_queue_t    mq_waitq = &mqueue->imq_wait_queue;
 
-       wait_queue_unlink_all(mq_waitq);
+       wait_queue_unlink_all_nofree(mq_waitq, links);
        return;
 }
 
@@ -194,11 +196,12 @@ ipc_mqueue_remove_from_all(
  */
 void
 ipc_mqueue_remove_all(
-       ipc_mqueue_t    mqueue)
+       ipc_mqueue_t    mqueue,
+       queue_t         links)
 {
        wait_queue_set_t        mq_setq = &mqueue->imq_set_queue;
 
-       wait_queue_set_unlink_all(mq_setq);
+       wait_queue_set_unlink_all_nofree(mq_setq, links);
        return;
 }
 
@@ -217,7 +220,8 @@ ipc_mqueue_remove_all(
 kern_return_t
 ipc_mqueue_add(
        ipc_mqueue_t     port_mqueue,
-       ipc_mqueue_t     set_mqueue)
+       ipc_mqueue_t     set_mqueue,
+       wait_queue_link_t wql)
 {
        wait_queue_t     port_waitq = &port_mqueue->imq_wait_queue;
        wait_queue_set_t set_waitq = &set_mqueue->imq_set_queue;
@@ -226,7 +230,7 @@ ipc_mqueue_add(
        kern_return_t    kr;
        spl_t            s;
 
-       kr = wait_queue_link(port_waitq, set_waitq);
+       kr = wait_queue_link_noalloc(port_waitq, set_waitq, wql);
        if (kr != KERN_SUCCESS)
                return kr;
 
@@ -278,7 +282,7 @@ ipc_mqueue_add(
                         */
                        msize = ipc_kmsg_copyout_size(kmsg, th->map);
                        if (th->ith_msize <
-                                       (msize + REQUESTED_TRAILER_SIZE(th->ith_option))) {
+                                       (msize + REQUESTED_TRAILER_SIZE(thread_is_64bit(th), th->ith_option))) {
                                th->ith_state = MACH_RCV_TOO_LARGE;
                                th->ith_msize = msize;
                                if (th->ith_option & MACH_RCV_LARGE) {
@@ -348,7 +352,7 @@ ipc_mqueue_changed(
  *             the message and must do something with it.  If successful,
  *             the message is queued, given to a receiver, or destroyed.
  *     Conditions:
- *             Nothing locked.
+ *             mqueue is locked.
  *     Returns:
  *             MACH_MSG_SUCCESS        The message was accepted.
  *             MACH_SEND_TIMED_OUT     Caller still has message.
@@ -405,7 +409,9 @@ ipc_mqueue_send(
                wresult = wait_queue_assert_wait64_locked(
                                                &mqueue->imq_wait_queue,
                                                IPC_MQUEUE_FULL,
-                                               THREAD_ABORTSAFE, deadline,
+                                               THREAD_ABORTSAFE,
+                                               TIMEOUT_URGENCY_USER_NORMAL,
+                                               deadline, 0,
                                                cur_thread);
                thread_unlock(cur_thread);
                imq_unlock(mqueue);
@@ -441,6 +447,7 @@ ipc_mqueue_send(
        return MACH_MSG_SUCCESS;
 }
 
+
 /*
  *     Routine:        ipc_mqueue_release_msgcount
  *     Purpose:
@@ -539,7 +546,7 @@ ipc_mqueue_post(
                 */
                msize = ipc_kmsg_copyout_size(kmsg, receiver->map);
                if (receiver->ith_msize <
-                               (msize + REQUESTED_TRAILER_SIZE(receiver->ith_option))) {
+                               (msize + REQUESTED_TRAILER_SIZE(thread_is_64bit(receiver), receiver->ith_option))) {
                        receiver->ith_msize = msize;
                        receiver->ith_state = MACH_RCV_TOO_LARGE;
                } else {
@@ -568,6 +575,7 @@ ipc_mqueue_post(
                 * and handle its error without getting the message.  We
                 * need to go back and pick another one.
                 */
+               receiver->ith_receiver_name = mqueue->imq_receiver_name;
                receiver->ith_kmsg = IKM_NULL;
                receiver->ith_seqno = 0;
                thread_unlock(receiver);
@@ -865,7 +873,9 @@ ipc_mqueue_receive_on_thread(
 
        wresult = wait_queue_assert_wait64_locked(&mqueue->imq_wait_queue,
                                                  IPC_MQUEUE_RECEIVE,
-                                                 interruptible, deadline,
+                                                 interruptible, 
+                                                 TIMEOUT_URGENCY_USER_NORMAL,
+                                                 deadline, 0,
                                                  thread);
        /* preposts should be detected above, not here */
        if (wresult == THREAD_AWAKENED)
@@ -917,7 +927,7 @@ ipc_mqueue_select_on_thread(
         * (and size needed).
         */
        rcv_size = ipc_kmsg_copyout_size(kmsg, thread->map);
-       if (rcv_size + REQUESTED_TRAILER_SIZE(option) > max_size) {
+       if (rcv_size + REQUESTED_TRAILER_SIZE(thread_is_64bit(thread), option) > max_size) {
                mr = MACH_RCV_TOO_LARGE;
                if (option & MACH_RCV_LARGE) {
                        thread->ith_receiver_name = mqueue->imq_receiver_name;
@@ -942,50 +952,167 @@ ipc_mqueue_select_on_thread(
 /*
  *     Routine:        ipc_mqueue_peek
  *     Purpose:
- *             Peek at a message queue to see if it has any messages
- *             (in it or contained message queues for a set).
+ *             Peek at a (non-set) message queue to see if it has a message
+ *             matching the sequence number provided (if zero, then the
+ *             first message in the queue) and return vital info about the
+ *             message.
+ *
+ *     Conditions:
+ *             Locks may be held by callers, so this routine cannot block.
+ *             Caller holds reference on the message queue.
+ */
+unsigned
+ipc_mqueue_peek(ipc_mqueue_t           mq,
+               mach_port_seqno_t       *seqnop,
+               mach_msg_size_t         *msg_sizep,
+               mach_msg_id_t           *msg_idp,
+               mach_msg_max_trailer_t  *msg_trailerp)
+{
+       ipc_kmsg_queue_t kmsgq;
+       ipc_kmsg_t kmsg; 
+       mach_port_seqno_t seqno, msgoff;
+       int res = 0;
+       spl_t s;
+
+       assert(!imq_is_set(mq));
+
+       s = splsched();
+       imq_lock(mq);
+
+       seqno = (seqnop != NULL) ? seqno = *seqnop : 0;
+
+       if (seqno == 0) {
+               seqno = mq->imq_seqno;
+               msgoff = 0;
+       } else if (seqno >= mq->imq_seqno && 
+                  seqno < mq->imq_seqno + mq->imq_msgcount) {
+               msgoff = seqno - mq->imq_seqno;
+       } else
+               goto out;
+
+       /* look for the message that would match that seqno */
+       kmsgq = &mq->imq_messages;
+       kmsg = ipc_kmsg_queue_first(kmsgq);
+       while (msgoff-- && kmsg != IKM_NULL) {
+               kmsg = ipc_kmsg_queue_next(kmsgq, kmsg);
+       }
+       if (kmsg == IKM_NULL)
+               goto out;
+
+       /* found one - return the requested info */
+       if (seqnop != NULL)
+               *seqnop = seqno;
+       if (msg_sizep != NULL)
+               *msg_sizep = kmsg->ikm_header->msgh_size;
+       if (msg_idp != NULL)
+               *msg_idp = kmsg->ikm_header->msgh_id;
+       if (msg_trailerp != NULL)
+               memcpy(msg_trailerp, 
+                      (mach_msg_max_trailer_t *)((vm_offset_t)kmsg->ikm_header +
+                                                 round_msg(kmsg->ikm_header->msgh_size)),
+                      sizeof(mach_msg_max_trailer_t));
+       res = 1;
+
+ out:
+       imq_unlock(mq);
+       splx(s);
+       return res;
+}
+
+/*
+ *     Routine:        ipc_mqueue_set_peek
+ *     Purpose:
+ *             Peek at a message queue set to see if it has any ports
+ *             with messages.
  *
  *     Conditions:
  *             Locks may be held by callers, so this routine cannot block.
  *             Caller holds reference on the message queue.
  */
 unsigned
-ipc_mqueue_peek(ipc_mqueue_t mq)
+ipc_mqueue_set_peek(ipc_mqueue_t mq)
 {
        wait_queue_link_t       wql;
        queue_t                 q;
        spl_t s;
+       int res;
 
-       if (!imq_is_set(mq))
-               return (ipc_kmsg_queue_first(&mq->imq_messages) != IKM_NULL);
+       assert(imq_is_set(mq));
 
-       /*
-        * Don't block trying to get the lock.
-        */
        s = splsched();
        imq_lock(mq);
 
        /* 
         * peek at the contained port message queues, return as soon as
         * we spot a message on one of the message queues linked on the
-        * prepost list.
+        * prepost list.  No need to lock each message queue, as only the
+        * head of each queue is checked. If a message wasn't there before
+        * we entered here, no need to find it (if we do, great).
         */
+       res = 0;
        q = &mq->imq_preposts;
        queue_iterate(q, wql, wait_queue_link_t, wql_preposts) {
                ipc_mqueue_t port_mq = (ipc_mqueue_t)wql->wql_queue;
                ipc_kmsg_queue_t kmsgs = &port_mq->imq_messages;
                        
                if (ipc_kmsg_queue_first(kmsgs) != IKM_NULL) {
-                       imq_unlock(mq);
-                       splx(s);
-                       return 1;
+                       res = 1;
+                       break;
                }
        }
        imq_unlock(mq);
        splx(s);
-       return 0;
+       return res;
+}
+
+/*
+ *     Routine:        ipc_mqueue_set_gather_member_names
+ *     Purpose:
+ *             Iterate a message queue set to identify the member port
+ *             names. Actual returned names is limited to maxnames entries,
+ *             but we keep counting the actual number of members to let
+ *             the caller decide to retry if necessary.
+ *
+ *     Conditions:
+ *             Locks may be held by callers, so this routine cannot block.
+ *             Caller holds reference on the message queue.
+ */
+void
+ipc_mqueue_set_gather_member_names(
+       ipc_mqueue_t mq, 
+       ipc_entry_num_t maxnames, 
+       mach_port_name_t *names,
+       ipc_entry_num_t *actualp)
+{
+       wait_queue_link_t       wql;
+       queue_t                 q;
+       spl_t s;
+       ipc_entry_num_t actual = 0;
+
+       assert(imq_is_set(mq));
+
+       s = splsched();
+       imq_lock(mq);
+
+       /* 
+        * Iterate over the member ports through the mqueue set links
+        * capturing as many names as we can.
+        */
+       q = &mq->imq_setlinks;
+       queue_iterate(q, wql, wait_queue_link_t, wql_setlinks) {
+               ipc_mqueue_t port_mq = (ipc_mqueue_t)wql->wql_queue;
+
+               if (actual < maxnames)
+                       names[actual] = port_mq->imq_receiver_name;
+               actual++;
+       }
+       imq_unlock(mq);
+       splx(s);
+
+       *actualp = actual;
 }
 
+
 /*
  *     Routine:        ipc_mqueue_destroy
  *     Purpose:
@@ -1005,7 +1132,6 @@ ipc_mqueue_destroy(
        boolean_t reap = FALSE;
        spl_t s;
 
-
        s = splsched();
        imq_lock(mqueue);
        /*
@@ -1136,7 +1262,7 @@ ipc_mqueue_copyin(
        ipc_mqueue_t mqueue;
 
        is_read_lock(space);
-       if (!space->is_active) {
+       if (!is_active(space)) {
                is_read_unlock(space);
                return MACH_RCV_INVALID_NAME;
        }