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