]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ipc/ipc_pset.c
xnu-3247.10.11.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_pset.c
CommitLineData
1c79356b 1/*
91447636 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31/*
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56/*
57 */
58/*
59 * File: ipc/ipc_pset.c
60 * Author: Rich Draves
61 * Date: 1989
62 *
63 * Functions to manipulate IPC port sets.
64 */
65
66#include <mach/port.h>
67#include <mach/kern_return.h>
68#include <mach/message.h>
69#include <ipc/ipc_mqueue.h>
70#include <ipc/ipc_object.h>
71#include <ipc/ipc_pset.h>
72#include <ipc/ipc_right.h>
73#include <ipc/ipc_space.h>
74#include <ipc/ipc_port.h>
1c79356b 75
91447636
A
76#include <kern/kern_types.h>
77#include <kern/spl.h>
b0d623f7
A
78
79#include <vm/vm_map.h>
80
1c79356b
A
81/*
82 * Routine: ipc_pset_alloc
83 * Purpose:
84 * Allocate a port set.
85 * Conditions:
86 * Nothing locked. If successful, the port set is returned
87 * locked. (The caller doesn't have a reference.)
88 * Returns:
89 * KERN_SUCCESS The port set is allocated.
90 * KERN_INVALID_TASK The space is dead.
91 * KERN_NO_SPACE No room for an entry in the space.
92 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
93 */
94
95kern_return_t
96ipc_pset_alloc(
97 ipc_space_t space,
98 mach_port_name_t *namep,
99 ipc_pset_t *psetp)
100{
101 ipc_pset_t pset;
102 mach_port_name_t name;
103 kern_return_t kr;
3e170ce0
A
104 uint64_t reserved_link;
105
106 reserved_link = waitq_link_reserve(NULL);
1c79356b
A
107
108 kr = ipc_object_alloc(space, IOT_PORT_SET,
109 MACH_PORT_TYPE_PORT_SET, 0,
110 &name, (ipc_object_t *) &pset);
3e170ce0
A
111 if (kr != KERN_SUCCESS) {
112 waitq_link_release(reserved_link);
1c79356b 113 return kr;
3e170ce0 114 }
99c3a104 115 /* pset and space are locked */
1c79356b
A
116
117 pset->ips_local_name = name;
3e170ce0 118 ipc_mqueue_init(&pset->ips_messages, TRUE /* set */, &reserved_link);
99c3a104 119 is_write_unlock(space);
1c79356b 120
3e170ce0
A
121 waitq_link_release(reserved_link);
122
1c79356b
A
123 *namep = name;
124 *psetp = pset;
125 return KERN_SUCCESS;
126}
127
128/*
129 * Routine: ipc_pset_alloc_name
130 * Purpose:
131 * Allocate a port set, with a specific name.
132 * Conditions:
133 * Nothing locked. If successful, the port set is returned
134 * locked. (The caller doesn't have a reference.)
135 * Returns:
136 * KERN_SUCCESS The port set is allocated.
137 * KERN_INVALID_TASK The space is dead.
138 * KERN_NAME_EXISTS The name already denotes a right.
139 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
140 */
141
142kern_return_t
143ipc_pset_alloc_name(
144 ipc_space_t space,
145 mach_port_name_t name,
146 ipc_pset_t *psetp)
147{
148 ipc_pset_t pset;
149 kern_return_t kr;
3e170ce0
A
150 uint64_t reserved_link;
151
1c79356b 152
3e170ce0 153 reserved_link = waitq_link_reserve(NULL);
1c79356b
A
154
155 kr = ipc_object_alloc_name(space, IOT_PORT_SET,
156 MACH_PORT_TYPE_PORT_SET, 0,
157 name, (ipc_object_t *) &pset);
3e170ce0
A
158 if (kr != KERN_SUCCESS) {
159 waitq_link_release(reserved_link);
1c79356b 160 return kr;
3e170ce0 161 }
1c79356b
A
162 /* pset is locked */
163
164 pset->ips_local_name = name;
3e170ce0
A
165 ipc_mqueue_init(&pset->ips_messages, TRUE /* set */, &reserved_link);
166
167 waitq_link_release(reserved_link);
1c79356b
A
168
169 *psetp = pset;
170 return KERN_SUCCESS;
171}
172
173/*
174 * Routine: ipc_pset_member
175 * Purpose:
176 * Checks to see if a port is a member of a pset
177 * Conditions:
178 * Both port and port set are locked.
179 * The port must be active.
180 */
181boolean_t
182ipc_pset_member(
183 ipc_pset_t pset,
184 ipc_port_t port)
185{
186 assert(ip_active(port));
187
188 return (ipc_mqueue_member(&port->ip_messages, &pset->ips_messages));
189}
190
191
192/*
193 * Routine: ipc_pset_add
194 * Purpose:
195 * Puts a port into a port set.
1c79356b
A
196 * Conditions:
197 * Both port and port set are locked and active.
198 * The owner of the port set is also receiver for the port.
199 */
200
201kern_return_t
202ipc_pset_add(
316670eb
A
203 ipc_pset_t pset,
204 ipc_port_t port,
3e170ce0
A
205 uint64_t *reserved_link,
206 uint64_t *reserved_prepost)
1c79356b 207{
9bccf70c
A
208 kern_return_t kr;
209
1c79356b
A
210 assert(ips_active(pset));
211 assert(ip_active(port));
212
3e170ce0
A
213 kr = ipc_mqueue_add(&port->ip_messages, &pset->ips_messages,
214 reserved_link, reserved_prepost);
1c79356b 215
9bccf70c 216 return kr;
1c79356b
A
217}
218
219
220
221/*
222 * Routine: ipc_pset_remove
223 * Purpose:
224 * Removes a port from a port set.
225 * The port set loses a reference.
226 * Conditions:
227 * Both port and port set are locked.
228 * The port must be active.
229 */
230
231kern_return_t
232ipc_pset_remove(
316670eb 233 ipc_pset_t pset,
3e170ce0 234 ipc_port_t port)
1c79356b 235{
9bccf70c 236 kern_return_t kr;
1c79356b
A
237
238 assert(ip_active(port));
239
3e170ce0 240 if (port->ip_in_pset == 0)
1c79356b
A
241 return KERN_NOT_IN_SET;
242
3e170ce0 243 kr = ipc_mqueue_remove(&port->ip_messages, &pset->ips_messages);
9bccf70c
A
244
245 return kr;
1c79356b
A
246}
247
248/*
9bccf70c 249 * Routine: ipc_pset_remove_from_all
1c79356b
A
250 * Purpose:
251 * Removes a port from all it's port sets.
1c79356b
A
252 * Conditions:
253 * port is locked and active.
254 */
255
256kern_return_t
9bccf70c 257ipc_pset_remove_from_all(
3e170ce0 258 ipc_port_t port)
1c79356b 259{
1c79356b
A
260 assert(ip_active(port));
261
3e170ce0 262 if (port->ip_in_pset == 0)
1c79356b
A
263 return KERN_NOT_IN_SET;
264
265 /*
9bccf70c 266 * Remove the port's mqueue from all sets
1c79356b 267 */
3e170ce0 268 ipc_mqueue_remove_from_all(&port->ip_messages);
1c79356b
A
269 return KERN_SUCCESS;
270}
271
272
273/*
274 * Routine: ipc_pset_destroy
275 * Purpose:
276 * Destroys a port_set.
1c79356b
A
277 * Conditions:
278 * The port_set is locked and alive.
279 * The caller has a reference, which is consumed.
280 * Afterwards, the port_set is unlocked and dead.
281 */
282
283void
284ipc_pset_destroy(
285 ipc_pset_t pset)
286{
287 spl_t s;
288
289 assert(ips_active(pset));
290
291 pset->ips_object.io_bits &= ~IO_BITS_ACTIVE;
292
9bccf70c
A
293 /*
294 * remove all the member message queues
3e170ce0 295 * AND remove this message queue from any containing sets
9bccf70c 296 */
3e170ce0 297 ipc_mqueue_remove_all(&pset->ips_messages);
9bccf70c 298
b0d623f7
A
299 /*
300 * Set all waiters on the portset running to
301 * discover the change.
302 */
1c79356b
A
303 s = splsched();
304 imq_lock(&pset->ips_messages);
305 ipc_mqueue_changed(&pset->ips_messages);
306 imq_unlock(&pset->ips_messages);
307 splx(s);
308
3e170ce0
A
309 ipc_mqueue_deinit(&pset->ips_messages);
310
316670eb
A
311 ips_unlock(pset);
312 ips_release(pset); /* consume the ref our caller gave us */
1c79356b
A
313}
314
b0d623f7
A
315/* Kqueue EVFILT_MACHPORT support */
316
317#include <sys/errno.h>
318
319static int filt_machportattach(struct knote *kn);
320static void filt_machportdetach(struct knote *kn);
321static int filt_machport(struct knote *kn, long hint);
3e170ce0 322static void filt_machporttouch(struct knote *kn, struct kevent_internal_s *kev, long type);
6d2010ae 323static unsigned filt_machportpeek(struct knote *kn);
b0d623f7
A
324struct filterops machport_filtops = {
325 .f_attach = filt_machportattach,
326 .f_detach = filt_machportdetach,
327 .f_event = filt_machport,
328 .f_touch = filt_machporttouch,
329 .f_peek = filt_machportpeek,
330};
331
332static int
333filt_machportattach(
334 struct knote *kn)
335{
336 mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident;
3e170ce0 337 uint64_t wq_link_id = waitq_link_reserve(NULL);
b0d623f7
A
338 ipc_pset_t pset = IPS_NULL;
339 int result = ENOSYS;
340 kern_return_t kr;
341
342 kr = ipc_object_translate(current_space(), name,
343 MACH_PORT_RIGHT_PORT_SET,
344 (ipc_object_t *)&pset);
345 if (kr != KERN_SUCCESS) {
3e170ce0 346 waitq_link_release(wq_link_id);
316670eb 347 return (kr == KERN_INVALID_NAME ? ENOENT : ENOTSUP);
b0d623f7
A
348 }
349 /* We've got a lock on pset */
350
b0d623f7
A
351 /*
352 * Bind the portset wait queue directly to knote/kqueue.
353 * This allows us to just use wait_queue foo to effect a wakeup,
354 * rather than having to call knote() from the Mach code on each
355 * message.
356 */
3e170ce0 357 result = knote_link_waitq(kn, &pset->ips_messages.imq_wait_queue, &wq_link_id);
316670eb 358 if (result == 0) {
3e170ce0 359 waitq_link_release(wq_link_id);
316670eb
A
360 /* keep a reference for the knote */
361 kn->kn_ptr.p_pset = pset;
362 ips_reference(pset);
363 ips_unlock(pset);
364 return 0;
365 }
366
b0d623f7 367 ips_unlock(pset);
3e170ce0 368 waitq_link_release(wq_link_id);
b0d623f7
A
369 return result;
370}
371
372static void
373filt_machportdetach(
374 struct knote *kn)
375{
376 ipc_pset_t pset = kn->kn_ptr.p_pset;
377
378 /*
379 * Unlink the portset wait queue from knote/kqueue,
380 * and release our reference on the portset.
381 */
382 ips_lock(pset);
3e170ce0 383 (void)knote_unlink_waitq(kn, &pset->ips_messages.imq_wait_queue);
316670eb
A
384 kn->kn_ptr.p_pset = IPS_NULL;
385 ips_unlock(pset);
386 ips_release(pset);
b0d623f7
A
387}
388
389static int
390filt_machport(
391 struct knote *kn,
392 __unused long hint)
393{
394 mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident;
395 ipc_pset_t pset = IPS_NULL;
396 wait_result_t wresult;
397 thread_t self = current_thread();
398 kern_return_t kr;
399 mach_msg_option_t option;
400 mach_msg_size_t size;
401
402 /* never called from below */
403 assert(hint == 0);
404
405 /*
406 * called from user context. Have to validate the
407 * name. If it changed, we have an EOF situation.
408 */
409 kr = ipc_object_translate(current_space(), name,
410 MACH_PORT_RIGHT_PORT_SET,
411 (ipc_object_t *)&pset);
412 if (kr != KERN_SUCCESS || pset != kn->kn_ptr.p_pset || !ips_active(pset)) {
413 kn->kn_data = 0;
414 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
316670eb
A
415 if (pset != IPS_NULL) {
416 ips_unlock(pset);
417 }
b0d623f7
A
418 return(1);
419 }
420
421 /* just use the reference from here on out */
422 ips_reference(pset);
423 ips_unlock(pset);
424
425 /*
426 * Only honor supported receive options. If no options are
427 * provided, just force a MACH_RCV_TOO_LARGE to detect the
428 * name of the port and sizeof the waiting message.
429 */
fe8ab488
A
430 option = kn->kn_sfflags & (MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_LARGE_IDENTITY|
431 MACH_RCV_TRAILER_MASK|MACH_RCV_VOUCHER);
b0d623f7
A
432 if (option & MACH_RCV_MSG) {
433 self->ith_msg_addr = (mach_vm_address_t) kn->kn_ext[0];
434 size = (mach_msg_size_t)kn->kn_ext[1];
435 } else {
436 option = MACH_RCV_LARGE;
437 self->ith_msg_addr = 0;
438 size = 0;
439 }
440
441 /*
442 * Set up to receive a message or the notification of a
443 * too large message. But never allow this call to wait.
444 * If the user provided aditional options, like trailer
445 * options, pass those through here. But we don't support
446 * scatter lists through this interface.
447 */
448 self->ith_object = (ipc_object_t)pset;
449 self->ith_msize = size;
450 self->ith_option = option;
b0d623f7
A
451 self->ith_receiver_name = MACH_PORT_NULL;
452 self->ith_continuation = NULL;
453 option |= MACH_RCV_TIMEOUT; // never wait
39236c6e 454 self->ith_state = MACH_RCV_IN_PROGRESS;
b0d623f7
A
455
456 wresult = ipc_mqueue_receive_on_thread(
457 &pset->ips_messages,
458 option,
459 size, /* max_size */
460 0, /* immediate timeout */
461 THREAD_INTERRUPTIBLE,
462 self);
463 assert(wresult == THREAD_NOT_WAITING);
464 assert(self->ith_state != MACH_RCV_IN_PROGRESS);
465
466 /*
467 * If we timed out, just release the reference on the
468 * portset and return zero.
469 */
470 if (self->ith_state == MACH_RCV_TIMED_OUT) {
316670eb 471 ips_release(pset);
b0d623f7
A
472 return 0;
473 }
474
475 /*
476 * If we weren't attempting to receive a message
477 * directly, we need to return the port name in
478 * the kevent structure.
479 */
480 if ((option & MACH_RCV_MSG) != MACH_RCV_MSG) {
481 assert(self->ith_state == MACH_RCV_TOO_LARGE);
482 assert(self->ith_kmsg == IKM_NULL);
483 kn->kn_data = self->ith_receiver_name;
316670eb 484 ips_release(pset);
b0d623f7
A
485 return 1;
486 }
487
488 /*
489 * Attempt to receive the message directly, returning
490 * the results in the fflags field.
491 */
492 assert(option & MACH_RCV_MSG);
b0d623f7 493 kn->kn_ext[1] = self->ith_msize;
39236c6e 494 kn->kn_data = MACH_PORT_NULL;
b0d623f7
A
495 kn->kn_fflags = mach_msg_receive_results();
496 /* kmsg and pset reference consumed */
39236c6e
A
497
498 /*
499 * if the user asked for the identity of ports containing a
500 * a too-large message, return it in the data field (as we
501 * do for messages we didn't try to receive).
502 */
503 if ((kn->kn_fflags == MACH_RCV_TOO_LARGE) &&
504 (option & MACH_RCV_LARGE_IDENTITY))
505 kn->kn_data = self->ith_receiver_name;
506
b0d623f7
A
507 return 1;
508}
509
510static void
3e170ce0 511filt_machporttouch(struct knote *kn, struct kevent_internal_s *kev, long type)
b0d623f7
A
512{
513 switch (type) {
514 case EVENT_REGISTER:
515 kn->kn_sfflags = kev->fflags;
516 kn->kn_sdata = kev->data;
39236c6e
A
517 kn->kn_ext[0] = kev->ext[0];
518 kn->kn_ext[1] = kev->ext[1];
b0d623f7
A
519 break;
520 case EVENT_PROCESS:
521 *kev = kn->kn_kevent;
522 if (kn->kn_flags & EV_CLEAR) {
523 kn->kn_data = 0;
524 kn->kn_fflags = 0;
525 }
526 break;
527 default:
528 panic("filt_machporttouch() - invalid type (%ld)", type);
529 break;
530 }
531}
532
533/*
534 * Peek to see if the portset associated with the knote has any
535 * events. This pre-hook is called when a filter uses the stay-
3e170ce0 536 * on-queue mechanism (as the knote_link_waitq mechanism
b0d623f7
A
537 * does).
538 *
539 * This is called with the kqueue that the knote belongs to still
540 * locked (thus holding a reference on the knote, but restricting
541 * also restricting our ability to take other locks).
542 *
543 * Just peek at the pre-post status of the portset's wait queue
544 * to determine if it has anything interesting. We can do it
545 * without holding the lock, as it is just a snapshot in time
546 * (if this is used as part of really waiting for events, we
547 * will catch changes in this status when the event gets posted
548 * up to the knote's kqueue).
549 */
6d2010ae 550static unsigned
b0d623f7
A
551filt_machportpeek(struct knote *kn)
552{
553 ipc_pset_t pset = kn->kn_ptr.p_pset;
554 ipc_mqueue_t set_mq = &pset->ips_messages;
555
39236c6e 556 return (ipc_mqueue_set_peek(set_mq));
b0d623f7 557}