]> git.saurik.com Git - apple/xnu.git/blame_incremental - osfmk/ipc/ipc_mqueue.c
xnu-1699.22.73.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_mqueue.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * @OSF_FREE_COPYRIGHT@
30 */
31/*
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
34 * All Rights Reserved.
35 *
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.
41 *
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.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56/*
57 */
58/*
59 * File: ipc/ipc_mqueue.c
60 * Author: Rich Draves
61 * Date: 1989
62 *
63 * Functions to manipulate IPC message queues.
64 */
65/*
66 * NOTICE: This file was modified by SPARTA, Inc. in 2006 to introduce
67 * support for mandatory and extensible security protections. This notice
68 * is included in support of clause 2.2 (b) of the Apple Public License,
69 * Version 2.0.
70 */
71
72
73#include <mach/port.h>
74#include <mach/message.h>
75#include <mach/sync_policy.h>
76
77#include <kern/assert.h>
78#include <kern/counters.h>
79#include <kern/sched_prim.h>
80#include <kern/ipc_kobject.h>
81#include <kern/ipc_mig.h> /* XXX - for mach_msg_receive_continue */
82#include <kern/misc_protos.h>
83#include <kern/task.h>
84#include <kern/thread.h>
85#include <kern/wait_queue.h>
86
87#include <ipc/ipc_mqueue.h>
88#include <ipc/ipc_kmsg.h>
89#include <ipc/ipc_port.h>
90#include <ipc/ipc_pset.h>
91#include <ipc/ipc_space.h>
92
93#ifdef __LP64__
94#include <vm/vm_map.h>
95#endif
96
97#if CONFIG_MACF_MACH
98#include <security/mac_mach_internal.h>
99#endif
100
101int ipc_mqueue_full; /* address is event for queue space */
102int ipc_mqueue_rcv; /* address is event for message arrival */
103
104/* forward declarations */
105void ipc_mqueue_receive_results(wait_result_t result);
106
107/*
108 * Routine: ipc_mqueue_init
109 * Purpose:
110 * Initialize a newly-allocated message queue.
111 */
112void
113ipc_mqueue_init(
114 ipc_mqueue_t mqueue,
115 boolean_t is_set)
116{
117 if (is_set) {
118 wait_queue_set_init(&mqueue->imq_set_queue, SYNC_POLICY_FIFO|SYNC_POLICY_PREPOST);
119 } else {
120 wait_queue_init(&mqueue->imq_wait_queue, SYNC_POLICY_FIFO);
121 ipc_kmsg_queue_init(&mqueue->imq_messages);
122 mqueue->imq_seqno = 0;
123 mqueue->imq_msgcount = 0;
124 mqueue->imq_qlimit = MACH_PORT_QLIMIT_DEFAULT;
125 mqueue->imq_fullwaiters = FALSE;
126 }
127}
128
129/*
130 * Routine: ipc_mqueue_member
131 * Purpose:
132 * Indicate whether the (port) mqueue is a member of
133 * this portset's mqueue. We do this by checking
134 * whether the portset mqueue's waitq is an member of
135 * the port's mqueue waitq.
136 * Conditions:
137 * the portset's mqueue is not already a member
138 * this may block while allocating linkage structures.
139 */
140
141boolean_t
142ipc_mqueue_member(
143 ipc_mqueue_t port_mqueue,
144 ipc_mqueue_t set_mqueue)
145{
146 wait_queue_t port_waitq = &port_mqueue->imq_wait_queue;
147 wait_queue_set_t set_waitq = &set_mqueue->imq_set_queue;
148
149 return (wait_queue_member(port_waitq, set_waitq));
150
151}
152
153/*
154 * Routine: ipc_mqueue_remove
155 * Purpose:
156 * Remove the association between the queue and the specified
157 * set message queue.
158 */
159
160kern_return_t
161ipc_mqueue_remove(
162 ipc_mqueue_t mqueue,
163 ipc_mqueue_t set_mqueue)
164{
165 wait_queue_t mq_waitq = &mqueue->imq_wait_queue;
166 wait_queue_set_t set_waitq = &set_mqueue->imq_set_queue;
167
168 return wait_queue_unlink(mq_waitq, set_waitq);
169}
170
171/*
172 * Routine: ipc_mqueue_remove_from_all
173 * Purpose:
174 * Remove the mqueue from all the sets it is a member of
175 * Conditions:
176 * Nothing locked.
177 */
178void
179ipc_mqueue_remove_from_all(
180 ipc_mqueue_t mqueue)
181{
182 wait_queue_t mq_waitq = &mqueue->imq_wait_queue;
183
184 wait_queue_unlink_all(mq_waitq);
185 return;
186}
187
188/*
189 * Routine: ipc_mqueue_remove_all
190 * Purpose:
191 * Remove all the member queues from the specified set.
192 * Conditions:
193 * Nothing locked.
194 */
195void
196ipc_mqueue_remove_all(
197 ipc_mqueue_t mqueue)
198{
199 wait_queue_set_t mq_setq = &mqueue->imq_set_queue;
200
201 wait_queue_set_unlink_all(mq_setq);
202 return;
203}
204
205
206/*
207 * Routine: ipc_mqueue_add
208 * Purpose:
209 * Associate the portset's mqueue with the port's mqueue.
210 * This has to be done so that posting the port will wakeup
211 * a portset waiter. If there are waiters on the portset
212 * mqueue and messages on the port mqueue, try to match them
213 * up now.
214 * Conditions:
215 * May block.
216 */
217kern_return_t
218ipc_mqueue_add(
219 ipc_mqueue_t port_mqueue,
220 ipc_mqueue_t set_mqueue)
221{
222 wait_queue_t port_waitq = &port_mqueue->imq_wait_queue;
223 wait_queue_set_t set_waitq = &set_mqueue->imq_set_queue;
224 ipc_kmsg_queue_t kmsgq;
225 ipc_kmsg_t kmsg, next;
226 kern_return_t kr;
227 spl_t s;
228
229 kr = wait_queue_link(port_waitq, set_waitq);
230 if (kr != KERN_SUCCESS)
231 return kr;
232
233 /*
234 * Now that the set has been added to the port, there may be
235 * messages queued on the port and threads waiting on the set
236 * waitq. Lets get them together.
237 */
238 s = splsched();
239 imq_lock(port_mqueue);
240 kmsgq = &port_mqueue->imq_messages;
241 for (kmsg = ipc_kmsg_queue_first(kmsgq);
242 kmsg != IKM_NULL;
243 kmsg = next) {
244 next = ipc_kmsg_queue_next(kmsgq, kmsg);
245
246 for (;;) {
247 thread_t th;
248 mach_msg_size_t msize;
249
250 th = wait_queue_wakeup64_identity_locked(
251 port_waitq,
252 IPC_MQUEUE_RECEIVE,
253 THREAD_AWAKENED,
254 FALSE);
255 /* waitq/mqueue still locked, thread locked */
256
257 if (th == THREAD_NULL)
258 goto leave;
259
260 /*
261 * If the receiver waited with a facility not directly
262 * related to Mach messaging, then it isn't prepared to get
263 * handed the message directly. Just set it running, and
264 * go look for another thread that can.
265 */
266 if (th->ith_state != MACH_RCV_IN_PROGRESS) {
267 thread_unlock(th);
268 continue;
269 }
270
271 /*
272 * Found a receiver. see if they can handle the message
273 * correctly (the message is not too large for them, or
274 * they didn't care to be informed that the message was
275 * too large). If they can't handle it, take them off
276 * the list and let them go back and figure it out and
277 * just move onto the next.
278 */
279 msize = ipc_kmsg_copyout_size(kmsg, th->map);
280 if (th->ith_msize <
281 (msize + REQUESTED_TRAILER_SIZE(th->ith_option))) {
282 th->ith_state = MACH_RCV_TOO_LARGE;
283 th->ith_msize = msize;
284 if (th->ith_option & MACH_RCV_LARGE) {
285 /*
286 * let him go without message
287 */
288 th->ith_receiver_name = port_mqueue->imq_receiver_name;
289 th->ith_kmsg = IKM_NULL;
290 th->ith_seqno = 0;
291 thread_unlock(th);
292 continue; /* find another thread */
293 }
294 } else {
295 th->ith_state = MACH_MSG_SUCCESS;
296 }
297
298 /*
299 * This thread is going to take this message,
300 * so give it to him.
301 */
302 ipc_kmsg_rmqueue(kmsgq, kmsg);
303 ipc_mqueue_release_msgcount(port_mqueue);
304
305 th->ith_kmsg = kmsg;
306 th->ith_seqno = port_mqueue->imq_seqno++;
307 thread_unlock(th);
308 break; /* go to next message */
309 }
310
311 }
312 leave:
313 imq_unlock(port_mqueue);
314 splx(s);
315 return KERN_SUCCESS;
316}
317
318/*
319 * Routine: ipc_mqueue_changed
320 * Purpose:
321 * Wake up receivers waiting in a message queue.
322 * Conditions:
323 * The message queue is locked.
324 */
325
326void
327ipc_mqueue_changed(
328 ipc_mqueue_t mqueue)
329{
330 wait_queue_wakeup64_all_locked(
331 &mqueue->imq_wait_queue,
332 IPC_MQUEUE_RECEIVE,
333 THREAD_RESTART,
334 FALSE); /* unlock waitq? */
335}
336
337
338
339
340/*
341 * Routine: ipc_mqueue_send
342 * Purpose:
343 * Send a message to a message queue. The message holds a reference
344 * for the destination port for this message queue in the
345 * msgh_remote_port field.
346 *
347 * If unsuccessful, the caller still has possession of
348 * the message and must do something with it. If successful,
349 * the message is queued, given to a receiver, or destroyed.
350 * Conditions:
351 * Nothing locked.
352 * Returns:
353 * MACH_MSG_SUCCESS The message was accepted.
354 * MACH_SEND_TIMED_OUT Caller still has message.
355 * MACH_SEND_INTERRUPTED Caller still has message.
356 */
357mach_msg_return_t
358ipc_mqueue_send(
359 ipc_mqueue_t mqueue,
360 ipc_kmsg_t kmsg,
361 mach_msg_option_t option,
362 mach_msg_timeout_t send_timeout,
363 spl_t s)
364{
365 int wresult;
366
367 /*
368 * Don't block if:
369 * 1) We're under the queue limit.
370 * 2) Caller used the MACH_SEND_ALWAYS internal option.
371 * 3) Message is sent to a send-once right.
372 */
373 if (!imq_full(mqueue) ||
374 (!imq_full_kernel(mqueue) &&
375 ((option & MACH_SEND_ALWAYS) ||
376 (MACH_MSGH_BITS_REMOTE(kmsg->ikm_header->msgh_bits) ==
377 MACH_MSG_TYPE_PORT_SEND_ONCE)))) {
378 mqueue->imq_msgcount++;
379 assert(mqueue->imq_msgcount > 0);
380 imq_unlock(mqueue);
381 splx(s);
382 } else {
383 thread_t cur_thread = current_thread();
384 uint64_t deadline;
385
386 /*
387 * We have to wait for space to be granted to us.
388 */
389 if ((option & MACH_SEND_TIMEOUT) && (send_timeout == 0)) {
390 imq_unlock(mqueue);
391 splx(s);
392 return MACH_SEND_TIMED_OUT;
393 }
394 if (imq_full_kernel(mqueue)) {
395 imq_unlock(mqueue);
396 splx(s);
397 return MACH_SEND_NO_BUFFER;
398 }
399 mqueue->imq_fullwaiters = TRUE;
400 thread_lock(cur_thread);
401 if (option & MACH_SEND_TIMEOUT)
402 clock_interval_to_deadline(send_timeout, 1000*NSEC_PER_USEC, &deadline);
403 else
404 deadline = 0;
405 wresult = wait_queue_assert_wait64_locked(
406 &mqueue->imq_wait_queue,
407 IPC_MQUEUE_FULL,
408 THREAD_ABORTSAFE, deadline,
409 cur_thread);
410 thread_unlock(cur_thread);
411 imq_unlock(mqueue);
412 splx(s);
413
414 if (wresult == THREAD_WAITING) {
415 wresult = thread_block(THREAD_CONTINUE_NULL);
416 counter(c_ipc_mqueue_send_block++);
417 }
418
419 switch (wresult) {
420 case THREAD_TIMED_OUT:
421 assert(option & MACH_SEND_TIMEOUT);
422 return MACH_SEND_TIMED_OUT;
423
424 case THREAD_AWAKENED:
425 /* we can proceed - inherited msgcount from waker */
426 assert(mqueue->imq_msgcount > 0);
427 break;
428
429 case THREAD_INTERRUPTED:
430 return MACH_SEND_INTERRUPTED;
431
432 case THREAD_RESTART:
433 /* mqueue is being destroyed */
434 return MACH_SEND_INVALID_DEST;
435 default:
436 panic("ipc_mqueue_send");
437 }
438 }
439
440 ipc_mqueue_post(mqueue, kmsg);
441 return MACH_MSG_SUCCESS;
442}
443
444/*
445 * Routine: ipc_mqueue_release_msgcount
446 * Purpose:
447 * Release a message queue reference in the case where we
448 * found a waiter.
449 *
450 * Conditions:
451 * The message queue is locked.
452 * The message corresponding to this reference is off the queue.
453 */
454void
455ipc_mqueue_release_msgcount(
456 ipc_mqueue_t mqueue)
457{
458 assert(imq_held(mqueue));
459 assert(mqueue->imq_msgcount > 1 || ipc_kmsg_queue_empty(&mqueue->imq_messages));
460
461 mqueue->imq_msgcount--;
462
463 if (!imq_full(mqueue) && mqueue->imq_fullwaiters) {
464 if (wait_queue_wakeup64_one_locked(
465 &mqueue->imq_wait_queue,
466 IPC_MQUEUE_FULL,
467 THREAD_AWAKENED,
468 FALSE) != KERN_SUCCESS) {
469 mqueue->imq_fullwaiters = FALSE;
470 } else {
471 /* gave away our slot - add reference back */
472 mqueue->imq_msgcount++;
473 }
474 }
475}
476
477/*
478 * Routine: ipc_mqueue_post
479 * Purpose:
480 * Post a message to a waiting receiver or enqueue it. If a
481 * receiver is waiting, we can release our reserved space in
482 * the message queue.
483 *
484 * Conditions:
485 * If we need to queue, our space in the message queue is reserved.
486 */
487void
488ipc_mqueue_post(
489 register ipc_mqueue_t mqueue,
490 register ipc_kmsg_t kmsg)
491{
492 spl_t s;
493
494 /*
495 * While the msg queue is locked, we have control of the
496 * kmsg, so the ref in it for the port is still good.
497 *
498 * Check for a receiver for the message.
499 */
500 s = splsched();
501 imq_lock(mqueue);
502 for (;;) {
503 wait_queue_t waitq = &mqueue->imq_wait_queue;
504 thread_t receiver;
505 mach_msg_size_t msize;
506
507 receiver = wait_queue_wakeup64_identity_locked(
508 waitq,
509 IPC_MQUEUE_RECEIVE,
510 THREAD_AWAKENED,
511 FALSE);
512 /* waitq still locked, thread locked */
513
514 if (receiver == THREAD_NULL) {
515 /*
516 * no receivers; queue kmsg
517 */
518 assert(mqueue->imq_msgcount > 0);
519 ipc_kmsg_enqueue_macro(&mqueue->imq_messages, kmsg);
520 break;
521 }
522
523 /*
524 * If the receiver waited with a facility not directly
525 * related to Mach messaging, then it isn't prepared to get
526 * handed the message directly. Just set it running, and
527 * go look for another thread that can.
528 */
529 if (receiver->ith_state != MACH_RCV_IN_PROGRESS) {
530 thread_unlock(receiver);
531 continue;
532 }
533
534
535 /*
536 * We found a waiting thread.
537 * If the message is too large or the scatter list is too small
538 * the thread we wake up will get that as its status.
539 */
540 msize = ipc_kmsg_copyout_size(kmsg, receiver->map);
541 if (receiver->ith_msize <
542 (msize + REQUESTED_TRAILER_SIZE(receiver->ith_option))) {
543 receiver->ith_msize = msize;
544 receiver->ith_state = MACH_RCV_TOO_LARGE;
545 } else {
546 receiver->ith_state = MACH_MSG_SUCCESS;
547 }
548
549 /*
550 * If there is no problem with the upcoming receive, or the
551 * receiver thread didn't specifically ask for special too
552 * large error condition, go ahead and select it anyway.
553 */
554 if ((receiver->ith_state == MACH_MSG_SUCCESS) ||
555 !(receiver->ith_option & MACH_RCV_LARGE)) {
556
557 receiver->ith_kmsg = kmsg;
558 receiver->ith_seqno = mqueue->imq_seqno++;
559 thread_unlock(receiver);
560
561 /* we didn't need our reserved spot in the queue */
562 ipc_mqueue_release_msgcount(mqueue);
563 break;
564 }
565
566 /*
567 * Otherwise, this thread needs to be released to run
568 * and handle its error without getting the message. We
569 * need to go back and pick another one.
570 */
571 receiver->ith_kmsg = IKM_NULL;
572 receiver->ith_seqno = 0;
573 thread_unlock(receiver);
574 }
575
576 imq_unlock(mqueue);
577 splx(s);
578
579 current_task()->messages_sent++;
580 return;
581}
582
583
584/* static */ void
585ipc_mqueue_receive_results(wait_result_t saved_wait_result)
586{
587 thread_t self = current_thread();
588 mach_msg_option_t option = self->ith_option;
589
590 /*
591 * why did we wake up?
592 */
593 switch (saved_wait_result) {
594 case THREAD_TIMED_OUT:
595 self->ith_state = MACH_RCV_TIMED_OUT;
596 return;
597
598 case THREAD_INTERRUPTED:
599 self->ith_state = MACH_RCV_INTERRUPTED;
600 return;
601
602 case THREAD_RESTART:
603 /* something bad happened to the port/set */
604 self->ith_state = MACH_RCV_PORT_CHANGED;
605 return;
606
607 case THREAD_AWAKENED:
608 /*
609 * We do not need to go select a message, somebody
610 * handed us one (or a too-large indication).
611 */
612 switch (self->ith_state) {
613 case MACH_RCV_SCATTER_SMALL:
614 case MACH_RCV_TOO_LARGE:
615 /*
616 * Somebody tried to give us a too large
617 * message. If we indicated that we cared,
618 * then they only gave us the indication,
619 * otherwise they gave us the indication
620 * AND the message anyway.
621 */
622 if (option & MACH_RCV_LARGE) {
623 return;
624 }
625
626 case MACH_MSG_SUCCESS:
627 return;
628
629 default:
630 panic("ipc_mqueue_receive_results: strange ith_state");
631 }
632
633 default:
634 panic("ipc_mqueue_receive_results: strange wait_result");
635 }
636}
637
638void
639ipc_mqueue_receive_continue(
640 __unused void *param,
641 wait_result_t wresult)
642{
643 ipc_mqueue_receive_results(wresult);
644 mach_msg_receive_continue(); /* hard-coded for now */
645}
646
647/*
648 * Routine: ipc_mqueue_receive
649 * Purpose:
650 * Receive a message from a message queue.
651 *
652 * If continuation is non-zero, then we might discard
653 * our kernel stack when we block. We will continue
654 * after unblocking by executing continuation.
655 *
656 * If resume is true, then we are resuming a receive
657 * operation after a blocked receive discarded our stack.
658 * Conditions:
659 * Our caller must hold a reference for the port or port set
660 * to which this queue belongs, to keep the queue
661 * from being deallocated.
662 *
663 * The kmsg is returned with clean header fields
664 * and with the circular bit turned off.
665 * Returns:
666 * MACH_MSG_SUCCESS Message returned in kmsgp.
667 * MACH_RCV_TOO_LARGE Message size returned in kmsgp.
668 * MACH_RCV_TIMED_OUT No message obtained.
669 * MACH_RCV_INTERRUPTED No message obtained.
670 * MACH_RCV_PORT_DIED Port/set died; no message.
671 * MACH_RCV_PORT_CHANGED Port moved into set; no msg.
672 *
673 */
674
675void
676ipc_mqueue_receive(
677 ipc_mqueue_t mqueue,
678 mach_msg_option_t option,
679 mach_msg_size_t max_size,
680 mach_msg_timeout_t rcv_timeout,
681 int interruptible)
682{
683 wait_result_t wresult;
684 thread_t self = current_thread();
685
686 wresult = ipc_mqueue_receive_on_thread(mqueue, option, max_size,
687 rcv_timeout, interruptible,
688 self);
689 if (wresult == THREAD_NOT_WAITING)
690 return;
691
692 if (wresult == THREAD_WAITING) {
693 counter((interruptible == THREAD_ABORTSAFE) ?
694 c_ipc_mqueue_receive_block_user++ :
695 c_ipc_mqueue_receive_block_kernel++);
696
697 if (self->ith_continuation)
698 thread_block(ipc_mqueue_receive_continue);
699 /* NOTREACHED */
700
701 wresult = thread_block(THREAD_CONTINUE_NULL);
702 }
703 ipc_mqueue_receive_results(wresult);
704}
705
706wait_result_t
707ipc_mqueue_receive_on_thread(
708 ipc_mqueue_t mqueue,
709 mach_msg_option_t option,
710 mach_msg_size_t max_size,
711 mach_msg_timeout_t rcv_timeout,
712 int interruptible,
713 thread_t thread)
714{
715 ipc_kmsg_queue_t kmsgs;
716 wait_result_t wresult;
717 uint64_t deadline;
718 spl_t s;
719#if CONFIG_MACF_MACH
720 ipc_labelh_t lh;
721 task_t task;
722 int rc;
723#endif
724
725 s = splsched();
726 imq_lock(mqueue);
727
728 if (imq_is_set(mqueue)) {
729 queue_t q;
730
731 q = &mqueue->imq_preposts;
732
733 /*
734 * If we are waiting on a portset mqueue, we need to see if
735 * any of the member ports have work for us. Ports that
736 * have (or recently had) messages will be linked in the
737 * prepost queue for the portset. By holding the portset's
738 * mqueue lock during the search, we tie up any attempts by
739 * mqueue_deliver or portset membership changes that may
740 * cross our path.
741 */
742 search_set:
743 while(!queue_empty(q)) {
744 wait_queue_link_t wql;
745 ipc_mqueue_t port_mq;
746
747 queue_remove_first(q, wql, wait_queue_link_t, wql_preposts);
748 assert(!wql_is_preposted(wql));
749
750 /*
751 * This is a lock order violation, so we have to do it
752 * "softly," putting the link back on the prepost list
753 * if it fails (at the tail is fine since the order of
754 * handling messages from different sources in a set is
755 * not guaranteed and we'd like to skip to the next source
756 * if one is available).
757 */
758 port_mq = (ipc_mqueue_t)wql->wql_queue;
759 if (!imq_lock_try(port_mq)) {
760 queue_enter(q, wql, wait_queue_link_t, wql_preposts);
761 imq_unlock(mqueue);
762 splx(s);
763 mutex_pause(0);
764 s = splsched();
765 imq_lock(mqueue);
766 goto search_set; /* start again at beginning - SMP */
767 }
768
769 /*
770 * If there are no messages on this queue, just skip it
771 * (we already removed the link from the set's prepost queue).
772 */
773 kmsgs = &port_mq->imq_messages;
774 if (ipc_kmsg_queue_first(kmsgs) == IKM_NULL) {
775 imq_unlock(port_mq);
776 continue;
777 }
778
779 /*
780 * There are messages, so reinsert the link back
781 * at the tail of the preposted queue (for fairness)
782 * while we still have the portset mqueue locked.
783 */
784 queue_enter(q, wql, wait_queue_link_t, wql_preposts);
785 imq_unlock(mqueue);
786
787 /*
788 * Continue on to handling the message with just
789 * the port mqueue locked.
790 */
791 ipc_mqueue_select_on_thread(port_mq, option, max_size, thread);
792 imq_unlock(port_mq);
793#if CONFIG_MACF_MACH
794 if (thread->task != TASK_NULL &&
795 thread->ith_kmsg != NULL &&
796 thread->ith_kmsg->ikm_sender != NULL) {
797 lh = thread->ith_kmsg->ikm_sender->label;
798 tasklabel_lock(thread->task);
799 ip_lock(lh->lh_port);
800 rc = mac_port_check_receive(&thread->task->maclabel,
801 &lh->lh_label);
802 ip_unlock(lh->lh_port);
803 tasklabel_unlock(thread->task);
804 if (rc)
805 thread->ith_state = MACH_RCV_INVALID_DATA;
806 }
807#endif
808 splx(s);
809 return THREAD_NOT_WAITING;
810
811 }
812
813 } else {
814
815 /*
816 * Receive on a single port. Just try to get the messages.
817 */
818 kmsgs = &mqueue->imq_messages;
819 if (ipc_kmsg_queue_first(kmsgs) != IKM_NULL) {
820 ipc_mqueue_select_on_thread(mqueue, option, max_size, thread);
821 imq_unlock(mqueue);
822#if CONFIG_MACF_MACH
823 if (thread->task != TASK_NULL &&
824 thread->ith_kmsg != NULL &&
825 thread->ith_kmsg->ikm_sender != NULL) {
826 lh = thread->ith_kmsg->ikm_sender->label;
827 tasklabel_lock(thread->task);
828 ip_lock(lh->lh_port);
829 rc = mac_port_check_receive(&thread->task->maclabel,
830 &lh->lh_label);
831 ip_unlock(lh->lh_port);
832 tasklabel_unlock(thread->task);
833 if (rc)
834 thread->ith_state = MACH_RCV_INVALID_DATA;
835 }
836#endif
837 splx(s);
838 return THREAD_NOT_WAITING;
839 }
840 }
841
842 /*
843 * Looks like we'll have to block. The mqueue we will
844 * block on (whether the set's or the local port's) is
845 * still locked.
846 */
847 if (option & MACH_RCV_TIMEOUT) {
848 if (rcv_timeout == 0) {
849 imq_unlock(mqueue);
850 splx(s);
851 thread->ith_state = MACH_RCV_TIMED_OUT;
852 return THREAD_NOT_WAITING;
853 }
854 }
855
856 thread_lock(thread);
857 thread->ith_state = MACH_RCV_IN_PROGRESS;
858 thread->ith_option = option;
859 thread->ith_msize = max_size;
860
861 if (option & MACH_RCV_TIMEOUT)
862 clock_interval_to_deadline(rcv_timeout, 1000*NSEC_PER_USEC, &deadline);
863 else
864 deadline = 0;
865
866 wresult = wait_queue_assert_wait64_locked(&mqueue->imq_wait_queue,
867 IPC_MQUEUE_RECEIVE,
868 interruptible, deadline,
869 thread);
870 /* preposts should be detected above, not here */
871 if (wresult == THREAD_AWAKENED)
872 panic("ipc_mqueue_receive_on_thread: sleep walking");
873
874 thread_unlock(thread);
875 imq_unlock(mqueue);
876 splx(s);
877 return wresult;
878}
879
880
881/*
882 * Routine: ipc_mqueue_select_on_thread
883 * Purpose:
884 * A receiver discovered that there was a message on the queue
885 * before he had to block. Pick the message off the queue and
886 * "post" it to thread.
887 * Conditions:
888 * mqueue locked.
889 * thread not locked.
890 * There is a message.
891 * Returns:
892 * MACH_MSG_SUCCESS Actually selected a message for ourselves.
893 * MACH_RCV_TOO_LARGE May or may not have pull it, but it is large
894 */
895void
896ipc_mqueue_select_on_thread(
897 ipc_mqueue_t mqueue,
898 mach_msg_option_t option,
899 mach_msg_size_t max_size,
900 thread_t thread)
901{
902 ipc_kmsg_t kmsg;
903 mach_msg_return_t mr = MACH_MSG_SUCCESS;
904 mach_msg_size_t rcv_size;
905
906 /*
907 * Do some sanity checking of our ability to receive
908 * before pulling the message off the queue.
909 */
910 kmsg = ipc_kmsg_queue_first(&mqueue->imq_messages);
911 assert(kmsg != IKM_NULL);
912
913 /*
914 * If we really can't receive it, but we had the
915 * MACH_RCV_LARGE option set, then don't take it off
916 * the queue, instead return the appropriate error
917 * (and size needed).
918 */
919 rcv_size = ipc_kmsg_copyout_size(kmsg, thread->map);
920 if (rcv_size + REQUESTED_TRAILER_SIZE(option) > max_size) {
921 mr = MACH_RCV_TOO_LARGE;
922 if (option & MACH_RCV_LARGE) {
923 thread->ith_receiver_name = mqueue->imq_receiver_name;
924 thread->ith_kmsg = IKM_NULL;
925 thread->ith_msize = rcv_size;
926 thread->ith_seqno = 0;
927 thread->ith_state = mr;
928 return;
929 }
930 }
931
932 ipc_kmsg_rmqueue_first_macro(&mqueue->imq_messages, kmsg);
933 ipc_mqueue_release_msgcount(mqueue);
934 thread->ith_seqno = mqueue->imq_seqno++;
935 thread->ith_kmsg = kmsg;
936 thread->ith_state = mr;
937
938 current_task()->messages_received++;
939 return;
940}
941
942/*
943 * Routine: ipc_mqueue_peek
944 * Purpose:
945 * Peek at a message queue to see if it has any messages
946 * (in it or contained message queues for a set).
947 *
948 * Conditions:
949 * Locks may be held by callers, so this routine cannot block.
950 * Caller holds reference on the message queue.
951 */
952unsigned
953ipc_mqueue_peek(ipc_mqueue_t mq)
954{
955 wait_queue_link_t wql;
956 queue_t q;
957 spl_t s;
958
959 if (!imq_is_set(mq))
960 return (ipc_kmsg_queue_first(&mq->imq_messages) != IKM_NULL);
961
962 /*
963 * Don't block trying to get the lock.
964 */
965 s = splsched();
966 imq_lock(mq);
967
968 /*
969 * peek at the contained port message queues, return as soon as
970 * we spot a message on one of the message queues linked on the
971 * prepost list.
972 */
973 q = &mq->imq_preposts;
974 queue_iterate(q, wql, wait_queue_link_t, wql_preposts) {
975 ipc_mqueue_t port_mq = (ipc_mqueue_t)wql->wql_queue;
976 ipc_kmsg_queue_t kmsgs = &port_mq->imq_messages;
977
978 if (ipc_kmsg_queue_first(kmsgs) != IKM_NULL) {
979 imq_unlock(mq);
980 splx(s);
981 return 1;
982 }
983 }
984 imq_unlock(mq);
985 splx(s);
986 return 0;
987}
988
989/*
990 * Routine: ipc_mqueue_destroy
991 * Purpose:
992 * Destroy a (non-set) message queue.
993 * Set any blocked senders running.
994 * Destroy the kmsgs in the queue.
995 * Conditions:
996 * Nothing locked.
997 * Receivers were removed when the receive right was "changed"
998 */
999void
1000ipc_mqueue_destroy(
1001 ipc_mqueue_t mqueue)
1002{
1003 ipc_kmsg_queue_t kmqueue;
1004 ipc_kmsg_t kmsg;
1005 boolean_t reap = FALSE;
1006 spl_t s;
1007
1008
1009 s = splsched();
1010 imq_lock(mqueue);
1011 /*
1012 * rouse all blocked senders
1013 */
1014 mqueue->imq_fullwaiters = FALSE;
1015 wait_queue_wakeup64_all_locked(
1016 &mqueue->imq_wait_queue,
1017 IPC_MQUEUE_FULL,
1018 THREAD_RESTART,
1019 FALSE);
1020
1021 /*
1022 * Move messages from the specified queue to the per-thread
1023 * clean/drain queue while we have the mqueue lock.
1024 */
1025 kmqueue = &mqueue->imq_messages;
1026 while ((kmsg = ipc_kmsg_dequeue(kmqueue)) != IKM_NULL) {
1027 boolean_t first;
1028 first = ipc_kmsg_delayed_destroy(kmsg);
1029 if (first)
1030 reap = first;
1031 }
1032
1033 imq_unlock(mqueue);
1034 splx(s);
1035
1036 /*
1037 * Destroy the messages we enqueued if we aren't nested
1038 * inside some other attempt to drain the same queue.
1039 */
1040 if (reap)
1041 ipc_kmsg_reap_delayed();
1042}
1043
1044/*
1045 * Routine: ipc_mqueue_set_qlimit
1046 * Purpose:
1047 * Changes a message queue limit; the maximum number
1048 * of messages which may be queued.
1049 * Conditions:
1050 * Nothing locked.
1051 */
1052
1053void
1054ipc_mqueue_set_qlimit(
1055 ipc_mqueue_t mqueue,
1056 mach_port_msgcount_t qlimit)
1057{
1058 spl_t s;
1059
1060 assert(qlimit <= MACH_PORT_QLIMIT_MAX);
1061
1062 /* wake up senders allowed by the new qlimit */
1063 s = splsched();
1064 imq_lock(mqueue);
1065 if (qlimit > mqueue->imq_qlimit) {
1066 mach_port_msgcount_t i, wakeup;
1067
1068 /* caution: wakeup, qlimit are unsigned */
1069 wakeup = qlimit - mqueue->imq_qlimit;
1070
1071 for (i = 0; i < wakeup; i++) {
1072 if (wait_queue_wakeup64_one_locked(
1073 &mqueue->imq_wait_queue,
1074 IPC_MQUEUE_FULL,
1075 THREAD_AWAKENED,
1076 FALSE) == KERN_NOT_WAITING) {
1077 mqueue->imq_fullwaiters = FALSE;
1078 break;
1079 }
1080 mqueue->imq_msgcount++; /* give it to the awakened thread */
1081 }
1082 }
1083 mqueue->imq_qlimit = qlimit;
1084 imq_unlock(mqueue);
1085 splx(s);
1086}
1087
1088/*
1089 * Routine: ipc_mqueue_set_seqno
1090 * Purpose:
1091 * Changes an mqueue's sequence number.
1092 * Conditions:
1093 * Caller holds a reference to the queue's containing object.
1094 */
1095void
1096ipc_mqueue_set_seqno(
1097 ipc_mqueue_t mqueue,
1098 mach_port_seqno_t seqno)
1099{
1100 spl_t s;
1101
1102 s = splsched();
1103 imq_lock(mqueue);
1104 mqueue->imq_seqno = seqno;
1105 imq_unlock(mqueue);
1106 splx(s);
1107}
1108
1109
1110/*
1111 * Routine: ipc_mqueue_copyin
1112 * Purpose:
1113 * Convert a name in a space to a message queue.
1114 * Conditions:
1115 * Nothing locked. If successful, the caller gets a ref for
1116 * for the object. This ref ensures the continued existence of
1117 * the queue.
1118 * Returns:
1119 * MACH_MSG_SUCCESS Found a message queue.
1120 * MACH_RCV_INVALID_NAME The space is dead.
1121 * MACH_RCV_INVALID_NAME The name doesn't denote a right.
1122 * MACH_RCV_INVALID_NAME
1123 * The denoted right is not receive or port set.
1124 * MACH_RCV_IN_SET Receive right is a member of a set.
1125 */
1126
1127mach_msg_return_t
1128ipc_mqueue_copyin(
1129 ipc_space_t space,
1130 mach_port_name_t name,
1131 ipc_mqueue_t *mqueuep,
1132 ipc_object_t *objectp)
1133{
1134 ipc_entry_t entry;
1135 ipc_object_t object;
1136 ipc_mqueue_t mqueue;
1137
1138 is_read_lock(space);
1139 if (!space->is_active) {
1140 is_read_unlock(space);
1141 return MACH_RCV_INVALID_NAME;
1142 }
1143
1144 entry = ipc_entry_lookup(space, name);
1145 if (entry == IE_NULL) {
1146 is_read_unlock(space);
1147 return MACH_RCV_INVALID_NAME;
1148 }
1149
1150 object = entry->ie_object;
1151
1152 if (entry->ie_bits & MACH_PORT_TYPE_RECEIVE) {
1153 ipc_port_t port;
1154
1155 port = (ipc_port_t) object;
1156 assert(port != IP_NULL);
1157
1158 ip_lock(port);
1159 assert(ip_active(port));
1160 assert(port->ip_receiver_name == name);
1161 assert(port->ip_receiver == space);
1162 is_read_unlock(space);
1163 mqueue = &port->ip_messages;
1164
1165 } else if (entry->ie_bits & MACH_PORT_TYPE_PORT_SET) {
1166 ipc_pset_t pset;
1167
1168 pset = (ipc_pset_t) object;
1169 assert(pset != IPS_NULL);
1170
1171 ips_lock(pset);
1172 assert(ips_active(pset));
1173 assert(pset->ips_local_name == name);
1174 is_read_unlock(space);
1175
1176 mqueue = &pset->ips_messages;
1177 } else {
1178 is_read_unlock(space);
1179 return MACH_RCV_INVALID_NAME;
1180 }
1181
1182 /*
1183 * At this point, the object is locked and active,
1184 * the space is unlocked, and mqueue is initialized.
1185 */
1186
1187 io_reference(object);
1188 io_unlock(object);
1189
1190 *objectp = object;
1191 *mqueuep = mqueue;
1192 return MACH_MSG_SUCCESS;
1193}
1194