]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ipc/ipc_mqueue.c
xnu-344.49.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_mqueue.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
43866e37 6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
1c79356b 7 *
43866e37
A
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1c79356b
A
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
43866e37
A
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
1c79356b
A
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25/*
26 * @OSF_FREE_COPYRIGHT@
27 */
28/*
29 * Mach Operating System
30 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
31 * All Rights Reserved.
32 *
33 * Permission to use, copy, modify and distribute this software and its
34 * documentation is hereby granted, provided that both the copyright
35 * notice and this permission notice appear in all copies of the
36 * software, derivative works or modified versions, and any portions
37 * thereof, and that both notices appear in supporting documentation.
38 *
39 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
40 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
41 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
42 *
43 * Carnegie Mellon requests users of this software to return to
44 *
45 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
46 * School of Computer Science
47 * Carnegie Mellon University
48 * Pittsburgh PA 15213-3890
49 *
50 * any improvements or extensions that they make and grant Carnegie Mellon
51 * the rights to redistribute these changes.
52 */
53/*
54 */
55/*
56 * File: ipc/ipc_mqueue.c
57 * Author: Rich Draves
58 * Date: 1989
59 *
60 * Functions to manipulate IPC message queues.
61 */
62
63#include <mach/port.h>
64#include <mach/message.h>
65#include <mach/sync_policy.h>
66
67#include <kern/assert.h>
68#include <kern/counters.h>
69#include <kern/sched_prim.h>
70#include <kern/ipc_kobject.h>
71#include <kern/misc_protos.h>
72#include <kern/task.h>
73#include <kern/thread.h>
74#include <kern/wait_queue.h>
75
76#include <ipc/ipc_mqueue.h>
77#include <ipc/ipc_kmsg.h>
78#include <ipc/ipc_port.h>
79#include <ipc/ipc_pset.h>
80#include <ipc/ipc_space.h>
81
82#include <ddb/tr.h>
83
84int ipc_mqueue_full; /* address is event for queue space */
85int ipc_mqueue_rcv; /* address is event for message arrival */
86
87#define TR_ENABLE 0
88
89/*
90 * Routine: ipc_mqueue_init
91 * Purpose:
92 * Initialize a newly-allocated message queue.
93 */
94void
95ipc_mqueue_init(
96 ipc_mqueue_t mqueue,
97 boolean_t is_set)
98{
99 if (is_set) {
9bccf70c 100 wait_queue_set_init(&mqueue->imq_set_queue, SYNC_POLICY_FIFO);
1c79356b
A
101 } else {
102 wait_queue_init(&mqueue->imq_wait_queue, SYNC_POLICY_FIFO);
103 ipc_kmsg_queue_init(&mqueue->imq_messages);
104 mqueue->imq_seqno = 0;
105 mqueue->imq_msgcount = 0;
106 mqueue->imq_qlimit = MACH_PORT_QLIMIT_DEFAULT;
107 mqueue->imq_fullwaiters = FALSE;
108 }
109}
110
111/*
112 * Routine: ipc_mqueue_member
113 * Purpose:
114 * Indicate whether the (port) mqueue is a member of
115 * this portset's mqueue. We do this by checking
116 * whether the portset mqueue's waitq is an member of
117 * the port's mqueue waitq.
118 * Conditions:
119 * the portset's mqueue is not already a member
120 * this may block while allocating linkage structures.
121 */
122
123boolean_t
124ipc_mqueue_member(
125 ipc_mqueue_t port_mqueue,
126 ipc_mqueue_t set_mqueue)
127{
128 wait_queue_t port_waitq = &port_mqueue->imq_wait_queue;
129 wait_queue_t set_waitq = &set_mqueue->imq_wait_queue;
130
131 return (wait_queue_member(port_waitq, set_waitq));
132
133}
134
135/*
136 * Routine: ipc_mqueue_remove
137 * Purpose:
138 * Remove the association between the queue and the specified
9bccf70c 139 * set message queue.
1c79356b
A
140 */
141
142kern_return_t
143ipc_mqueue_remove(
144 ipc_mqueue_t mqueue,
9bccf70c 145 ipc_mqueue_t set_mqueue)
1c79356b
A
146{
147 wait_queue_t mq_waitq = &mqueue->imq_wait_queue;
9bccf70c 148 wait_queue_set_t set_waitq = &set_mqueue->imq_set_queue;
1c79356b 149
9bccf70c 150 return wait_queue_unlink(mq_waitq, set_waitq);
1c79356b
A
151}
152
153/*
9bccf70c 154 * Routine: ipc_mqueue_remove_from_all
1c79356b 155 * Purpose:
9bccf70c 156 * Remove the mqueue from all the sets it is a member of
1c79356b 157 * Conditions:
9bccf70c 158 * Nothing locked.
1c79356b
A
159 */
160void
9bccf70c
A
161ipc_mqueue_remove_from_all(
162 ipc_mqueue_t mqueue)
1c79356b
A
163{
164 wait_queue_t mq_waitq = &mqueue->imq_wait_queue;
165
9bccf70c
A
166 wait_queue_unlink_all(mq_waitq);
167 return;
168}
169
170/*
171 * Routine: ipc_mqueue_remove_all
172 * Purpose:
173 * Remove all the member queues from the specified set.
174 * Conditions:
175 * Nothing locked.
176 */
177void
178ipc_mqueue_remove_all(
179 ipc_mqueue_t mqueue)
180{
181 wait_queue_set_t mq_setq = &mqueue->imq_set_queue;
182
183 wait_queue_set_unlink_all(mq_setq);
1c79356b
A
184 return;
185}
186
187
188/*
189 * Routine: ipc_mqueue_add
190 * Purpose:
191 * Associate the portset's mqueue with the port's mqueue.
192 * This has to be done so that posting the port will wakeup
193 * a portset waiter. If there are waiters on the portset
194 * mqueue and messages on the port mqueue, try to match them
195 * up now.
196 * Conditions:
197 * May block.
198 */
199kern_return_t
200ipc_mqueue_add(
201 ipc_mqueue_t port_mqueue,
202 ipc_mqueue_t set_mqueue)
203{
204 wait_queue_t port_waitq = &port_mqueue->imq_wait_queue;
9bccf70c 205 wait_queue_set_t set_waitq = &set_mqueue->imq_set_queue;
1c79356b
A
206 ipc_kmsg_queue_t kmsgq;
207 ipc_kmsg_t kmsg, next;
208 kern_return_t kr;
209 spl_t s;
210
211 kr = wait_queue_link(port_waitq, set_waitq);
212 if (kr != KERN_SUCCESS)
213 return kr;
214
215 /*
216 * Now that the set has been added to the port, there may be
217 * messages queued on the port and threads waiting on the set
218 * waitq. Lets get them together.
219 */
220 s = splsched();
221 imq_lock(port_mqueue);
222 kmsgq = &port_mqueue->imq_messages;
223 for (kmsg = ipc_kmsg_queue_first(kmsgq);
224 kmsg != IKM_NULL;
225 kmsg = next) {
226 next = ipc_kmsg_queue_next(kmsgq, kmsg);
227
228 for (;;) {
229 thread_t th;
230
9bccf70c
A
231 th = wait_queue_wakeup64_identity_locked(
232 port_waitq,
233 IPC_MQUEUE_RECEIVE,
234 THREAD_AWAKENED,
235 FALSE);
1c79356b
A
236 /* waitq/mqueue still locked, thread locked */
237
238 if (th == THREAD_NULL)
239 goto leave;
240
241 /*
242 * Found a receiver. see if they can handle the message
243 * correctly (the message is not too large for them, or
244 * they didn't care to be informed that the message was
245 * too large). If they can't handle it, take them off
246 * the list and let them go back and figure it out and
247 * just move onto the next.
248 */
249 if (th->ith_msize <
250 kmsg->ikm_header.msgh_size +
251 REQUESTED_TRAILER_SIZE(th->ith_option)) {
252 th->ith_state = MACH_RCV_TOO_LARGE;
253 th->ith_msize = kmsg->ikm_header.msgh_size;
254 if (th->ith_option & MACH_RCV_LARGE) {
255 /*
256 * let him go without message
257 */
258 th->ith_kmsg = IKM_NULL;
259 th->ith_seqno = 0;
260 thread_unlock(th);
261 continue; /* find another thread */
262 }
263 } else {
264 th->ith_state = MACH_MSG_SUCCESS;
265 }
266
267 /*
268 * This thread is going to take this message,
269 * so give it to him.
270 */
271 ipc_mqueue_release_msgcount(port_mqueue);
272 ipc_kmsg_rmqueue(kmsgq, kmsg);
273 th->ith_kmsg = kmsg;
274 th->ith_seqno = port_mqueue->imq_seqno++;
275 thread_unlock(th);
276 break; /* go to next message */
277 }
278
279 }
280 leave:
281 imq_unlock(port_mqueue);
282 splx(s);
283 return KERN_SUCCESS;
284}
285
286/*
287 * Routine: ipc_mqueue_changed
288 * Purpose:
289 * Wake up receivers waiting in a message queue.
290 * Conditions:
291 * The message queue is locked.
292 */
293
294void
295ipc_mqueue_changed(
296 ipc_mqueue_t mqueue)
297{
9bccf70c
A
298 wait_queue_wakeup64_all_locked(
299 &mqueue->imq_wait_queue,
300 IPC_MQUEUE_RECEIVE,
301 THREAD_RESTART,
302 FALSE); /* unlock waitq? */
1c79356b
A
303}
304
305
306
307
308/*
309 * Routine: ipc_mqueue_send
310 * Purpose:
311 * Send a message to a message queue. The message holds a reference
312 * for the destination port for this message queue in the
313 * msgh_remote_port field.
314 *
315 * If unsuccessful, the caller still has possession of
316 * the message and must do something with it. If successful,
317 * the message is queued, given to a receiver, or destroyed.
318 * Conditions:
319 * Nothing locked.
320 * Returns:
321 * MACH_MSG_SUCCESS The message was accepted.
322 * MACH_SEND_TIMED_OUT Caller still has message.
323 * MACH_SEND_INTERRUPTED Caller still has message.
324 */
325mach_msg_return_t
326ipc_mqueue_send(
327 ipc_mqueue_t mqueue,
328 ipc_kmsg_t kmsg,
329 mach_msg_option_t option,
330 mach_msg_timeout_t timeout)
331{
9bccf70c 332 int wresult;
1c79356b
A
333 spl_t s;
334
335 /*
336 * Don't block if:
337 * 1) We're under the queue limit.
338 * 2) Caller used the MACH_SEND_ALWAYS internal option.
339 * 3) Message is sent to a send-once right.
340 */
341 s = splsched();
342 imq_lock(mqueue);
343
344 if (!imq_full(mqueue) ||
345 (option & MACH_SEND_ALWAYS) ||
346 (MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits) ==
347 MACH_MSG_TYPE_PORT_SEND_ONCE)) {
348 mqueue->imq_msgcount++;
349 imq_unlock(mqueue);
350 splx(s);
351 } else {
352
353 /*
354 * We have to wait for space to be granted to us.
355 */
356 if ((option & MACH_SEND_TIMEOUT) && (timeout == 0)) {
357 imq_unlock(mqueue);
358 splx(s);
359 return MACH_SEND_TIMED_OUT;
360 }
361 mqueue->imq_fullwaiters = TRUE;
9bccf70c
A
362 wresult = wait_queue_assert_wait64_locked(
363 &mqueue->imq_wait_queue,
364 IPC_MQUEUE_FULL,
365 THREAD_ABORTSAFE,
366 TRUE); /* unlock? */
1c79356b
A
367 /* wait/mqueue is unlocked */
368 splx(s);
369
9bccf70c
A
370 if (wresult == THREAD_WAITING) {
371 if (option & MACH_SEND_TIMEOUT) {
372 thread_set_timer(timeout, 1000*NSEC_PER_USEC);
373 wresult = thread_block(THREAD_CONTINUE_NULL);
374 if (wresult != THREAD_TIMED_OUT)
375 thread_cancel_timer();
376 } else {
377 wresult = thread_block(THREAD_CONTINUE_NULL);
378 }
379 counter(c_ipc_mqueue_send_block++);
380 }
1c79356b 381
9bccf70c 382 switch (wresult) {
1c79356b
A
383 case THREAD_TIMED_OUT:
384 assert(option & MACH_SEND_TIMEOUT);
385 return MACH_SEND_TIMED_OUT;
386
387 case THREAD_AWAKENED:
388 /* we can proceed - inherited msgcount from waker */
1c79356b
A
389 break;
390
391 case THREAD_INTERRUPTED:
1c79356b
A
392 return MACH_SEND_INTERRUPTED;
393
394 case THREAD_RESTART:
395 default:
396 panic("ipc_mqueue_send");
397 }
398 }
399
400 ipc_mqueue_post(mqueue, kmsg);
401 return MACH_MSG_SUCCESS;
402}
403
404/*
405 * Routine: ipc_mqueue_release_msgcount
406 * Purpose:
407 * Release a message queue reference in the case where we
408 * found a waiter.
409 *
410 * Conditions:
411 * The message queue is locked
412 */
413void
414ipc_mqueue_release_msgcount(
415 ipc_mqueue_t mqueue)
416{
417 assert(imq_held(mqueue));
418 assert(mqueue->imq_msgcount > 0);
419
420 mqueue->imq_msgcount--;
421 if (!imq_full(mqueue) && mqueue->imq_fullwaiters) {
9bccf70c
A
422 if (wait_queue_wakeup64_one_locked(
423 &mqueue->imq_wait_queue,
424 IPC_MQUEUE_FULL,
425 THREAD_AWAKENED,
426 FALSE) != KERN_SUCCESS) {
1c79356b
A
427 mqueue->imq_fullwaiters = FALSE;
428 } else {
429 mqueue->imq_msgcount++; /* gave it away */
430 }
431 }
432}
433
434/*
435 * Routine: ipc_mqueue_post
436 * Purpose:
437 * Post a message to a waiting receiver or enqueue it. If a
438 * receiver is waiting, we can release our reserved space in
439 * the message queue.
440 *
441 * Conditions:
442 * If we need to queue, our space in the message queue is reserved.
443 */
444void
445ipc_mqueue_post(
446 register ipc_mqueue_t mqueue,
447 register ipc_kmsg_t kmsg)
448{
449
450 spl_t s;
451
452 /*
453 * While the msg queue is locked, we have control of the
454 * kmsg, so the ref in it for the port is still good.
455 *
456 * Check for a receiver for the message.
457 */
458 s = splsched();
459 imq_lock(mqueue);
460 for (;;) {
461 wait_queue_t waitq = &mqueue->imq_wait_queue;
462 thread_t receiver;
463
9bccf70c
A
464 receiver = wait_queue_wakeup64_identity_locked(
465 waitq,
466 IPC_MQUEUE_RECEIVE,
467 THREAD_AWAKENED,
468 FALSE);
1c79356b
A
469 /* waitq still locked, thread locked */
470
471 if (receiver == THREAD_NULL) {
472 /*
473 * no receivers; queue kmsg
474 */
475 assert(mqueue->imq_msgcount > 0);
476 ipc_kmsg_enqueue_macro(&mqueue->imq_messages, kmsg);
477 break;
478 }
479
480 /*
481 * We found a waiting thread.
482 * If the message is too large or the scatter list is too small
483 * the thread we wake up will get that as its status.
484 */
485 if (receiver->ith_msize <
486 (kmsg->ikm_header.msgh_size) +
487 REQUESTED_TRAILER_SIZE(receiver->ith_option)) {
488 receiver->ith_msize = kmsg->ikm_header.msgh_size;
489 receiver->ith_state = MACH_RCV_TOO_LARGE;
490 } else {
491 receiver->ith_state = MACH_MSG_SUCCESS;
492 }
493
494 /*
495 * If there is no problem with the upcoming receive, or the
496 * receiver thread didn't specifically ask for special too
497 * large error condition, go ahead and select it anyway.
498 */
499 if ((receiver->ith_state == MACH_MSG_SUCCESS) ||
500 !(receiver->ith_option & MACH_RCV_LARGE)) {
501
502 receiver->ith_kmsg = kmsg;
503 receiver->ith_seqno = mqueue->imq_seqno++;
504 thread_unlock(receiver);
505
506 /* we didn't need our reserved spot in the queue */
507 ipc_mqueue_release_msgcount(mqueue);
508 break;
509 }
510
511 /*
512 * Otherwise, this thread needs to be released to run
513 * and handle its error without getting the message. We
514 * need to go back and pick another one.
515 */
516 receiver->ith_kmsg = IKM_NULL;
517 receiver->ith_seqno = 0;
518 thread_unlock(receiver);
519 }
520
521 imq_unlock(mqueue);
522 splx(s);
523
524 current_task()->messages_sent++;
525 return;
526}
527
528
529kern_return_t
530ipc_mqueue_receive_results(void)
531{
532 thread_t self = current_thread();
533 mach_msg_option_t option = self->ith_option;
534 kern_return_t saved_wait_result = self->wait_result;
535 kern_return_t mr;
536
537 /*
538 * why did we wake up?
539 */
540 switch (saved_wait_result) {
541 case THREAD_TIMED_OUT:
542 self->ith_state = MACH_RCV_TIMED_OUT;
543 return;
544
545 case THREAD_INTERRUPTED:
546 if (option & MACH_RCV_TIMEOUT)
547 thread_cancel_timer();
548 self->ith_state = MACH_RCV_INTERRUPTED;
549 return;
550
551 case THREAD_RESTART:
552 /* something bad happened to the port/set */
553 if (option & MACH_RCV_TIMEOUT)
554 thread_cancel_timer();
555 self->ith_state = MACH_RCV_PORT_CHANGED;
556 return;
557
558 case THREAD_AWAKENED:
559 /*
560 * We do not need to go select a message, somebody
561 * handed us one (or a too-large indication).
562 */
563 if (option & MACH_RCV_TIMEOUT)
564 thread_cancel_timer();
565
566 mr = MACH_MSG_SUCCESS;
567
568 switch (self->ith_state) {
569 case MACH_RCV_SCATTER_SMALL:
570 case MACH_RCV_TOO_LARGE:
571 /*
572 * Somebody tried to give us a too large
573 * message. If we indicated that we cared,
574 * then they only gave us the indication,
575 * otherwise they gave us the indication
576 * AND the message anyway.
577 */
578 if (option & MACH_RCV_LARGE) {
579 return;
580 }
581
582 case MACH_MSG_SUCCESS:
583 return;
584
585 default:
586 panic("ipc_mqueue_receive_results: strange ith_state");
587 }
588
589 default:
590 panic("ipc_mqueue_receive_results: strange wait_result");
591 }
592}
593
594void
595ipc_mqueue_receive_continue(void)
596{
597 ipc_mqueue_receive_results();
598 mach_msg_receive_continue(); /* hard-coded for now */
599}
600
601/*
602 * Routine: ipc_mqueue_receive
603 * Purpose:
604 * Receive a message from a message queue.
605 *
606 * If continuation is non-zero, then we might discard
607 * our kernel stack when we block. We will continue
608 * after unblocking by executing continuation.
609 *
610 * If resume is true, then we are resuming a receive
611 * operation after a blocked receive discarded our stack.
612 * Conditions:
613 * Our caller must hold a reference for the port or port set
614 * to which this queue belongs, to keep the queue
615 * from being deallocated.
616 *
617 * The kmsg is returned with clean header fields
618 * and with the circular bit turned off.
619 * Returns:
620 * MACH_MSG_SUCCESS Message returned in kmsgp.
621 * MACH_RCV_TOO_LARGE Message size returned in kmsgp.
622 * MACH_RCV_TIMED_OUT No message obtained.
623 * MACH_RCV_INTERRUPTED No message obtained.
624 * MACH_RCV_PORT_DIED Port/set died; no message.
625 * MACH_RCV_PORT_CHANGED Port moved into set; no msg.
626 *
627 */
628
629void
630ipc_mqueue_receive(
631 ipc_mqueue_t mqueue,
632 mach_msg_option_t option,
633 mach_msg_size_t max_size,
634 mach_msg_timeout_t timeout,
635 int interruptible)
636{
9bccf70c
A
637 ipc_port_t port;
638 mach_msg_return_t mr, mr2;
639 ipc_kmsg_queue_t kmsgs;
640 wait_result_t wresult;
641 thread_t self;
1c79356b
A
642 ipc_kmsg_t *kmsgp;
643 mach_port_seqno_t *seqnop;
644 spl_t s;
645
646 s = splsched();
647 imq_lock(mqueue);
648
649 if (imq_is_set(mqueue)) {
650 wait_queue_link_t wql;
651 ipc_mqueue_t port_mq;
652 queue_t q;
653
654 q = &mqueue->imq_setlinks;
655
656 /*
657 * If we are waiting on a portset mqueue, we need to see if
658 * any of the member ports have work for us. If so, try to
659 * deliver one of those messages. By holding the portset's
660 * mqueue lock during the search, we tie up any attempts by
661 * mqueue_deliver or portset membership changes that may
662 * cross our path. But this is a lock order violation, so we
663 * have to do it "softly." If we don't find a message waiting
664 * for us, we will assert our intention to wait while still
665 * holding that lock. When we release the lock, the deliver/
666 * change will succeed and find us.
667 */
668 search_set:
9bccf70c 669 queue_iterate(q, wql, wait_queue_link_t, wql_setlinks) {
1c79356b
A
670 port_mq = (ipc_mqueue_t)wql->wql_queue;
671 kmsgs = &port_mq->imq_messages;
672
673 if (!imq_lock_try(port_mq)) {
674 imq_unlock(mqueue);
675 splx(s);
676 delay(1);
677 s = splsched();
678 imq_lock(mqueue);
679 goto search_set; /* start again at beginning - SMP */
680 }
681
682 /*
683 * If there is still a message to be had, we will
684 * try to select it (may not succeed because of size
685 * and options). In any case, we deliver those
686 * results back to the user.
687 *
688 * We also move the port's linkage to the tail of the
689 * list for this set (fairness). Future versions will
690 * sort by timestamp or priority.
691 */
692 if (ipc_kmsg_queue_first(kmsgs) == IKM_NULL) {
693 imq_unlock(port_mq);
694 continue;
695 }
9bccf70c
A
696 queue_remove(q, wql, wait_queue_link_t, wql_setlinks);
697 queue_enter(q, wql, wait_queue_link_t, wql_setlinks);
1c79356b
A
698 imq_unlock(mqueue);
699
700 ipc_mqueue_select(port_mq, option, max_size);
701 imq_unlock(port_mq);
702 splx(s);
703 return;
704
705 }
706
707 } else {
708
709 /*
710 * Receive on a single port. Just try to get the messages.
711 */
712 kmsgs = &mqueue->imq_messages;
713 if (ipc_kmsg_queue_first(kmsgs) != IKM_NULL) {
714 ipc_mqueue_select(mqueue, option, max_size);
715 imq_unlock(mqueue);
716 splx(s);
717 return;
718 }
719 }
720
721 /*
722 * Looks like we'll have to block. The mqueue we will
723 * block on (whether the set's or the local port's) is
724 * still locked.
725 */
726 self = current_thread();
727 if (option & MACH_RCV_TIMEOUT) {
728 if (timeout == 0) {
729 imq_unlock(mqueue);
730 splx(s);
731 self->ith_state = MACH_RCV_TIMED_OUT;
732 return;
733 }
734 }
735
736 self->ith_state = MACH_RCV_IN_PROGRESS;
737 self->ith_option = option;
738 self->ith_msize = max_size;
739
9bccf70c
A
740 wresult = wait_queue_assert_wait64_locked(&mqueue->imq_wait_queue,
741 IPC_MQUEUE_RECEIVE,
742 interruptible,
743 TRUE); /* unlock? */
1c79356b
A
744 /* mqueue/waitq is unlocked */
745 splx(s);
746
9bccf70c
A
747 if (wresult == THREAD_WAITING) {
748 if (option & MACH_RCV_TIMEOUT)
749 thread_set_timer(timeout, 1000*NSEC_PER_USEC);
1c79356b 750
9bccf70c
A
751 if (interruptible == THREAD_ABORTSAFE)
752 counter(c_ipc_mqueue_receive_block_user++);
753 else
754 counter(c_ipc_mqueue_receive_block_kernel++);
1c79356b 755
9bccf70c
A
756 if (self->ith_continuation)
757 thread_block(ipc_mqueue_receive_continue);
758 /* NOTREACHED */
1c79356b 759
9bccf70c
A
760 thread_block(THREAD_CONTINUE_NULL);
761 }
762 ipc_mqueue_receive_results();
1c79356b
A
763}
764
765
766/*
767 * Routine: ipc_mqueue_select
768 * Purpose:
769 * A receiver discovered that there was a message on the queue
770 * before he had to block. Pick the message off the queue and
771 * "post" it to himself.
772 * Conditions:
773 * mqueue locked.
774 * There is a message.
775 * Returns:
776 * MACH_MSG_SUCCESS Actually selected a message for ourselves.
777 * MACH_RCV_TOO_LARGE May or may not have pull it, but it is large
778 */
779void
780ipc_mqueue_select(
781 ipc_mqueue_t mqueue,
782 mach_msg_option_t option,
783 mach_msg_size_t max_size)
784{
785 thread_t self = current_thread();
786 ipc_kmsg_t kmsg;
787 mach_port_seqno_t seqno;
788 mach_msg_return_t mr;
789
790 mr = MACH_MSG_SUCCESS;
791
792
793 /*
794 * Do some sanity checking of our ability to receive
795 * before pulling the message off the queue.
796 */
797 kmsg = ipc_kmsg_queue_first(&mqueue->imq_messages);
798
799 assert(kmsg != IKM_NULL);
800
801 if (kmsg->ikm_header.msgh_size +
802 REQUESTED_TRAILER_SIZE(option) > max_size) {
803 mr = MACH_RCV_TOO_LARGE;
804 }
805
806 /*
807 * If we really can't receive it, but we had the
808 * MACH_RCV_LARGE option set, then don't take it off
809 * the queue, instead return the appropriate error
810 * (and size needed).
811 */
812 if ((mr == MACH_RCV_TOO_LARGE) && (option & MACH_RCV_LARGE)) {
813 self->ith_kmsg = IKM_NULL;
814 self->ith_msize = kmsg->ikm_header.msgh_size;
815 self->ith_seqno = 0;
816 self->ith_state = mr;
817 return;
818 }
819
820 ipc_kmsg_rmqueue_first_macro(&mqueue->imq_messages, kmsg);
821 ipc_mqueue_release_msgcount(mqueue);
822 self->ith_seqno = mqueue->imq_seqno++;
823 self->ith_kmsg = kmsg;
824 self->ith_state = mr;
825
826 current_task()->messages_received++;
827 return;
828}
829
830/*
831 * Routine: ipc_mqueue_destroy
832 * Purpose:
833 * Destroy a message queue. Set any blocked senders running.
834 * Destroy the kmsgs in the queue.
835 * Conditions:
836 * Nothing locked.
837 * Receivers were removed when the receive right was "changed"
838 */
839void
840ipc_mqueue_destroy(
841 ipc_mqueue_t mqueue)
842{
843 ipc_kmsg_queue_t kmqueue;
844 ipc_kmsg_t kmsg;
845 spl_t s;
846
847
848 s = splsched();
849 imq_lock(mqueue);
850 /*
851 * rouse all blocked senders
852 */
853 mqueue->imq_fullwaiters = FALSE;
9bccf70c
A
854 wait_queue_wakeup64_all_locked(
855 &mqueue->imq_wait_queue,
856 IPC_MQUEUE_FULL,
857 THREAD_AWAKENED,
858 FALSE);
1c79356b
A
859
860 kmqueue = &mqueue->imq_messages;
861
862 while ((kmsg = ipc_kmsg_dequeue(kmqueue)) != IKM_NULL) {
863 imq_unlock(mqueue);
864 splx(s);
865
866 ipc_kmsg_destroy_dest(kmsg);
867
868 s = splsched();
869 imq_lock(mqueue);
870 }
871 imq_unlock(mqueue);
872 splx(s);
873}
874
875/*
876 * Routine: ipc_mqueue_set_qlimit
877 * Purpose:
878 * Changes a message queue limit; the maximum number
879 * of messages which may be queued.
880 * Conditions:
881 * Nothing locked.
882 */
883
884void
885ipc_mqueue_set_qlimit(
886 ipc_mqueue_t mqueue,
887 mach_port_msgcount_t qlimit)
888{
889 spl_t s;
890
891 /* wake up senders allowed by the new qlimit */
892 s = splsched();
893 imq_lock(mqueue);
894 if (qlimit > mqueue->imq_qlimit) {
895 mach_port_msgcount_t i, wakeup;
896
897 /* caution: wakeup, qlimit are unsigned */
898 wakeup = qlimit - mqueue->imq_qlimit;
899
900 for (i = 0; i < wakeup; i++) {
9bccf70c
A
901 if (wait_queue_wakeup64_one_locked(
902 &mqueue->imq_wait_queue,
903 IPC_MQUEUE_FULL,
904 THREAD_AWAKENED,
905 FALSE) == KERN_NOT_WAITING) {
1c79356b
A
906 mqueue->imq_fullwaiters = FALSE;
907 break;
908 }
909 }
910 }
911 mqueue->imq_qlimit = qlimit;
912 imq_unlock(mqueue);
913 splx(s);
914}
915
916/*
917 * Routine: ipc_mqueue_set_seqno
918 * Purpose:
919 * Changes an mqueue's sequence number.
920 * Conditions:
921 * Caller holds a reference to the queue's containing object.
922 */
923void
924ipc_mqueue_set_seqno(
925 ipc_mqueue_t mqueue,
926 mach_port_seqno_t seqno)
927{
928 spl_t s;
929
930 s = splsched();
931 imq_lock(mqueue);
932 mqueue->imq_seqno = seqno;
933 imq_unlock(mqueue);
934 splx(s);
935}
936
937
938/*
939 * Routine: ipc_mqueue_copyin
940 * Purpose:
941 * Convert a name in a space to a message queue.
942 * Conditions:
943 * Nothing locked. If successful, the caller gets a ref for
944 * for the object. This ref ensures the continued existence of
945 * the queue.
946 * Returns:
947 * MACH_MSG_SUCCESS Found a message queue.
948 * MACH_RCV_INVALID_NAME The space is dead.
949 * MACH_RCV_INVALID_NAME The name doesn't denote a right.
950 * MACH_RCV_INVALID_NAME
951 * The denoted right is not receive or port set.
952 * MACH_RCV_IN_SET Receive right is a member of a set.
953 */
954
955mach_msg_return_t
956ipc_mqueue_copyin(
957 ipc_space_t space,
958 mach_port_name_t name,
959 ipc_mqueue_t *mqueuep,
960 ipc_object_t *objectp)
961{
962 ipc_entry_t entry;
963 ipc_object_t object;
964 ipc_mqueue_t mqueue;
965
966 is_read_lock(space);
967 if (!space->is_active) {
968 is_read_unlock(space);
969 return MACH_RCV_INVALID_NAME;
970 }
971
972 entry = ipc_entry_lookup(space, name);
973 if (entry == IE_NULL) {
974 is_read_unlock(space);
975 return MACH_RCV_INVALID_NAME;
976 }
977
978 object = entry->ie_object;
979
980 if (entry->ie_bits & MACH_PORT_TYPE_RECEIVE) {
981 ipc_port_t port;
982 ipc_pset_t pset;
983
984 port = (ipc_port_t) object;
985 assert(port != IP_NULL);
986
987 ip_lock(port);
988 assert(ip_active(port));
989 assert(port->ip_receiver_name == name);
990 assert(port->ip_receiver == space);
991 is_read_unlock(space);
992 mqueue = &port->ip_messages;
993
994 } else if (entry->ie_bits & MACH_PORT_TYPE_PORT_SET) {
995 ipc_pset_t pset;
996
997 pset = (ipc_pset_t) object;
998 assert(pset != IPS_NULL);
999
1000 ips_lock(pset);
1001 assert(ips_active(pset));
1002 assert(pset->ips_local_name == name);
1003 is_read_unlock(space);
1004
1005 mqueue = &pset->ips_messages;
1006 } else {
1007 is_read_unlock(space);
1008 return MACH_RCV_INVALID_NAME;
1009 }
1010
1011 /*
1012 * At this point, the object is locked and active,
1013 * the space is unlocked, and mqueue is initialized.
1014 */
1015
1016 io_reference(object);
1017 io_unlock(object);
1018
1019 *objectp = object;
1020 *mqueuep = mqueue;
1021 return MACH_MSG_SUCCESS;
1022}
1023