]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ipc/mach_msg.c
xnu-517.tar.gz
[apple/xnu.git] / osfmk / ipc / mach_msg.c
CommitLineData
1c79356b 1/*
55e303ae 2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
1c79356b
A
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_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/mach_msg.c
57 * Author: Rich Draves
58 * Date: 1989
59 *
60 * Exported message traps. See mach/message.h.
61 */
62
63#include <cpus.h>
64
65#include <mach/kern_return.h>
66#include <mach/port.h>
67#include <mach/message.h>
68#include <mach/mig_errors.h>
69#include <kern/assert.h>
70#include <kern/counters.h>
71#include <kern/cpu_number.h>
72#include <kern/task.h>
73#include <kern/thread.h>
74#include <kern/lock.h>
75#include <kern/sched_prim.h>
76#include <kern/exception.h>
77#include <kern/misc_protos.h>
78#include <vm/vm_map.h>
79#include <ipc/ipc_kmsg.h>
80#include <ipc/ipc_mqueue.h>
81#include <ipc/ipc_object.h>
82#include <ipc/ipc_notify.h>
83#include <ipc/ipc_port.h>
84#include <ipc/ipc_pset.h>
85#include <ipc/ipc_space.h>
86#include <ipc/ipc_entry.h>
87#include <kern/kalloc.h>
88#include <kern/thread_swap.h>
89#include <kern/processor.h>
90
0b4e3aa0
A
91#include <kern/mk_sp.h>
92
1c79356b
A
93#include <machine/machine_routines.h>
94#include <sys/kdebug.h>
95
96/*
97 * Forward declarations
98 */
99
100mach_msg_return_t mach_msg_send(
101 mach_msg_header_t *msg,
102 mach_msg_option_t option,
103 mach_msg_size_t send_size,
104 mach_msg_timeout_t timeout,
105 mach_port_name_t notify);
106
107mach_msg_return_t mach_msg_receive(
108 mach_msg_header_t *msg,
109 mach_msg_option_t option,
110 mach_msg_size_t rcv_size,
111 mach_port_name_t rcv_name,
112 mach_msg_timeout_t timeout,
113 void (*continuation)(mach_msg_return_t),
114 mach_msg_size_t slist_size);
115
116
117mach_msg_return_t msg_receive_error(
118 ipc_kmsg_t kmsg,
119 mach_msg_header_t *msg,
120 mach_msg_option_t option,
121 mach_port_seqno_t seqno,
122 ipc_space_t space);
123
1c79356b 124security_token_t KERNEL_SECURITY_TOKEN = KERNEL_SECURITY_TOKEN_VALUE;
55e303ae 125audit_token_t KERNEL_AUDIT_TOKEN = KERNEL_AUDIT_TOKEN_VALUE;
1c79356b
A
126
127mach_msg_format_0_trailer_t trailer_template = {
128 /* mach_msg_trailer_type_t */ MACH_MSG_TRAILER_FORMAT_0,
129 /* mach_msg_trailer_size_t */ MACH_MSG_TRAILER_MINIMUM_SIZE,
130 /* mach_port_seqno_t */ 0,
131 /* security_token_t */ KERNEL_SECURITY_TOKEN_VALUE
132};
133
134/*
135 * Routine: mach_msg_send
136 * Purpose:
137 * Send a message.
138 * Conditions:
139 * Nothing locked.
140 * Returns:
141 * MACH_MSG_SUCCESS Sent the message.
142 * MACH_SEND_MSG_TOO_SMALL Message smaller than a header.
143 * MACH_SEND_NO_BUFFER Couldn't allocate buffer.
144 * MACH_SEND_INVALID_DATA Couldn't copy message data.
145 * MACH_SEND_INVALID_HEADER
146 * Illegal value in the message header bits.
147 * MACH_SEND_INVALID_DEST The space is dead.
148 * MACH_SEND_INVALID_NOTIFY Bad notify port.
149 * MACH_SEND_INVALID_DEST Can't copyin destination port.
150 * MACH_SEND_INVALID_REPLY Can't copyin reply port.
151 * MACH_SEND_TIMED_OUT Timeout expired without delivery.
152 * MACH_SEND_INTERRUPTED Delivery interrupted.
153 * MACH_SEND_NO_NOTIFY Can't allocate a msg-accepted request.
154 * MACH_SEND_WILL_NOTIFY Msg-accepted notif. requested.
155 * MACH_SEND_NOTIFY_IN_PROGRESS
156 * This space has already forced a message to this port.
157 */
158
159mach_msg_return_t
160mach_msg_send(
161 mach_msg_header_t *msg,
162 mach_msg_option_t option,
163 mach_msg_size_t send_size,
164 mach_msg_timeout_t timeout,
165 mach_port_name_t notify)
166{
167 ipc_space_t space = current_space();
168 vm_map_t map = current_map();
169 ipc_kmsg_t kmsg;
170 mach_msg_return_t mr;
171
172 mr = ipc_kmsg_get(msg, send_size, &kmsg);
173
174 if (mr != MACH_MSG_SUCCESS)
175 return mr;
176
177 if (option & MACH_SEND_CANCEL) {
178 if (notify == MACH_PORT_NULL)
179 mr = MACH_SEND_INVALID_NOTIFY;
180 else
181 mr = ipc_kmsg_copyin(kmsg, space, map, notify);
182 } else
183 mr = ipc_kmsg_copyin(kmsg, space, map, MACH_PORT_NULL);
184 if (mr != MACH_MSG_SUCCESS) {
185 ipc_kmsg_free(kmsg);
186 return mr;
187 }
188
189 mr = ipc_kmsg_send(kmsg, option & MACH_SEND_TIMEOUT, timeout);
190
191 if (mr != MACH_MSG_SUCCESS) {
192 mr |= ipc_kmsg_copyout_pseudo(kmsg, space, map, MACH_MSG_BODY_NULL);
193 (void) ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size);
194 }
195
196 return mr;
197}
198
199/*
200 * Routine: mach_msg_receive
201 * Purpose:
202 * Receive a message.
203 * Conditions:
204 * Nothing locked.
205 * Returns:
206 * MACH_MSG_SUCCESS Received a message.
207 * MACH_RCV_INVALID_NAME The name doesn't denote a right,
208 * or the denoted right is not receive or port set.
209 * MACH_RCV_IN_SET Receive right is a member of a set.
210 * MACH_RCV_TOO_LARGE Message wouldn't fit into buffer.
211 * MACH_RCV_TIMED_OUT Timeout expired without a message.
212 * MACH_RCV_INTERRUPTED Reception interrupted.
213 * MACH_RCV_PORT_DIED Port/set died while receiving.
214 * MACH_RCV_PORT_CHANGED Port moved into set while receiving.
215 * MACH_RCV_INVALID_DATA Couldn't copy to user buffer.
216 * MACH_RCV_INVALID_NOTIFY Bad notify port.
217 * MACH_RCV_HEADER_ERROR
218 */
219
220mach_msg_return_t
221mach_msg_receive_results(void)
222{
223 thread_t self = current_thread();
224 ipc_space_t space = current_space();
225 vm_map_t map = current_map();
226
227 ipc_object_t object = self->ith_object;
228 mach_msg_return_t mr = self->ith_state;
229 mach_msg_header_t *msg = self->ith_msg;
230 mach_msg_option_t option = self->ith_option;
231 ipc_kmsg_t kmsg = self->ith_kmsg;
232 mach_port_seqno_t seqno = self->ith_seqno;
233 mach_msg_size_t slist_size = self->ith_scatter_list_size;
234
235 mach_msg_format_0_trailer_t *trailer;
236
237 ipc_object_release(object);
238
239 if (mr != MACH_MSG_SUCCESS) {
240
241 if (mr == MACH_RCV_TOO_LARGE ) {
242 if (option & MACH_RCV_LARGE) {
243 /*
244 * We need to inform the user-level code that it needs more
245 * space. The value for how much space was returned in the
246 * msize save area instead of the message (which was left on
247 * the queue).
248 */
249 if (copyout((char *) &self->ith_msize,
250 (char *) &msg->msgh_size,
251 sizeof(mach_msg_size_t)))
252 mr = MACH_RCV_INVALID_DATA;
253 goto out;
254 }
255
256 if (msg_receive_error(kmsg, msg, option, seqno, space)
257 == MACH_RCV_INVALID_DATA)
258 mr = MACH_RCV_INVALID_DATA;
259 }
260 goto out;
261 }
262
263 trailer = (mach_msg_format_0_trailer_t *)
264 ((vm_offset_t)&kmsg->ikm_header +
265 round_msg(kmsg->ikm_header.msgh_size));
266 if (option & MACH_RCV_TRAILER_MASK) {
267 trailer->msgh_seqno = seqno;
268 trailer->msgh_trailer_size = REQUESTED_TRAILER_SIZE(option);
269 }
270
271 /*
272 * If MACH_RCV_OVERWRITE was specified, try to get the scatter
273 * list and verify it against the contents of the message. If
274 * there is any problem with it, we will continue without it as
275 * normal.
276 */
277 if (option & MACH_RCV_OVERWRITE) {
278 mach_msg_size_t slist_size = self->ith_scatter_list_size;
279 mach_msg_body_t *slist;
280
281 slist = ipc_kmsg_copyin_scatter(msg, slist_size, kmsg);
282 mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL, slist);
283 ipc_kmsg_free_scatter(slist, slist_size);
284 } else {
285 mr = ipc_kmsg_copyout(kmsg, space, map,
286 MACH_PORT_NULL, MACH_MSG_BODY_NULL);
287 }
288
289 if (mr != MACH_MSG_SUCCESS) {
290 if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
291 if (ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size +
292 trailer->msgh_trailer_size) == MACH_RCV_INVALID_DATA)
293 mr = MACH_RCV_INVALID_DATA;
294 }
295 else {
296 if (msg_receive_error(kmsg, msg, option, seqno, space)
297 == MACH_RCV_INVALID_DATA)
298 mr = MACH_RCV_INVALID_DATA;
299 }
300 goto out;
301 }
302 mr = ipc_kmsg_put(msg,
303 kmsg,
304 kmsg->ikm_header.msgh_size +
305 trailer->msgh_trailer_size);
306 out:
307 return mr;
308}
309
310mach_msg_return_t
311mach_msg_receive(
312 mach_msg_header_t *msg,
313 mach_msg_option_t option,
314 mach_msg_size_t rcv_size,
315 mach_port_name_t rcv_name,
316 mach_msg_timeout_t timeout,
317 void (*continuation)(mach_msg_return_t),
318 mach_msg_size_t slist_size)
319{
320 thread_t self = current_thread();
321 ipc_space_t space = current_space();
322 vm_map_t map = current_map();
323 ipc_object_t object;
324 ipc_mqueue_t mqueue;
325 ipc_kmsg_t kmsg;
326 mach_port_seqno_t seqno;
327 mach_msg_return_t mr;
328 mach_msg_body_t *slist;
329 mach_msg_format_0_trailer_t *trailer;
330
331 mr = ipc_mqueue_copyin(space, rcv_name, &mqueue, &object);
332 if (mr != MACH_MSG_SUCCESS) {
333 return mr;
334 }
335 /* hold ref for object */
336
337 self->ith_msg = msg;
338 self->ith_object = object;
339 self->ith_msize = rcv_size;
340 self->ith_option = option;
341 self->ith_scatter_list_size = slist_size;
342 self->ith_continuation = continuation;
343
344 ipc_mqueue_receive(mqueue, option, rcv_size, timeout, THREAD_ABORTSAFE);
0b4e3aa0
A
345 if ((option & MACH_RCV_TIMEOUT) && timeout == 0)
346 _mk_sp_thread_perhaps_yield(self);
1c79356b
A
347 return mach_msg_receive_results();
348}
349
350void
351mach_msg_receive_continue(void)
352{
353 thread_t self = current_thread();
354
355 (*self->ith_continuation)(mach_msg_receive_results());
356}
357
358/*
359 * Toggle this to compile the hotpath in/out
360 * If compiled in, the run-time toggle "enable_hotpath" below
361 * eases testing & debugging
362 */
363#define ENABLE_HOTPATH 1 /* Hacked on for now */
364
365#if ENABLE_HOTPATH
366/*
367 * These counters allow tracing of hotpath behavior under test loads.
368 * A couple key counters are unconditional (see below).
369 */
370#define HOTPATH_DEBUG 0 /* Toggle to include lots of counters */
371#if HOTPATH_DEBUG
372#define HOT(expr) expr
373
374unsigned int c_mmot_FIRST = 0; /* Unused First Counter */
375unsigned int c_mmot_combined_S_R = 0; /* hotpath candidates */
376unsigned int c_mach_msg_trap_switch_fast = 0; /* hotpath successes */
377unsigned int c_mmot_kernel_send = 0; /* kernel server */
378unsigned int c_mmot_cold_000 = 0; /* see below ... */
379unsigned int c_mmot_smallsendsize = 0;
380unsigned int c_mmot_oddsendsize = 0;
381unsigned int c_mmot_bigsendsize = 0;
382unsigned int c_mmot_copyinmsg_fail = 0;
383unsigned int c_mmot_g_slow_copyin3 = 0;
384unsigned int c_mmot_cold_006 = 0;
385unsigned int c_mmot_cold_007 = 0;
386unsigned int c_mmot_cold_008 = 0;
387unsigned int c_mmot_cold_009 = 0;
388unsigned int c_mmot_cold_010 = 0;
389unsigned int c_mmot_cold_012 = 0;
390unsigned int c_mmot_cold_013 = 0;
391unsigned int c_mmot_cold_014 = 0;
392unsigned int c_mmot_cold_016 = 0;
393unsigned int c_mmot_cold_018 = 0;
394unsigned int c_mmot_cold_019 = 0;
395unsigned int c_mmot_cold_020 = 0;
396unsigned int c_mmot_cold_021 = 0;
397unsigned int c_mmot_cold_022 = 0;
398unsigned int c_mmot_cold_023 = 0;
399unsigned int c_mmot_cold_024 = 0;
400unsigned int c_mmot_cold_025 = 0;
401unsigned int c_mmot_cold_026 = 0;
402unsigned int c_mmot_cold_027 = 0;
403unsigned int c_mmot_hot_fSR_ok = 0;
404unsigned int c_mmot_cold_029 = 0;
405unsigned int c_mmot_cold_030 = 0;
406unsigned int c_mmot_cold_031 = 0;
407unsigned int c_mmot_cold_032 = 0;
408unsigned int c_mmot_cold_033 = 0;
409unsigned int c_mmot_bad_rcvr = 0;
410unsigned int c_mmot_rcvr_swapped = 0;
411unsigned int c_mmot_rcvr_locked = 0;
412unsigned int c_mmot_rcvr_tswapped = 0;
413unsigned int c_mmot_rcvr_freed = 0;
414unsigned int c_mmot_g_slow_copyout6 = 0;
415unsigned int c_mmot_g_slow_copyout5 = 0;
416unsigned int c_mmot_cold_037 = 0;
417unsigned int c_mmot_cold_038 = 0;
418unsigned int c_mmot_cold_039 = 0;
419unsigned int c_mmot_g_slow_copyout4 = 0;
420unsigned int c_mmot_g_slow_copyout3 = 0;
421unsigned int c_mmot_hot_ok1 = 0;
422unsigned int c_mmot_hot_ok2 = 0;
423unsigned int c_mmot_hot_ok3 = 0;
424unsigned int c_mmot_g_slow_copyout1 = 0;
425unsigned int c_mmot_g_slow_copyout2 = 0;
426unsigned int c_mmot_getback_fast_copyin = 0;
427unsigned int c_mmot_cold_048 = 0;
428unsigned int c_mmot_getback_FastSR = 0;
429unsigned int c_mmot_cold_050 = 0;
430unsigned int c_mmot_cold_051 = 0;
431unsigned int c_mmot_cold_052 = 0;
432unsigned int c_mmot_cold_053 = 0;
433unsigned int c_mmot_fastkernelreply = 0;
434unsigned int c_mmot_cold_055 = 0;
435unsigned int c_mmot_getback_fast_put = 0;
436unsigned int c_mmot_LAST = 0; /* End Marker - Unused */
437
438void db_mmot_zero_counters(void); /* forward; */
439void db_mmot_show_counters(void); /* forward; */
440
441void /* Call from the debugger to clear all counters */
442db_mmot_zero_counters(void)
443{
444 register unsigned int *ip = &c_mmot_FIRST;
445 while (ip <= &c_mmot_LAST)
446 *ip++ = 0;
447}
448
449void /* Call from the debugger to show all counters */
450db_mmot_show_counters(void)
451{
452#define xx(str) printf("%s: %d\n", # str, str);
453
454 xx(c_mmot_combined_S_R);
455 xx(c_mach_msg_trap_switch_fast);
456 xx(c_mmot_kernel_send);
457 xx(c_mmot_cold_000);
458 xx(c_mmot_smallsendsize);
459 xx(c_mmot_oddsendsize);
460 xx(c_mmot_bigsendsize);
461 xx(c_mmot_copyinmsg_fail);
462 xx(c_mmot_g_slow_copyin3);
463 xx(c_mmot_cold_006);
464 xx(c_mmot_cold_007);
465 xx(c_mmot_cold_008);
466 xx(c_mmot_cold_009);
467 xx(c_mmot_cold_010);
468 xx(c_mmot_cold_012);
469 xx(c_mmot_cold_013);
470 xx(c_mmot_cold_014);
471 xx(c_mmot_cold_016);
472 xx(c_mmot_cold_018);
473 xx(c_mmot_cold_019);
474 xx(c_mmot_cold_020);
475 xx(c_mmot_cold_021);
476 xx(c_mmot_cold_022);
477 xx(c_mmot_cold_023);
478 xx(c_mmot_cold_024);
479 xx(c_mmot_cold_025);
480 xx(c_mmot_cold_026);
481 xx(c_mmot_cold_027);
482 xx(c_mmot_hot_fSR_ok);
483 xx(c_mmot_cold_029);
484 xx(c_mmot_cold_030);
485 xx(c_mmot_cold_031);
486 xx(c_mmot_cold_032);
487 xx(c_mmot_cold_033);
488 xx(c_mmot_bad_rcvr);
489 xx(c_mmot_rcvr_swapped);
490 xx(c_mmot_rcvr_locked);
491 xx(c_mmot_rcvr_tswapped);
492 xx(c_mmot_rcvr_freed);
493 xx(c_mmot_g_slow_copyout6);
494 xx(c_mmot_g_slow_copyout5);
495 xx(c_mmot_cold_037);
496 xx(c_mmot_cold_038);
497 xx(c_mmot_cold_039);
498 xx(c_mmot_g_slow_copyout4);
499 xx(c_mmot_g_slow_copyout3);
500 xx(c_mmot_g_slow_copyout1);
501 xx(c_mmot_hot_ok3);
502 xx(c_mmot_hot_ok2);
503 xx(c_mmot_hot_ok1);
504 xx(c_mmot_g_slow_copyout2);
505 xx(c_mmot_getback_fast_copyin);
506 xx(c_mmot_cold_048);
507 xx(c_mmot_getback_FastSR);
508 xx(c_mmot_cold_050);
509 xx(c_mmot_cold_051);
510 xx(c_mmot_cold_052);
511 xx(c_mmot_cold_053);
512 xx(c_mmot_fastkernelreply);
513 xx(c_mmot_cold_055);
514 xx(c_mmot_getback_fast_put);
515
516#undef xx
517}
518
519#else /* !HOTPATH_DEBUG */
520
521/*
522 * Duplicate just these few so we can always do a quick sanity check
523 */
524unsigned int c_mmot_combined_S_R = 0; /* hotpath candidates */
525unsigned int c_mach_msg_trap_switch_fast = 0; /* hotpath successes */
526unsigned int c_mmot_kernel_send = 0; /* kernel server calls */
527#define HOT(expr) /* no optional counters */
528
529#endif /* !HOTPATH_DEBUG */
530
531boolean_t enable_hotpath = TRUE; /* Patchable, just in case ... */
532#endif /* HOTPATH_ENABLE */
533
534/*
535 * Routine: mach_msg_overwrite_trap [mach trap]
536 * Purpose:
537 * Possibly send a message; possibly receive a message.
538 * Conditions:
539 * Nothing locked.
540 * Returns:
541 * All of mach_msg_send and mach_msg_receive error codes.
542 */
543
544mach_msg_return_t
545mach_msg_overwrite_trap(
546 mach_msg_header_t *msg,
547 mach_msg_option_t option,
548 mach_msg_size_t send_size,
549 mach_msg_size_t rcv_size,
550 mach_port_name_t rcv_name,
551 mach_msg_timeout_t timeout,
552 mach_port_name_t notify,
553 mach_msg_header_t *rcv_msg,
554 mach_msg_size_t scatter_list_size)
555{
556 register mach_msg_header_t *hdr;
557 mach_msg_return_t mr = MACH_MSG_SUCCESS;
558 /* mask out some of the options before entering the hot path */
559 mach_msg_option_t masked_option =
560 option & ~(MACH_SEND_TRAILER|MACH_RCV_TRAILER_MASK|MACH_RCV_LARGE);
561 int i;
562
563#if ENABLE_HOTPATH
564 /* BEGINNING OF HOT PATH */
565 if ((masked_option == (MACH_SEND_MSG|MACH_RCV_MSG)) && enable_hotpath) {
566 register thread_t self = current_thread();
567 register mach_msg_format_0_trailer_t *trailer;
568
569 ipc_space_t space = current_act()->task->itk_space;
570 ipc_kmsg_t kmsg;
571 register ipc_port_t dest_port;
572 ipc_object_t rcv_object;
573 register ipc_mqueue_t rcv_mqueue;
574 mach_msg_size_t reply_size;
575 ipc_kmsg_t rcv_kmsg;
576
577 c_mmot_combined_S_R++;
578
579 /*
580 * This case is divided into ten sections, each
581 * with a label. There are five optimized
582 * sections and six unoptimized sections, which
583 * do the same thing but handle all possible
584 * cases and are slower.
585 *
586 * The five sections for an RPC are
587 * 1) Get request message into a buffer.
588 * 2) Copyin request message and rcv_name.
589 * (fast_copyin or slow_copyin)
590 * 3) Enqueue request and dequeue reply.
591 * (fast_send_receive or
592 * slow_send and slow_receive)
593 * 4) Copyout reply message.
594 * (fast_copyout or slow_copyout)
595 * 5) Put reply message to user's buffer.
596 *
597 * Keep the locking hierarchy firmly in mind.
598 * (First spaces, then ports, then port sets,
599 * then message queues.) Only a non-blocking
600 * attempt can be made to acquire locks out of
601 * order, or acquire two locks on the same level.
602 * Acquiring two locks on the same level will
603 * fail if the objects are really the same,
604 * unless simple locking is disabled. This is OK,
605 * because then the extra unlock does nothing.
606 *
607 * There are two major reasons these RPCs can't use
608 * ipc_thread_switch, and use slow_send/slow_receive:
609 * 1) Kernel RPCs.
610 * 2) Servers fall behind clients, so
611 * client doesn't find a blocked server thread and
612 * server finds waiting messages and can't block.
613 */
614
615 mr = ipc_kmsg_get(msg, send_size, &kmsg);
616 if (mr != KERN_SUCCESS) {
617 return mr;
618 }
619 hdr = &kmsg->ikm_header;
620 trailer = (mach_msg_format_0_trailer_t *) ((vm_offset_t) hdr +
621 send_size);
622
623 fast_copyin:
624 /*
625 * optimized ipc_kmsg_copyin/ipc_mqueue_copyin
626 *
627 * We have the request message data in kmsg.
628 * Must still do copyin, send, receive, etc.
629 *
630 * If the message isn't simple, we can't combine
631 * ipc_kmsg_copyin_header and ipc_mqueue_copyin,
632 * because copyin of the message body might
633 * affect rcv_name.
634 */
635
636 switch (hdr->msgh_bits) {
637 case MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
638 MACH_MSG_TYPE_MAKE_SEND_ONCE): {
639 register ipc_entry_t table;
640 register ipc_entry_num_t size;
641 register ipc_port_t reply_port;
642
643 /* sending a request message */
644
645 {
646 register mach_port_index_t index;
647 register mach_port_gen_t gen;
648
649 {
650 register mach_port_name_t reply_name =
651 (mach_port_name_t)hdr->msgh_local_port;
652
653 if (reply_name != rcv_name) {
654 HOT(c_mmot_g_slow_copyin3++);
655 goto slow_copyin;
656 }
657
658 /* optimized ipc_entry_lookup of reply_name */
659
660 index = MACH_PORT_INDEX(reply_name);
661 gen = MACH_PORT_GEN(reply_name);
662
663 is_read_lock(space);
664 assert(space->is_active);
665
666 size = space->is_table_size;
667 table = space->is_table;
668
669 {
670 register ipc_entry_t entry;
671 register ipc_entry_bits_t bits;
672
673 if (index < size) {
674 entry = &table[index];
675 bits = entry->ie_bits;
676 if (IE_BITS_GEN(bits) != gen ||
677 (bits & IE_BITS_COLLISION)) {
678 entry = IE_NULL;
679 }
680 } else {
681 entry = IE_NULL;
682 }
683 if (entry == IE_NULL) {
684 entry = ipc_entry_lookup(space, reply_name);
685 if (entry == IE_NULL) {
686 HOT(c_mmot_cold_006++);
687 goto abort_request_copyin;
688 }
689 bits = entry->ie_bits;
690 }
691
692 /* check type bit */
693
694 if (! (bits & MACH_PORT_TYPE_RECEIVE)) {
695 HOT(c_mmot_cold_007++);
696 goto abort_request_copyin;
697 }
698
699 reply_port = (ipc_port_t) entry->ie_object;
700 assert(reply_port != IP_NULL);
701 }
702 }
703 }
704
705 /* optimized ipc_entry_lookup of dest_name */
706
707 {
708 register mach_port_index_t index;
709 register mach_port_gen_t gen;
710
711 {
712 register mach_port_name_t dest_name =
713 (mach_port_name_t)hdr->msgh_remote_port;
714
715 index = MACH_PORT_INDEX(dest_name);
716 gen = MACH_PORT_GEN(dest_name);
717
718 {
719 register ipc_entry_t entry;
720 register ipc_entry_bits_t bits;
721
722 if (index < size) {
723 entry = &table[index];
724 bits = entry->ie_bits;
725 if (IE_BITS_GEN(bits) != gen ||
726 (bits & IE_BITS_COLLISION)) {
727 entry = IE_NULL;
728 }
729 } else {
730 entry = IE_NULL;
731 }
732 if (entry == IE_NULL) {
733 entry = ipc_entry_lookup(space, dest_name);
734 if (entry == IE_NULL) {
735 HOT(c_mmot_cold_008++);
736 goto abort_request_copyin;
737 }
738 bits = entry->ie_bits;
739 }
740
741 /* check type bit */
742
743 if (! (bits & MACH_PORT_TYPE_SEND)) {
744 HOT(c_mmot_cold_009++);
745 goto abort_request_copyin;
746 }
747
748 assert(IE_BITS_UREFS(bits) > 0);
749
750 dest_port = (ipc_port_t) entry->ie_object;
751 assert(dest_port != IP_NULL);
752 }
753 }
754 }
755
756 /*
757 * To do an atomic copyin, need simultaneous
758 * locks on both ports and the space. If
759 * dest_port == reply_port, and simple locking is
760 * enabled, then we will abort. Otherwise it's
761 * OK to unlock twice.
762 */
763
764 ip_lock(dest_port);
765 if (!ip_active(dest_port) ||
766 !ip_lock_try(reply_port)) {
767 ip_unlock(dest_port);
768 HOT(c_mmot_cold_010++);
769 goto abort_request_copyin;
770 }
771 is_read_unlock(space);
772
773 assert(dest_port->ip_srights > 0);
774 dest_port->ip_srights++;
775 ip_reference(dest_port);
776
777 assert(ip_active(reply_port));
778 assert(reply_port->ip_receiver_name ==
779 (mach_port_name_t)hdr->msgh_local_port);
780 assert(reply_port->ip_receiver == space);
781
782 reply_port->ip_sorights++;
783 ip_reference(reply_port);
784
785 hdr->msgh_bits =
786 MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
787 MACH_MSG_TYPE_PORT_SEND_ONCE);
788 hdr->msgh_remote_port = dest_port;
789 hdr->msgh_local_port = reply_port;
790
791 /* make sure we can queue to the destination */
792
793 if (dest_port->ip_receiver == ipc_space_kernel) {
794 /*
795 * The kernel server has a reference to
796 * the reply port, which it hands back
797 * to us in the reply message. We do
798 * not need to keep another reference to
799 * it.
800 */
801 ip_unlock(reply_port);
802
803 assert(ip_active(dest_port));
804 dest_port->ip_messages.imq_seqno++;
805 ip_unlock(dest_port);
806 goto kernel_send;
807 }
808
809 if (imq_full(&dest_port->ip_messages)) {
810 HOT(c_mmot_cold_013++);
811 goto abort_request_send_receive;
812 }
813
814 /* optimized ipc_mqueue_copyin */
815
816 rcv_object = (ipc_object_t) reply_port;
817 io_reference(rcv_object);
818 rcv_mqueue = &reply_port->ip_messages;
819 io_unlock(rcv_object);
820 HOT(c_mmot_hot_fSR_ok++);
821 goto fast_send_receive;
822
823 abort_request_copyin:
824 is_read_unlock(space);
825 goto slow_copyin;
826
827 abort_request_send_receive:
828 ip_unlock(dest_port);
829 ip_unlock(reply_port);
830 goto slow_send;
831 }
832
833 case MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0): {
834 register ipc_entry_num_t size;
835 register ipc_entry_t table;
836
837 /* sending a reply message */
838
839 {
840 register mach_port_name_t reply_name =
841 (mach_port_name_t)hdr->msgh_local_port;
842
843 if (reply_name != MACH_PORT_NULL) {
844 HOT(c_mmot_cold_018++);
845 goto slow_copyin;
846 }
847 }
848
849 is_write_lock(space);
850 assert(space->is_active);
851
852 /* optimized ipc_entry_lookup */
853
854 size = space->is_table_size;
855 table = space->is_table;
856
857 {
858 register ipc_entry_t entry;
859 register mach_port_gen_t gen;
860 register mach_port_index_t index;
861 ipc_table_index_t *requests;
862
863 {
864 register mach_port_name_t dest_name =
865 (mach_port_name_t)hdr->msgh_remote_port;
866
867 index = MACH_PORT_INDEX(dest_name);
868 gen = MACH_PORT_GEN(dest_name);
869 }
870
871 if (index >= size) {
872 HOT(c_mmot_cold_019++);
873 goto abort_reply_dest_copyin;
874 }
875
876 entry = &table[index];
877
878 /* check generation, collision bit, and type bit */
879
880 if ((entry->ie_bits & (IE_BITS_GEN_MASK|
881 IE_BITS_COLLISION|
882 MACH_PORT_TYPE_SEND_ONCE)) !=
883 (gen | MACH_PORT_TYPE_SEND_ONCE)) {
884 HOT(c_mmot_cold_020++);
885 goto abort_reply_dest_copyin;
886 }
887
888 /* optimized ipc_right_copyin */
889
890 assert(IE_BITS_TYPE(entry->ie_bits) ==
891 MACH_PORT_TYPE_SEND_ONCE);
892 assert(IE_BITS_UREFS(entry->ie_bits) == 1);
893
894 if (entry->ie_request != 0) {
895 HOT(c_mmot_cold_021++);
896 goto abort_reply_dest_copyin;
897 }
898
899 dest_port = (ipc_port_t) entry->ie_object;
900 assert(dest_port != IP_NULL);
901
902 ip_lock(dest_port);
903 if (!ip_active(dest_port)) {
904 ip_unlock(dest_port);
905 HOT(c_mmot_cold_022++);
906 goto abort_reply_dest_copyin;
907 }
908
909 assert(dest_port->ip_sorights > 0);
910
911 /* optimized ipc_entry_dealloc */
912
913
914 entry->ie_bits = gen;
915 entry->ie_next = table->ie_next;
916 table->ie_next = index;
917 entry->ie_object = IO_NULL;
918 }
919
920 hdr->msgh_bits =
921 MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE,
922 0);
923 hdr->msgh_remote_port = dest_port;
924
925 /* make sure we can queue to the destination */
926
927 assert(dest_port->ip_receiver != ipc_space_kernel);
928
929 /* optimized ipc_entry_lookup/ipc_mqueue_copyin */
930
931 {
932 register ipc_entry_t entry;
933 register ipc_entry_bits_t bits;
934
935 {
936 register mach_port_index_t index;
937 register mach_port_gen_t gen;
938
939 index = MACH_PORT_INDEX(rcv_name);
940 gen = MACH_PORT_GEN(rcv_name);
941
942 if (index < size) {
943 entry = &table[index];
944 bits = entry->ie_bits;
945 if (IE_BITS_GEN(bits) != gen ||
946 (bits & IE_BITS_COLLISION)) {
947 entry = IE_NULL;
948 }
949 } else {
950 entry = IE_NULL;
951 }
952 if (entry == IE_NULL) {
953 entry = ipc_entry_lookup(space, rcv_name);
954 if (entry == IE_NULL) {
955 HOT(c_mmot_cold_024++);
956 goto abort_reply_rcv_copyin;
957 }
958 bits = entry->ie_bits;
959 }
960
961 }
962
963 /* check type bits; looking for receive or set */
964#if 0
965 /*
966 * JMM - The check below for messages in the receive
967 * mqueue is insufficient to work with port sets, since
968 * the messages stay in the port queues. For now, don't
969 * allow portsets (but receiving on portsets when sending
970 * a message to a send-once right is actually a very
971 * common case (so we should re-enable).
972 */
973 if (bits & MACH_PORT_TYPE_PORT_SET) {
974 register ipc_pset_t rcv_pset;
975
976 rcv_pset = (ipc_pset_t) entry->ie_object;
977 assert(rcv_pset != IPS_NULL);
978
979 ips_lock(rcv_pset);
980 assert(ips_active(rcv_pset));
981
982 rcv_object = (ipc_object_t) rcv_pset;
983 rcv_mqueue = &rcv_pset->ips_messages;
984 } else
985#endif /* 0 */
986 if (bits & MACH_PORT_TYPE_RECEIVE) {
987 register ipc_port_t rcv_port;
988
989 rcv_port = (ipc_port_t) entry->ie_object;
990 assert(rcv_port != IP_NULL);
991
992 if (!ip_lock_try(rcv_port)) {
993 HOT(c_mmot_cold_025++);
994 goto abort_reply_rcv_copyin;
995 }
996 assert(ip_active(rcv_port));
997
998 if (rcv_port->ip_pset_count != 0) {
999 ip_unlock(rcv_port);
1000 HOT(c_mmot_cold_026++);
1001 goto abort_reply_rcv_copyin;
1002 }
1003
1004 rcv_object = (ipc_object_t) rcv_port;
1005 rcv_mqueue = &rcv_port->ip_messages;
1006 } else {
1007 HOT(c_mmot_cold_027++);
1008 goto abort_reply_rcv_copyin;
1009 }
1010 }
1011
1012 is_write_unlock(space);
1013 io_reference(rcv_object);
1014 io_unlock(rcv_object);
1015 HOT(c_mmot_hot_fSR_ok++);
1016 goto fast_send_receive;
1017
1018 abort_reply_dest_copyin:
1019 is_write_unlock(space);
1020 HOT(c_mmot_cold_029++);
1021 goto slow_copyin;
1022
1023 abort_reply_rcv_copyin:
1024 ip_unlock(dest_port);
1025 is_write_unlock(space);
1026 HOT(c_mmot_cold_030++);
1027 goto slow_send;
1028 }
1029
1030 default:
1031 HOT(c_mmot_cold_031++);
1032 goto slow_copyin;
1033 }
1034 /*NOTREACHED*/
1035
1036 fast_send_receive:
1037 /*
1038 * optimized ipc_mqueue_send/ipc_mqueue_receive
1039 *
1040 * Finished get/copyin of kmsg and copyin of rcv_name.
1041 * space is unlocked, dest_port is locked,
1042 * we can queue kmsg to dest_port,
1043 * rcv_mqueue is set, and rcv_object holds a ref
1044 * so the mqueue cannot go away.
1045 *
1046 * JMM - For now, rcv_object is just a port. Portsets
1047 * are disabled for the time being.
1048 */
1049
1050 assert(ip_active(dest_port));
1051 assert(dest_port->ip_receiver != ipc_space_kernel);
55e303ae
A
1052// assert(!imq_full(&dest_port->ip_messages) ||
1053// (MACH_MSGH_BITS_REMOTE(hdr->msgh_bits) ==
1054// MACH_MSG_TYPE_PORT_SEND_ONCE));
1c79356b
A
1055 assert((hdr->msgh_bits & MACH_MSGH_BITS_CIRCULAR) == 0);
1056
1057 {
1058 register ipc_mqueue_t dest_mqueue;
1059 wait_queue_t waitq;
1060 thread_t receiver;
55e303ae 1061 processor_t processor;
1c79356b
A
1062 spl_t s;
1063
1064 s = splsched();
55e303ae
A
1065 processor = current_processor();
1066 if (processor->current_pri >= BASEPRI_RTQUEUES)
1067 goto abort_send_receive1;
1068
1c79356b
A
1069 dest_mqueue = &dest_port->ip_messages;
1070 waitq = &dest_mqueue->imq_wait_queue;
1071 imq_lock(dest_mqueue);
1072
9bccf70c 1073 wait_queue_peek64_locked(waitq, IPC_MQUEUE_RECEIVE, &receiver, &waitq);
1c79356b
A
1074 /* queue still locked, thread locked - but still on q */
1075
55e303ae 1076 if ( receiver == THREAD_NULL ) {
1c79356b
A
1077 abort_send_receive:
1078 imq_unlock(dest_mqueue);
55e303ae 1079 abort_send_receive1:
1c79356b
A
1080 splx(s);
1081 ip_unlock(dest_port);
1082 ipc_object_release(rcv_object);
1083 HOT(c_mmot_cold_032++);
1084 goto slow_send;
1085 }
1086
1087 assert(receiver->wait_queue == waitq);
1088 assert(receiver->wait_event == IPC_MQUEUE_RECEIVE);
1089
1090 /*
55e303ae
A
1091 * Make sure that the scheduling state of the receiver is such
1092 * that we can handoff to it here. If not, fall off.
1c79356b 1093 *
55e303ae
A
1094 * JMM - We have an opportunity here. If the thread is locked
1095 * and we find it runnable, it may still be trying to get into
1c79356b
A
1096 * thread_block on itself. We could just "hand him the message"
1097 * and let him go (thread_go_locked()) and then fall down into a
1098 * slow receive for ourselves. Only his RECEIVE_TOO_LARGE handling
1099 * runs afoul of that. Clean this up!
1100 */
55e303ae
A
1101 if ((receiver->state & (TH_RUN|TH_WAIT)) != TH_WAIT ||
1102 receiver->sched_pri >= BASEPRI_RTQUEUES ||
1103 receiver->processor_set != processor->processor_set ||
1104 (receiver->bound_processor != PROCESSOR_NULL &&
1105 receiver->bound_processor != processor)) {
1c79356b
A
1106 HOT(c_mmot_cold_033++);
1107 fall_off:
1108 thread_unlock(receiver);
1109 if (waitq != &dest_mqueue->imq_wait_queue)
1110 wait_queue_unlock(waitq);
1111 goto abort_send_receive;
1112 }
1113
1114 /*
1115 * Check that the receiver can stay on the hot path.
1116 */
1117 if (send_size + REQUESTED_TRAILER_SIZE(receiver->ith_option) >
1118 receiver->ith_msize) {
1119 /*
1120 * The receiver can't accept the message.
1121 */
1122 HOT(c_mmot_bad_rcvr++);
1123 goto fall_off;
1124 }
1125
1c79356b
A
1126 /*
1127 * Before committing to the handoff, make sure that we are
1128 * really going to block (i.e. there are no messages already
1129 * queued for us. This violates lock ordering, so make sure
1130 * we don't deadlock. After the trylock succeeds below, we
1131 * may have up to 3 message queues locked:
1132 * - the dest port mqueue
1133 * - a portset mqueue (where waiting receiver was found)
1134 * - finally our own rcv_mqueue
1135 *
1136 * JMM - Need to make this check appropriate for portsets as
1137 * well before re-enabling them.
1138 */
1139 if (!imq_lock_try(rcv_mqueue)) {
1140 goto fall_off;
1141 }
1142 if (ipc_kmsg_queue_first(&rcv_mqueue->imq_messages) != IKM_NULL) {
1143 imq_unlock(rcv_mqueue);
1144 HOT(c_mmot_cold_033++);
1145 goto fall_off;
1146 }
1147
1148 /* At this point we are committed to do the "handoff". */
1149 c_mach_msg_trap_switch_fast++;
1150
1151 /*
1152 * JMM - Go ahead and pull the receiver from the runq. If the
1153 * runq wasn't the one for the mqueue, unlock it.
1154 */
1155 wait_queue_pull_thread_locked(waitq,
1156 receiver,
1157 (waitq != &dest_mqueue->imq_wait_queue));
1158
1159 /*
1160 * Store the kmsg and seqno where the receiver can pick it up.
1161 */
1162 receiver->ith_state = MACH_MSG_SUCCESS;
1163 receiver->ith_kmsg = kmsg;
1164 receiver->ith_seqno = dest_mqueue->imq_seqno++;
1165
1166 /*
55e303ae 1167 * Update the scheduling state for the handoff.
1c79356b 1168 */
55e303ae
A
1169 receiver->state &= ~(TH_WAIT|TH_UNINT);
1170 receiver->state |= TH_RUN;
1171
1172 pset_run_incr(receiver->processor_set);
1173 if (receiver->sched_mode & TH_MODE_TIMESHARE)
1174 pset_share_incr(receiver->processor_set);
1175
1176 receiver->wait_result = THREAD_AWAKENED;
1177
1178 receiver->computation_metered = 0;
1179 receiver->reason = AST_NONE;
1180
1c79356b 1181 thread_unlock(receiver);
1c79356b
A
1182
1183 imq_unlock(dest_mqueue);
1184 ip_unlock(dest_port);
1185 current_task()->messages_sent++;
1186
1187
1188 /*
1189 * Put self on receive port's queue.
1190 * Also save state that the sender of
1191 * our reply message needs to determine if it
1192 * can hand off directly back to us.
1193 */
55e303ae 1194 thread_lock(self);
1c79356b
A
1195 self->ith_msg = (rcv_msg) ? rcv_msg : msg;
1196 self->ith_object = rcv_object; /* still holds reference */
1197 self->ith_msize = rcv_size;
1198 self->ith_option = option;
1199 self->ith_scatter_list_size = scatter_list_size;
1200 self->ith_continuation = thread_syscall_return;
1201
1202 waitq = &rcv_mqueue->imq_wait_queue;
9bccf70c 1203 (void)wait_queue_assert_wait64_locked(waitq,
1c79356b
A
1204 IPC_MQUEUE_RECEIVE,
1205 THREAD_ABORTSAFE,
55e303ae
A
1206 self);
1207 thread_unlock(self);
1208 imq_unlock(rcv_mqueue);
1c79356b 1209
9bccf70c
A
1210 /*
1211 * Switch directly to receiving thread, and block
1212 * this thread as though it had called ipc_mqueue_receive.
1c79356b 1213 */
9bccf70c 1214 thread_run(self, ipc_mqueue_receive_continue, receiver);
1c79356b
A
1215 /* NOTREACHED */
1216 }
1217
1218 fast_copyout:
1219 /*
1220 * Nothing locked and no references held, except
1221 * we have kmsg with msgh_seqno filled in. Must
1222 * still check against rcv_size and do
1223 * ipc_kmsg_copyout/ipc_kmsg_put.
1224 */
1225
1226 reply_size = send_size + trailer->msgh_trailer_size;
1227 if (rcv_size < reply_size) {
1228 HOT(c_mmot_g_slow_copyout6++);
1229 goto slow_copyout;
1230 }
1231
1232 /* optimized ipc_kmsg_copyout/ipc_kmsg_copyout_header */
1233
1234 switch (hdr->msgh_bits) {
1235 case MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
1236 MACH_MSG_TYPE_PORT_SEND_ONCE): {
1237 ipc_port_t reply_port =
1238 (ipc_port_t) hdr->msgh_local_port;
1239 mach_port_name_t dest_name, reply_name;
1240
1241 /* receiving a request message */
1242
1243 if (!IP_VALID(reply_port)) {
1244 HOT(c_mmot_g_slow_copyout5++);
1245 goto slow_copyout;
1246 }
1247
1248 is_write_lock(space);
1249 assert(space->is_active);
1250
1251 /*
1252 * To do an atomic copyout, need simultaneous
1253 * locks on both ports and the space. If
1254 * dest_port == reply_port, and simple locking is
1255 * enabled, then we will abort. Otherwise it's
1256 * OK to unlock twice.
1257 */
1258
1259 ip_lock(dest_port);
1260 if (!ip_active(dest_port) ||
1261 !ip_lock_try(reply_port)) {
1262 HOT(c_mmot_cold_037++);
1263 goto abort_request_copyout;
1264 }
1265
1266 if (!ip_active(reply_port)) {
1267 ip_unlock(reply_port);
1268 HOT(c_mmot_cold_038++);
1269 goto abort_request_copyout;
1270 }
1271
1272 assert(reply_port->ip_sorights > 0);
1273 ip_unlock(reply_port);
1274
1275 {
1276 register ipc_entry_t table;
1277 register ipc_entry_t entry;
1278 register mach_port_index_t index;
1279
1280 /* optimized ipc_entry_get */
1281
1282 table = space->is_table;
1283 index = table->ie_next;
1284
1285 if (index == 0) {
1286 HOT(c_mmot_cold_039++);
1287 goto abort_request_copyout;
1288 }
1289
1290 entry = &table[index];
1291 table->ie_next = entry->ie_next;
1292 entry->ie_request = 0;
1293
1294 {
1295 register mach_port_gen_t gen;
1296
1297 assert((entry->ie_bits &~ IE_BITS_GEN_MASK) == 0);
1298 gen = IE_BITS_NEW_GEN(entry->ie_bits);
1299
1300 reply_name = MACH_PORT_MAKE(index, gen);
1301
1302 /* optimized ipc_right_copyout */
1303
1304 entry->ie_bits = gen | (MACH_PORT_TYPE_SEND_ONCE | 1);
1305 }
1306
1307 assert(MACH_PORT_VALID(reply_name));
1308 entry->ie_object = (ipc_object_t) reply_port;
1309 is_write_unlock(space);
1310 }
1311
1312 /* optimized ipc_object_copyout_dest */
1313
1314 assert(dest_port->ip_srights > 0);
1315 ip_release(dest_port);
1316
1317 if (dest_port->ip_receiver == space)
1318 dest_name = dest_port->ip_receiver_name;
1319 else
1320 dest_name = MACH_PORT_NULL;
1321
1322 if ((--dest_port->ip_srights == 0) &&
1323 (dest_port->ip_nsrequest != IP_NULL)) {
1324 ipc_port_t nsrequest;
1325 mach_port_mscount_t mscount;
1326
1327 /* a rather rare case */
1328
1329 nsrequest = dest_port->ip_nsrequest;
1330 mscount = dest_port->ip_mscount;
1331 dest_port->ip_nsrequest = IP_NULL;
1332 ip_unlock(dest_port);
1333 ipc_notify_no_senders(nsrequest, mscount);
1334 } else
1335 ip_unlock(dest_port);
1336
1337 hdr->msgh_bits =
1338 MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE,
1339 MACH_MSG_TYPE_PORT_SEND);
1340 hdr->msgh_remote_port = (mach_port_t)reply_name;
1341 hdr->msgh_local_port = (mach_port_t)dest_name;
1342 HOT(c_mmot_hot_ok1++);
1343 goto fast_put;
1344
1345 abort_request_copyout:
1346 ip_unlock(dest_port);
1347 is_write_unlock(space);
1348 HOT(c_mmot_g_slow_copyout4++);
1349 goto slow_copyout;
1350 }
1351
1352 case MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0): {
1353 register mach_port_name_t dest_name;
1354
1355 /* receiving a reply message */
1356
1357 ip_lock(dest_port);
1358 if (!ip_active(dest_port)) {
1359 ip_unlock(dest_port);
1360 HOT(c_mmot_g_slow_copyout3++);
1361 goto slow_copyout;
1362 }
1363
1364 /* optimized ipc_object_copyout_dest */
1365
1366 assert(dest_port->ip_sorights > 0);
1367
1368 if (dest_port->ip_receiver == space) {
1369 ip_release(dest_port);
1370 dest_port->ip_sorights--;
1371 dest_name = dest_port->ip_receiver_name;
1372 ip_unlock(dest_port);
1373 } else {
1374 ip_unlock(dest_port);
1375
1376 ipc_notify_send_once(dest_port);
1377 dest_name = MACH_PORT_NULL;
1378 }
1379
1380 hdr->msgh_bits = MACH_MSGH_BITS(0,
1381 MACH_MSG_TYPE_PORT_SEND_ONCE);
1382 hdr->msgh_remote_port = MACH_PORT_NULL;
1383 hdr->msgh_local_port = (ipc_port_t)dest_name;
1384 HOT(c_mmot_hot_ok2++);
1385 goto fast_put;
1386 }
1387
1388 case MACH_MSGH_BITS_COMPLEX|
1389 MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0): {
1390 register mach_port_name_t dest_name;
1391
1392 /* receiving a complex reply message */
1393
1394 ip_lock(dest_port);
1395 if (!ip_active(dest_port)) {
1396 ip_unlock(dest_port);
1397 HOT(c_mmot_g_slow_copyout1++);
1398 goto slow_copyout;
1399 }
1400
1401 /* optimized ipc_object_copyout_dest */
1402
1403 assert(dest_port->ip_sorights > 0);
1404
1405 if (dest_port->ip_receiver == space) {
1406 ip_release(dest_port);
1407 dest_port->ip_sorights--;
1408 dest_name = dest_port->ip_receiver_name;
1409 ip_unlock(dest_port);
1410 } else {
1411 ip_unlock(dest_port);
1412
1413 ipc_notify_send_once(dest_port);
1414 dest_name = MACH_PORT_NULL;
1415 }
1416
1417 hdr->msgh_bits =
1418 MACH_MSGH_BITS_COMPLEX |
1419 MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND_ONCE);
1420 hdr->msgh_remote_port = MACH_PORT_NULL;
1421 hdr->msgh_local_port = (mach_port_t)dest_name;
1422
1423 mr = ipc_kmsg_copyout_body(kmsg, space,
1424 current_map(),
1425 MACH_MSG_BODY_NULL);
1426 if (mr != MACH_MSG_SUCCESS) {
1427 if (ipc_kmsg_put(msg, kmsg, hdr->msgh_size +
1428 trailer->msgh_trailer_size) ==
1429 MACH_RCV_INVALID_DATA)
1430 return MACH_RCV_INVALID_DATA;
1431 else
1432 return mr | MACH_RCV_BODY_ERROR;
1433 }
1434 HOT(c_mmot_hot_ok3++);
1435 goto fast_put;
1436 }
1437
1438 default:
1439 HOT(c_mmot_g_slow_copyout2++);
1440 goto slow_copyout;
1441 }
1442 /*NOTREACHED*/
1443
1444 fast_put:
1445 mr = ipc_kmsg_put(rcv_msg ? rcv_msg : msg,
1446 kmsg,
1447 hdr->msgh_size + trailer->msgh_trailer_size);
1448 if (mr != MACH_MSG_SUCCESS) {
1449 return MACH_RCV_INVALID_DATA;
1450 }
1451 current_task()->messages_received++;
1452 return mr;
1453
1454
1455 /* BEGINNING OF WARM PATH */
1456
1457 /*
1458 * The slow path has a few non-register temporary
1459 * variables used only for call-by-reference.
1460 */
1461
1462 slow_copyin:
1463 {
1464 ipc_kmsg_t temp_kmsg;
1465 mach_port_seqno_t temp_seqno;
1466 ipc_object_t temp_rcv_object;
1467 ipc_mqueue_t temp_rcv_mqueue;
1468 register mach_port_name_t reply_name =
1469 (mach_port_name_t)hdr->msgh_local_port;
1470
1471
1472 /*
1473 * We have the message data in kmsg, but
1474 * we still need to copyin, send it,
1475 * receive a reply, and do copyout.
1476 */
1477
1478 mr = ipc_kmsg_copyin(kmsg, space, current_map(),
1479 MACH_PORT_NULL);
1480 if (mr != MACH_MSG_SUCCESS) {
1481 ipc_kmsg_free(kmsg);
1482 return(mr);
1483 }
1484
1485 /* try to get back on optimized path */
1486
1487 if ((reply_name != rcv_name) ||
1488 (hdr->msgh_bits & MACH_MSGH_BITS_CIRCULAR)) {
1489 HOT(c_mmot_cold_048++);
1490 goto slow_send;
1491 }
1492
1493 dest_port = (ipc_port_t) hdr->msgh_remote_port;
1494 assert(IP_VALID(dest_port));
1495
1496 ip_lock(dest_port);
1497 if (!ip_active(dest_port)) {
1498 ip_unlock(dest_port);
1499 goto slow_send;
1500 }
1501
1502 if (dest_port->ip_receiver == ipc_space_kernel) {
1503 dest_port->ip_messages.imq_seqno++;
1504 ip_unlock(dest_port);
1505 goto kernel_send;
1506 }
1507
1508 if (!imq_full(&dest_port->ip_messages) ||
1509 (MACH_MSGH_BITS_REMOTE(hdr->msgh_bits) ==
1510 MACH_MSG_TYPE_PORT_SEND_ONCE))
1511 {
1512 /*
1513 * Try an optimized ipc_mqueue_copyin.
1514 * It will work if this is a request message.
1515 */
1516
1517 register ipc_port_t reply_port;
1518
1519 reply_port = (ipc_port_t) hdr->msgh_local_port;
1520 if (IP_VALID(reply_port)) {
1521 if (ip_lock_try(reply_port)) {
1522 if (ip_active(reply_port) &&
1523 reply_port->ip_receiver == space &&
1524 reply_port->ip_receiver_name == rcv_name &&
1525 reply_port->ip_pset_count == 0)
1526 {
1527 /* Grab a reference to the reply port. */
1528 rcv_object = (ipc_object_t) reply_port;
1529 io_reference(rcv_object);
1530 rcv_mqueue = &reply_port->ip_messages;
1531 io_unlock(rcv_object);
1532 HOT(c_mmot_getback_FastSR++);
1533 goto fast_send_receive;
1534 }
1535 ip_unlock(reply_port);
1536 }
1537 }
1538 }
1539
1540 ip_unlock(dest_port);
1541 HOT(c_mmot_cold_050++);
1542 goto slow_send;
1543
1544 kernel_send:
1545 /*
1546 * Special case: send message to kernel services.
1547 * The request message has been copied into the
1548 * kmsg. Nothing is locked.
1549 */
1550
1551 {
1552 register ipc_port_t reply_port;
1553 mach_port_seqno_t local_seqno;
1554 spl_t s;
1555
1556 /*
1557 * Perform the kernel function.
1558 */
1559 c_mmot_kernel_send++;
1560
1561 current_task()->messages_sent++;
1562
1563 kmsg = ipc_kobject_server(kmsg);
1564 if (kmsg == IKM_NULL) {
1565 /*
1566 * No reply. Take the
1567 * slow receive path.
1568 */
1569 HOT(c_mmot_cold_051++);
1570 goto slow_get_rcv_port;
1571 }
1572
1573 /*
1574 * Check that:
1575 * the reply port is alive
1576 * we hold the receive right
1577 * the name has not changed.
1578 * the port is not in a set
1579 * If any of these are not true,
1580 * we cannot directly receive the reply
1581 * message.
1582 */
1583 hdr = &kmsg->ikm_header;
1584 send_size = hdr->msgh_size;
1585 trailer = (mach_msg_format_0_trailer_t *) ((vm_offset_t) hdr +
1586 round_msg(send_size));
1587 reply_port = (ipc_port_t) hdr->msgh_remote_port;
1588 ip_lock(reply_port);
1589
1590 if ((!ip_active(reply_port)) ||
1591 (reply_port->ip_receiver != space) ||
1592 (reply_port->ip_receiver_name != rcv_name) ||
1593 (reply_port->ip_pset_count != 0))
1594 {
1595 ip_unlock(reply_port);
1596 ipc_kmsg_send_always(kmsg);
1597 HOT(c_mmot_cold_052++);
1598 goto slow_get_rcv_port;
1599 }
1600
1601 s = splsched();
1602 rcv_mqueue = &reply_port->ip_messages;
1603 imq_lock(rcv_mqueue);
1604
1605 /* keep port locked, and don`t change ref count yet */
1606
1607 /*
1608 * If there are messages on the port
1609 * or other threads waiting for a message,
1610 * we cannot directly receive the reply.
1611 */
1612 if (!wait_queue_empty(&rcv_mqueue->imq_wait_queue) ||
1613 (ipc_kmsg_queue_first(&rcv_mqueue->imq_messages) != IKM_NULL))
1614 {
1615 imq_unlock(rcv_mqueue);
1616 splx(s);
1617 ip_unlock(reply_port);
1618 ipc_kmsg_send_always(kmsg);
1619 HOT(c_mmot_cold_053++);
1620 goto slow_get_rcv_port;
1621 }
1622
1623 /*
1624 * We can directly receive this reply.
1625 * Since there were no messages queued
1626 * on the reply port, there should be
1627 * no threads blocked waiting to send.
1628 */
1629 dest_port = reply_port;
1630 local_seqno = rcv_mqueue->imq_seqno++;
1631 imq_unlock(rcv_mqueue);
1632 splx(s);
1633
1634 /*
1635 * inline ipc_object_release.
1636 * Port is still locked.
1637 * Reference count was not incremented.
1638 */
1639 ip_check_unlock(reply_port);
1640
1641 if (option & MACH_RCV_TRAILER_MASK) {
1642 trailer->msgh_seqno = local_seqno;
1643 trailer->msgh_trailer_size = REQUESTED_TRAILER_SIZE(option);
1644 }
1645 /* copy out the kernel reply */
1646 HOT(c_mmot_fastkernelreply++);
1647 goto fast_copyout;
1648 }
1649
1650 slow_send:
1651 /*
1652 * Nothing is locked. We have acquired kmsg, but
1653 * we still need to send it and receive a reply.
1654 */
1655
1656 mr = ipc_kmsg_send(kmsg, MACH_MSG_OPTION_NONE,
1657 MACH_MSG_TIMEOUT_NONE);
1658 if (mr != MACH_MSG_SUCCESS) {
1659 mr |= ipc_kmsg_copyout_pseudo(kmsg, space,
1660 current_map(),
1661 MACH_MSG_BODY_NULL);
1662
1663 (void) ipc_kmsg_put(msg, kmsg, hdr->msgh_size);
1664 return(mr);
1665 }
1666
1667 slow_get_rcv_port:
1668 /*
1669 * We have sent the message. Copy in the receive port.
1670 */
1671 mr = ipc_mqueue_copyin(space, rcv_name,
1672 &temp_rcv_mqueue, &temp_rcv_object);
1673 if (mr != MACH_MSG_SUCCESS) {
1674 return(mr);
1675 }
1676 rcv_mqueue = temp_rcv_mqueue;
1677 rcv_object = temp_rcv_object;
1678 /* hold ref for rcv_object */
1679
1680 slow_receive:
1681 /*
1682 * Now we have sent the request and copied in rcv_name,
1683 * and hold ref for rcv_object (to keep mqueue alive).
1684 * Just receive a reply and try to get back to fast path.
1685 */
1686
1687 self->ith_continuation = (void (*)(mach_msg_return_t))0;
1688 ipc_mqueue_receive(rcv_mqueue,
1689 MACH_MSG_OPTION_NONE,
1690 MACH_MSG_SIZE_MAX,
1691 MACH_MSG_TIMEOUT_NONE,
1692 THREAD_ABORTSAFE);
1693
1694 mr = self->ith_state;
1695 temp_kmsg = self->ith_kmsg;
1696 temp_seqno = self->ith_seqno;
1697
1698 ipc_object_release(rcv_object);
1699
1700 if (mr != MACH_MSG_SUCCESS) {
1701 return(mr);
1702 }
1703
1704 kmsg = temp_kmsg;
1705 hdr = &kmsg->ikm_header;
1706 send_size = hdr->msgh_size;
1707 trailer = (mach_msg_format_0_trailer_t *) ((vm_offset_t) hdr +
1708 round_msg(send_size));
1709 if (option & MACH_RCV_TRAILER_MASK) {
1710 trailer->msgh_seqno = temp_seqno;
1711 trailer->msgh_trailer_size = REQUESTED_TRAILER_SIZE(option);
1712 }
1713 dest_port = (ipc_port_t) hdr->msgh_remote_port;
1714 HOT(c_mmot_cold_055++);
1715 goto fast_copyout;
1716
1717 slow_copyout:
1718 /*
1719 * Nothing locked and no references held, except
1720 * we have kmsg with msgh_seqno filled in. Must
1721 * still check against rcv_size and do
1722 * ipc_kmsg_copyout/ipc_kmsg_put.
1723 */
1724
1725 reply_size = send_size + trailer->msgh_trailer_size;
1726 if (rcv_size < reply_size) {
1727 if (msg_receive_error(kmsg, msg, option, temp_seqno,
1728 space) == MACH_RCV_INVALID_DATA) {
1729 mr = MACH_RCV_INVALID_DATA;
1730 return(mr);
1731 }
1732 else {
1733 mr = MACH_RCV_TOO_LARGE;
1734 return(mr);
1735 }
1736 }
1737
1738 mr = ipc_kmsg_copyout(kmsg, space, current_map(),
1739 MACH_PORT_NULL, MACH_MSG_BODY_NULL);
1740 if (mr != MACH_MSG_SUCCESS) {
1741 if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
1742 if (ipc_kmsg_put(msg, kmsg, reply_size) ==
1743 MACH_RCV_INVALID_DATA)
1744 mr = MACH_RCV_INVALID_DATA;
1745 }
1746 else {
1747 if (msg_receive_error(kmsg, msg, option,
1748 temp_seqno, space) == MACH_RCV_INVALID_DATA)
1749 mr = MACH_RCV_INVALID_DATA;
1750 }
1751
1752 return(mr);
1753 }
1754
1755 /* try to get back on optimized path */
1756 HOT(c_mmot_getback_fast_put++);
1757 goto fast_put;
1758
1759 /*NOTREACHED*/
1760 }
1761 } /* END OF HOT PATH */
1762#endif /* ENABLE_HOTPATH */
1763
1764 if (option & MACH_SEND_MSG) {
1765 mr = mach_msg_send(msg, option, send_size,
1766 timeout, notify);
1767 if (mr != MACH_MSG_SUCCESS) {
1768 return mr;
1769 }
1770 }
1771
1772 if (option & MACH_RCV_MSG) {
1773 mach_msg_header_t *rcv;
1774
1775 /*
1776 * 1. MACH_RCV_OVERWRITE is on, and rcv_msg is our scatter list
1777 * and receive buffer
1778 * 2. MACH_RCV_OVERWRITE is off, and rcv_msg might be the
1779 * alternate receive buffer (separate send and receive buffers).
1780 */
1781 if (option & MACH_RCV_OVERWRITE)
1782 rcv = rcv_msg;
1783 else if (rcv_msg != MACH_MSG_NULL)
1784 rcv = rcv_msg;
1785 else
1786 rcv = msg;
1787 mr = mach_msg_receive(rcv, option, rcv_size, rcv_name,
1788 timeout, thread_syscall_return, scatter_list_size);
1789 thread_syscall_return(mr);
1790 }
1791
1792 return MACH_MSG_SUCCESS;
1793}
1794
9bccf70c
A
1795/*
1796 * Routine: mach_msg_trap [mach trap]
1797 * Purpose:
1798 * Possibly send a message; possibly receive a message.
1799 * Conditions:
1800 * Nothing locked.
1801 * Returns:
1802 * All of mach_msg_send and mach_msg_receive error codes.
1803 */
1804
1805mach_msg_return_t
1806mach_msg_trap(
1807 mach_msg_header_t *msg,
1808 mach_msg_option_t option,
1809 mach_msg_size_t send_size,
1810 mach_msg_size_t rcv_size,
1811 mach_port_name_t rcv_name,
1812 mach_msg_timeout_t timeout,
1813 mach_port_name_t notify)
1814{
1815 return mach_msg_overwrite_trap(msg,
1816 option,
1817 send_size,
1818 rcv_size,
1819 rcv_name,
1820 timeout,
1821 notify,
1822 (mach_msg_header_t *)0,
1823 (mach_msg_size_t)0);
1824}
1825
1826
1c79356b
A
1827/*
1828 * Routine: msg_receive_error [internal]
1829 * Purpose:
1830 * Builds a minimal header/trailer and copies it to
1831 * the user message buffer. Invoked when in the case of a
1832 * MACH_RCV_TOO_LARGE or MACH_RCV_BODY_ERROR error.
1833 * Conditions:
1834 * Nothing locked.
1835 * Returns:
1836 * MACH_MSG_SUCCESS minimal header/trailer copied
1837 * MACH_RCV_INVALID_DATA copyout to user buffer failed
1838 */
1839
1840mach_msg_return_t
1841msg_receive_error(
1842 ipc_kmsg_t kmsg,
1843 mach_msg_header_t *msg,
1844 mach_msg_option_t option,
1845 mach_port_seqno_t seqno,
1846 ipc_space_t space)
1847{
1848 mach_msg_format_0_trailer_t *trailer;
1849
1850 /*
1851 * Copy out the destination port in the message.
1852 * Destroy all other rights and memory in the message.
1853 */
1854 ipc_kmsg_copyout_dest(kmsg, space);
1855
1856 /*
1857 * Build a minimal message with the requested trailer.
1858 */
1859 trailer = (mach_msg_format_0_trailer_t *)
1860 ((vm_offset_t)&kmsg->ikm_header +
1861 round_msg(sizeof(mach_msg_header_t)));
1862 kmsg->ikm_header.msgh_size = sizeof(mach_msg_header_t);
1863 bcopy( (char *)&trailer_template,
1864 (char *)trailer,
1865 sizeof(trailer_template));
1866 if (option & MACH_RCV_TRAILER_MASK) {
1867 trailer->msgh_seqno = seqno;
1868 trailer->msgh_trailer_size = REQUESTED_TRAILER_SIZE(option);
1869 }
1870
1871 /*
1872 * Copy the message to user space
1873 */
1874 if (ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size +
1875 trailer->msgh_trailer_size) == MACH_RCV_INVALID_DATA)
1876 return(MACH_RCV_INVALID_DATA);
1877 else
1878 return(MACH_MSG_SUCCESS);
1879}