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