]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ipc/flipc.c
xnu-6153.11.26.tar.gz
[apple/xnu.git] / osfmk / ipc / flipc.c
1 /*
2 * Copyright (c) 2015-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /* File: ipc/flipc.h
29 * Author: Dean Reece
30 * Date: 2016
31 *
32 * Implementation of fast local ipc (flipc).
33 */
34
35
36 #include <mach/mach_types.h>
37 #include <mach/boolean.h>
38 #include <mach/kern_return.h>
39
40 #include <kern/kern_types.h>
41 #include <kern/assert.h>
42 #include <kern/host.h>
43 #include <kern/kalloc.h>
44 #include <kern/mach_node.h>
45
46 #include <ipc/port.h>
47 #include <ipc/ipc_types.h>
48 #include <ipc/ipc_init.h>
49 #include <ipc/ipc_kmsg.h>
50 #include <ipc/ipc_port.h>
51 #include <ipc/ipc_pset.h>
52 #include <ipc/ipc_table.h>
53 #include <ipc/ipc_entry.h>
54 #include <ipc/flipc.h>
55
56 #pragma pack(4)
57
58
59 /*** FLIPC Internal Implementation (private to flipc.c) ***/
60
61
62 zone_t flipc_port_zone;
63
64
65 /* Get the mnl_name associated with local ipc_port <lport>.
66 * Returns MNL_NAME_NULL if <lport> is invalid or not a flipc port.
67 */
68 static inline mnl_name_t
69 mnl_name_from_port(ipc_port_t lport)
70 {
71 mnl_name_t name = MNL_NAME_NULL;
72
73 if (IP_VALID(lport)) {
74 flipc_port_t fport = lport->ip_messages.data.port.fport;
75 if (FPORT_VALID(fport)) {
76 name = fport->obj.name;
77 }
78 }
79 return name;
80 }
81
82
83 /* Lookup the ipc_port associated with mnl_name <name>.
84 * Returns IP_NULL if <name> is invalid or not a known mnl object.
85 */
86 static inline ipc_port_t
87 mnl_name_to_port(mnl_name_t name)
88 {
89 ipc_port_t lport = IP_NULL;
90
91 if (MNL_NAME_VALID(name)) {
92 flipc_port_t fport = (flipc_port_t)mnl_obj_lookup(name);
93 if (FPORT_VALID(fport)) {
94 lport = fport->lport;
95 }
96 }
97 return lport;
98 }
99
100
101 /* flipc_port_create() is called to convert a regular mach port into a
102 * flipc port (i.e., the port has one or more rights off-node).
103 * <lport> must be locked on entry and is not unlocked on return.
104 */
105 static kern_return_t
106 flipc_port_create(ipc_port_t lport, mach_node_t node, mnl_name_t name)
107 {
108 /* Ensure parameters are valid and not already linked */
109 assert(IP_VALID(lport));
110 assert(MACH_NODE_VALID(node));
111 assert(MNL_NAME_VALID(name));
112 assert(!FPORT_VALID(lport->ip_messages.imq_fport));
113
114 /* Allocate and initialize a flipc port */
115 flipc_port_t fport = (flipc_port_t) zalloc(flipc_port_zone);
116 if (!FPORT_VALID(fport)) {
117 return KERN_RESOURCE_SHORTAGE;
118 }
119 bzero(fport, sizeof(struct flipc_port));
120 fport->obj.name = name;
121 fport->hostnode = node;
122 if (node == localnode) {
123 fport->state = FPORT_STATE_PRINCIPAL;
124 } else {
125 fport->state = FPORT_STATE_PROXY;
126 }
127
128 /* Link co-structures (lport is locked) */
129 fport->lport = lport;
130 lport->ip_messages.imq_fport = fport;
131
132 /* Add fport to the name hash table; revert link if insert fails */
133 kern_return_t kr = mnl_obj_insert((mnl_obj_t)fport);
134 if (kr != KERN_SUCCESS) {
135 lport->ip_messages.imq_fport = FPORT_NULL;
136 fport->lport = IP_NULL;
137 zfree(flipc_port_zone, fport);
138 }
139
140 return kr;
141 }
142
143
144 /* flipc_port_destroy() is called to convert a flipc port back to a
145 * local-only ipc port (i.e., the port has no remaining off-node rights).
146 * This will dispose of any undelivered flipc messages, generating NAKs if
147 * needed. <lport> must be locked on entry and is not unlocked on return.
148 */
149 static void
150 flipc_port_destroy(ipc_port_t lport)
151 {
152 /* Ensure parameter is valid, and linked to an fport with a valid name */
153 assert(IP_VALID(lport));
154 ipc_mqueue_t port_mq = &lport->ip_messages;
155 flipc_port_t fport = port_mq->data.port.fport;
156 assert(FPORT_VALID(fport));
157 assert(MNL_NAME_VALID(fport->obj.name));
158
159 /* Dispose of any undelivered messages */
160 int m = port_mq->data.port.msgcount;
161 if (m > 0) {
162 ipc_kmsg_t kmsg;
163 #if DEBUG
164 printf("flipc: destroying %p with %d undelivered msgs\n", lport, m);
165 #endif
166
167 /* Logic was lifted from ipc_mqueue_select_on_thread() */
168 while (m--) {
169 kmsg = ipc_kmsg_queue_first(&port_mq->imq_messages);
170 assert(kmsg != IKM_NULL);
171 ipc_kmsg_rmqueue(&port_mq->imq_messages, kmsg);
172 if (fport->state == FPORT_STATE_PRINCIPAL) {
173 flipc_msg_ack(kmsg->ikm_node, port_mq, FALSE);
174 }
175 ipc_mqueue_release_msgcount(port_mq, NULL);
176 port_mq->imq_seqno++;
177 }
178 }
179
180 /* Remove from name hash table, unlink co-structures, and free fport */
181 mnl_obj_remove(fport->obj.name);
182 lport->ip_messages.data.port.fport = FPORT_NULL;
183 fport->lport = IP_NULL;
184 zfree(flipc_port_zone, fport);
185 }
186
187
188 /*
189 * Routine: flipc_msg_size_from_kmsg(ipc_kmsg_t kmsg)
190 * Purpose:
191 * Compute the size of the buffer needed to hold the translated flipc
192 * message. All identifiers are converted to flipc_names which are 64b.
193 * If this node's pointers are a different size, we have to allow for
194 * expansion of the descriptors as appropriate.
195 * Conditions:
196 * Nothing locked.
197 * Returns:
198 * size of the message as it would be sent over the flipc link.
199 */
200 static mach_msg_size_t
201 flipc_msg_size_from_kmsg(ipc_kmsg_t kmsg)
202 {
203 mach_msg_size_t fsize = kmsg->ikm_header->msgh_size;
204
205 if (kmsg->ikm_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
206 PE_enter_debugger("flipc_msg_size_from_kmsg(): Complex messages not supported.");
207 }
208
209 return fsize;
210 }
211
212
213 /* Translate a kmsg into a flipc msg suitable to transmit over the mach node
214 * link. All in-line rights and objects are similarly processed. If the msg
215 * moves a receive right, then queued messages may need to be moved as a
216 * result, causing this function to ultimately be recursive.
217 */
218 static kern_return_t
219 mnl_msg_from_kmsg(ipc_kmsg_t kmsg, mnl_msg_t *fmsgp)
220 {
221 if (kmsg->ikm_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
222 printf("mnl_msg_from_kmsg(): Complex messages not supported.");
223 return KERN_FAILURE;
224 }
225
226 mach_msg_size_t fsize = flipc_msg_size_from_kmsg(kmsg);
227
228 mnl_msg_t fmsg = mnl_msg_alloc(fsize, 0);
229
230 if (fmsg == MNL_MSG_NULL) {
231 return KERN_RESOURCE_SHORTAGE;
232 }
233
234 /* Setup flipc message header */
235 fmsg->sub = MACH_NODE_SUB_FLIPC;
236 fmsg->cmd = FLIPC_CMD_IPCMESSAGE;
237 fmsg->node_id = localnode_id; // Message is from us
238 fmsg->qos = 0; // not used
239 fmsg->size = fsize; // Payload size (does NOT include mnl_msg header)
240 fmsg->object = kmsg->ikm_header->msgh_remote_port->ip_messages.data.port.fport->obj.name;
241
242 /* Copy body of message */
243 bcopy((const void*)kmsg->ikm_header, (void*)MNL_MSG_PAYLOAD(fmsg), fsize);
244
245 // Convert port fields
246 mach_msg_header_t *mmsg = (mach_msg_header_t*)MNL_MSG_PAYLOAD(fmsg);
247 mmsg->msgh_remote_port = (mach_port_t)fmsg->object;
248 mmsg->msgh_local_port = (mach_port_t)
249 mnl_name_from_port(mmsg->msgh_local_port);
250 mmsg->msgh_voucher_port = (mach_port_name_t)MNL_NAME_NULL;
251
252 *fmsgp = (mnl_msg_t)fmsg;
253
254 return KERN_SUCCESS;
255 }
256
257
258 /* lifted from ipc_mig.c:mach_msg_send_from_kernel_proper() */
259 static mach_msg_return_t
260 mach_msg_send_from_remote_kernel(mach_msg_header_t *msg,
261 mach_msg_size_t send_size,
262 mach_node_t node)
263 {
264 ipc_kmsg_t kmsg;
265 mach_msg_return_t mr;
266
267 mr = ipc_kmsg_get_from_kernel(msg, send_size, &kmsg);
268 if (mr != MACH_MSG_SUCCESS) {
269 return mr;
270 }
271
272 mr = ipc_kmsg_copyin_from_kernel(kmsg);
273 if (mr != MACH_MSG_SUCCESS) {
274 ipc_kmsg_free(kmsg);
275 return mr;
276 }
277
278 kmsg->ikm_node = node; // node that needs to receive message ack
279 mr = ipc_kmsg_send(kmsg,
280 MACH_SEND_KERNEL_DEFAULT,
281 MACH_MSG_TIMEOUT_NONE);
282 if (mr != MACH_MSG_SUCCESS) {
283 ipc_kmsg_destroy(kmsg);
284 }
285
286 return mr;
287 }
288
289
290 /* Translate a flipc msg <fmsg> into a kmsg and post it to the appropriate
291 * port. <node> is the node that originated the message, not necessarily the
292 * node we received it from. This will block if the receiving port is full.
293 */
294 static mach_msg_return_t
295 flipc_cmd_ipc(mnl_msg_t fmsg,
296 mach_node_t node,
297 uint32_t flags __unused)
298 {
299 mach_msg_header_t *mmsg;
300
301 // Convert flipc message into mach message in place to avoid alloc/copy
302 mmsg = (mach_msg_header_t*)MNL_MSG_PAYLOAD(fmsg);
303 mmsg->msgh_size = fmsg->size;
304 mmsg->msgh_remote_port = mnl_name_to_port(fmsg->object);
305 mmsg->msgh_local_port = mnl_name_to_port((mnl_name_t)mmsg->msgh_local_port);
306 mmsg->msgh_voucher_port = (mach_port_name_t)MACH_PORT_NULL;
307 mmsg->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
308 // unchanged: msgh_id
309
310 return mach_msg_send_from_remote_kernel(mmsg, fmsg->size, node);
311 }
312
313
314 /* Called when an ACKMESSAGE packet is received. <name> indicates
315 * the flipc name of the port holding the messages to be acknowledged.
316 * <msg_count> indicates the number of messages being acked for this node:port.
317 */
318 static void
319 flipc_cmd_ack(flipc_ack_msg_t fmsg,
320 mach_node_t node __unused,
321 uint32_t flags __unused)
322 {
323 unsigned int msg_count = fmsg->msg_count;
324 thread_t thread = current_thread();
325 boolean_t kick = FALSE;
326
327 flipc_port_t fport = (flipc_port_t)mnl_obj_lookup(fmsg->mnl.object);
328
329 ipc_port_t lport = fport->lport;
330 ip_lock(lport);
331
332 ipc_mqueue_t lport_mq = &lport->ip_messages;
333 imq_lock(lport_mq);
334
335 assert(fport->peek_count >= msg_count); // Can't ack what we haven't peeked!
336
337 while (msg_count--) {
338 ipc_mqueue_select_on_thread(lport_mq, IMQ_NULL, 0, 0, thread);
339 fport->peek_count--;
340 kick |= ipc_kmsg_delayed_destroy(thread->ith_kmsg);
341 }
342
343 imq_unlock(lport_mq);
344 ip_unlock(lport);
345
346 if (kick) {
347 ipc_kmsg_reap_delayed();
348 }
349 }
350
351
352
353 /*** FLIPC Node Managment Functions (called by mach node layer) ***/
354
355
356 /* The mach node layer calls flipc_init() once before it calls any other
357 * flipc entry points. Returns KERN_SUCCESS on success; otherwise flipc
358 * is not initialized and cannot be used.
359 */
360 kern_return_t
361 flipc_init(void)
362 {
363 /* Create zone for flipc ports.
364 * TODO: Pick a better max value than ipc_port_max>>4
365 */
366 flipc_port_zone = zinit(sizeof(struct flipc_port),
367 (ipc_port_max >> 4) * sizeof(struct flipc_port),
368 sizeof(struct flipc_port),
369 "flipc ports");
370
371 zone_change(flipc_port_zone, Z_CALLERACCT, FALSE);
372 zone_change(flipc_port_zone, Z_NOENCRYPT, TRUE);
373 return KERN_SUCCESS;
374 }
375
376
377 /* flipc_node_prepare() is called by mach node layer when a remote node is
378 * registered by a link driver, or when the bootstrap port changes for the
379 * local node. This is the flipc layer's opportunity to initialize per-node
380 * flipc state, and to convert the node's bootstrap port into a flipc port.
381 * Note that the node is not yet in the mach node table.
382 * Returns KERN_SUCCESS on success; otherwise node is not prepared.
383 */
384 kern_return_t
385 flipc_node_prepare(mach_node_t node)
386 {
387 kern_return_t kr;
388
389 assert(MACH_NODE_VALID(node));
390 ipc_port_t bs_port = node->bootstrap_port;
391 assert(IP_VALID(bs_port));
392
393 ip_lock(bs_port);
394
395 kr = flipc_port_create(bs_port,
396 node,
397 MNL_NAME_BOOTSTRAP(node->info.node_id));
398 ip_unlock(bs_port);
399
400 return kr;
401 }
402
403
404 /* flipc_node_retire() is called by mach node layer when a remote node is
405 * terminated by a link driver, or when the local node's bootstrap port
406 * becomes invalid. This is the flipc layer's opportunity to free per-node
407 * flipc state, and to revert the node's bootstrap port to a local ipc port.
408 * <node> must be locked by the caller.
409 * Returns KERN_SUCCESS on success.
410 */
411 kern_return_t
412 flipc_node_retire(mach_node_t node)
413 {
414 if (!MACH_NODE_VALID(node)) {
415 return KERN_NODE_DOWN;
416 }
417
418 ipc_port_t bs_port = node->bootstrap_port;
419 if (IP_VALID(bs_port)) {
420 ip_lock(bs_port);
421 flipc_port_destroy(bs_port);
422 ip_unlock(bs_port);
423 }
424
425 return KERN_SUCCESS;
426 }
427
428
429 /*** FLIPC Message Functions (called by mach node layer) ***/
430
431
432 /* The node layer calls flipc_msg_to_remote_node() to fetch the next message
433 * for <node>. This function will block until a message is available or the
434 * node is terminated, in which case it returns MNL_MSG_NULL.
435 */
436 mnl_msg_t
437 flipc_msg_to_remote_node(mach_node_t to_node,
438 uint32_t flags __unused)
439 {
440 mach_port_seqno_t msgoff;
441 ipc_kmsg_t kmsg = IKM_NULL;
442 mnl_msg_t fmsg = MNL_MSG_NULL;
443
444 assert(to_node != localnode);
445 assert(get_preemption_level() == 0);
446
447 ipc_mqueue_t portset_mq = &to_node->proxy_port_set->ips_messages;
448 ipc_mqueue_t port_mq = IMQ_NULL;
449
450 while (!to_node->dead) {
451 /* Fetch next message from proxy port */
452 ipc_mqueue_receive(portset_mq, MACH_PEEK_MSG, 0, 0, THREAD_ABORTSAFE);
453
454 thread_t thread = current_thread();
455 if (thread->ith_state == MACH_PEEK_READY) {
456 port_mq = thread->ith_peekq;
457 thread->ith_peekq = IMQ_NULL;
458 } else {
459 panic("Unexpected thread state %d after ipc_mqueue_receive()",
460 thread->ith_state);
461 }
462
463 assert(get_preemption_level() == 0);
464 imq_lock(port_mq);
465
466 flipc_port_t fport = port_mq->data.port.fport;
467
468 if (FPORT_VALID(fport)) {
469 msgoff = port_mq->data.port.fport->peek_count;
470
471 ipc_mqueue_peek_locked(port_mq, &msgoff, NULL, NULL, NULL, &kmsg);
472 if (kmsg != IKM_NULL) {
473 port_mq->data.port.fport->peek_count++;
474 }
475
476 /* Clean up outstanding prepost on port_mq.
477 * This also unlocks port_mq.
478 */
479 ipc_mqueue_release_peek_ref(port_mq);
480 assert(get_preemption_level() == 0);
481
482 /* DANGER: The code below must be allowed to allocate so it can't
483 * run under the protection of the imq_lock, but that leaves mqueue
484 * open for business for a small window before we examine kmsg.
485 * This SHOULD be OK, since we are the only thread looking.
486 */
487 if (kmsg != IKM_NULL) {
488 mnl_msg_from_kmsg(kmsg, (mnl_msg_t*)&fmsg);
489 }
490 } else {
491 /* Must be from the control_port, which is not a flipc port */
492 assert(!FPORT_VALID(port_mq->data.port.fport));
493
494 /* This is a simplified copy of ipc_mqueue_select_on_thread() */
495 kmsg = ipc_kmsg_queue_first(&port_mq->imq_messages);
496 assert(kmsg != IKM_NULL);
497 ipc_kmsg_rmqueue(&port_mq->imq_messages, kmsg);
498 ipc_mqueue_release_msgcount(port_mq, portset_mq);
499 imq_unlock(port_mq);
500 current_task()->messages_received++;
501 ip_release(to_node->control_port); // Should derive ref from port_mq
502
503 /* We just pass the kmsg payload as the fmsg.
504 * flipc_msg_free() will notice and free the kmsg properly.
505 */
506 mach_msg_header_t *hdr = kmsg->ikm_header;
507 fmsg = (mnl_msg_t)(&hdr[1]);
508 /* Stash kmsg pointer just before fmsg */
509 *(ipc_kmsg_t*)((vm_offset_t)fmsg - sizeof(vm_offset_t)) = kmsg;
510 }
511
512 if (MNL_MSG_VALID(fmsg)) {
513 break;
514 }
515 }
516 assert(MNL_MSG_VALID(fmsg));
517 return fmsg;
518 }
519
520
521 /* The mach node layer calls this to deliver an incoming message. It is the
522 * responsibility of the caller to release the received message buffer after
523 * return.
524 */
525 void
526 flipc_msg_from_node(mach_node_t from_node __unused,
527 mnl_msg_t msg,
528 uint32_t flags)
529 {
530 /* Note that if flipc message forwarding is supported, the from_node arg
531 * may not match fmsg->node_id. The former is the node from which we
532 * received the message; the latter is the node that originated the
533 * message. We use the originating node, which is where the ack goes.
534 */
535 assert(msg->sub == MACH_NODE_SUB_FLIPC);
536 mach_node_t node = mach_node_for_id_locked(msg->node_id, FALSE, FALSE);
537 MACH_NODE_UNLOCK(node);
538
539 switch (msg->cmd) {
540 case FLIPC_CMD_IPCMESSAGE:
541 flipc_cmd_ipc(msg, node, flags);
542 break;
543
544 case FLIPC_CMD_ACKMESSAGE:
545 case FLIPC_CMD_NAKMESSAGE:
546 flipc_cmd_ack((flipc_ack_msg_t)msg, node, flags);
547 break;
548
549 default:
550 #if DEBUG
551 PE_enter_debugger("flipc_incoming(): Invalid command");
552 #endif
553 break;
554 }
555 }
556
557
558 /* The node layer calls flipc_msg_free() to dispose of sent messages that
559 * originated in the FLIPC layer. This allows us to repurpose the payload
560 * of an ack or nak kmsg as a flipc message to avoid a copy - we detect
561 * such messages here and free them appropriately.
562 */
563 void
564 flipc_msg_free(mnl_msg_t msg,
565 uint32_t flags)
566 {
567 switch (msg->cmd) {
568 case FLIPC_CMD_ACKMESSAGE: // Flipc msg is a kmsg in disguise...
569 case FLIPC_CMD_NAKMESSAGE: // Convert back to kmsg for disposal
570 ipc_kmsg_free(*(ipc_kmsg_t*)((vm_offset_t)msg - sizeof(vm_offset_t)));
571 break;
572
573 default: // Flipc msg is not a kmsg in disguise; dispose of normally
574 mnl_msg_free(msg, flags);
575 break;
576 }
577 }
578
579
580 /*** FLIPC Message Functions (called by mach ipc subsystem) ***/
581
582 /* Ack's one message sent to <mqueue> from <node>. A new kmsg is allocated
583 * and filled in as an ack, then posted to the node's contol port. This will
584 * wake the link driver (if sleeping) and cause the ack to be included with
585 * normal IPC traffic.
586 *
587 * This function immediately returns if <fport> or <node> is invalid, so it
588 * is safe & quick to call speculatively.
589 *
590 * Called from mach ipc_mqueue.c when a flipc-originated message is consumed.
591 */
592 void
593 flipc_msg_ack(mach_node_t node,
594 ipc_mqueue_t mqueue,
595 boolean_t delivered)
596 {
597 flipc_port_t fport = mqueue->imq_fport;
598
599 assert(FPORT_VALID(fport));
600 assert(MACH_NODE_VALID(node));
601
602 mnl_name_t name = MNL_NAME_NULL;
603 mach_node_id_t nid = HOST_LOCAL_NODE;
604 ipc_port_t ack_port = IP_NULL;
605
606 ip_lock(fport->lport);
607 name = fport->obj.name;
608 ip_unlock(fport->lport);
609
610 if (!MNL_NAME_VALID(name)) {
611 return;
612 }
613
614 MACH_NODE_LOCK(node);
615 if (node->active) {
616 nid = node->info.node_id;
617 ack_port = node->control_port;
618 }
619 MACH_NODE_UNLOCK(node);
620
621 if (!IP_VALID(ack_port) || !MACH_NODE_ID_VALID(nid)) {
622 return;
623 }
624
625 /* We have a valid node id & obj name, and a port to send the ack to. */
626 ipc_kmsg_t kmsg = ipc_kmsg_alloc(sizeof(struct flipc_ack_msg) + MAX_TRAILER_SIZE);
627 assert((unsigned long long)kmsg >= 4ULL);//!= IKM_NULL);
628 mach_msg_header_t *msg = kmsg->ikm_header;
629
630 /* Fill in the mach_msg_header struct */
631 msg->msgh_bits = MACH_MSGH_BITS_SET(0, 0, 0, 0);
632 msg->msgh_size = sizeof(msg);
633 msg->msgh_remote_port = ack_port;
634 msg->msgh_local_port = MACH_PORT_NULL;
635 msg->msgh_voucher_port = MACH_PORT_NULL;
636 msg->msgh_id = FLIPC_CMD_ID;
637
638 /* Fill in the flipc_ack_msg struct */
639 flipc_ack_msg_t fmsg = (flipc_ack_msg_t)(&msg[1]);
640 fmsg->resend_to = HOST_LOCAL_NODE;
641 fmsg->msg_count = 1; // Might want to coalesce acks to a node/name pair
642
643 /* Fill in the mnl_msg struct */
644 fmsg->mnl.sub = MACH_NODE_SUB_FLIPC;
645 fmsg->mnl.cmd = delivered ? FLIPC_CMD_ACKMESSAGE : FLIPC_CMD_NAKMESSAGE;
646 fmsg->mnl.qos = 0; // Doesn't do anything yet
647 fmsg->mnl.flags = 0;
648 fmsg->mnl.node_id = nid;
649 fmsg->mnl.object = name;
650 fmsg->mnl.options = 0;
651 fmsg->mnl.size = sizeof(struct flipc_ack_msg) - sizeof(struct mnl_msg);
652
653 #if (0)
654 mach_msg_return_t mmr;
655 ipc_mqueue_t ack_mqueue;
656
657 ip_lock(ack_port);
658 ack_mqueue = &ack_port->ip_messages;
659 imq_lock(ack_mqueue);
660 ip_unlock(ack_port);
661
662 /* ipc_mqueue_send() unlocks ack_mqueue */
663 mmr = ipc_mqueue_send(ack_mqueue, kmsg, 0, 0);
664 #else
665 kern_return_t kr;
666 kr = ipc_kmsg_send(kmsg,
667 MACH_SEND_KERNEL_DEFAULT,
668 MACH_MSG_TIMEOUT_NONE);
669 #endif
670 }