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