]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ipc/ipc_pset.c
xnu-2782.10.72.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;
104
105 kr = ipc_object_alloc(space, IOT_PORT_SET,
106 MACH_PORT_TYPE_PORT_SET, 0,
107 &name, (ipc_object_t *) &pset);
108 if (kr != KERN_SUCCESS)
109 return kr;
99c3a104 110 /* pset and space are locked */
1c79356b
A
111
112 pset->ips_local_name = name;
1c79356b 113 ipc_mqueue_init(&pset->ips_messages, TRUE /* set */);
99c3a104 114 is_write_unlock(space);
1c79356b
A
115
116 *namep = name;
117 *psetp = pset;
118 return KERN_SUCCESS;
119}
120
121/*
122 * Routine: ipc_pset_alloc_name
123 * Purpose:
124 * Allocate a port set, with a specific name.
125 * Conditions:
126 * Nothing locked. If successful, the port set is returned
127 * locked. (The caller doesn't have a reference.)
128 * Returns:
129 * KERN_SUCCESS The port set is allocated.
130 * KERN_INVALID_TASK The space is dead.
131 * KERN_NAME_EXISTS The name already denotes a right.
132 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
133 */
134
135kern_return_t
136ipc_pset_alloc_name(
137 ipc_space_t space,
138 mach_port_name_t name,
139 ipc_pset_t *psetp)
140{
141 ipc_pset_t pset;
142 kern_return_t kr;
143
144
145 kr = ipc_object_alloc_name(space, IOT_PORT_SET,
146 MACH_PORT_TYPE_PORT_SET, 0,
147 name, (ipc_object_t *) &pset);
148 if (kr != KERN_SUCCESS)
149 return kr;
150 /* pset is locked */
151
152 pset->ips_local_name = name;
1c79356b 153 ipc_mqueue_init(&pset->ips_messages, TRUE /* set */);
1c79356b
A
154
155 *psetp = pset;
156 return KERN_SUCCESS;
157}
158
159/*
160 * Routine: ipc_pset_member
161 * Purpose:
162 * Checks to see if a port is a member of a pset
163 * Conditions:
164 * Both port and port set are locked.
165 * The port must be active.
166 */
167boolean_t
168ipc_pset_member(
169 ipc_pset_t pset,
170 ipc_port_t port)
171{
172 assert(ip_active(port));
173
174 return (ipc_mqueue_member(&port->ip_messages, &pset->ips_messages));
175}
176
177
178/*
179 * Routine: ipc_pset_add
180 * Purpose:
181 * Puts a port into a port set.
1c79356b
A
182 * Conditions:
183 * Both port and port set are locked and active.
184 * The owner of the port set is also receiver for the port.
185 */
186
187kern_return_t
188ipc_pset_add(
316670eb
A
189 ipc_pset_t pset,
190 ipc_port_t port,
191 wait_queue_link_t wql)
1c79356b 192{
9bccf70c
A
193 kern_return_t kr;
194
1c79356b
A
195 assert(ips_active(pset));
196 assert(ip_active(port));
197
316670eb 198 kr = ipc_mqueue_add(&port->ip_messages, &pset->ips_messages, wql);
1c79356b 199
9bccf70c
A
200 if (kr == KERN_SUCCESS)
201 port->ip_pset_count++;
1c79356b 202
9bccf70c 203 return kr;
1c79356b
A
204}
205
206
207
208/*
209 * Routine: ipc_pset_remove
210 * Purpose:
211 * Removes a port from a port set.
212 * The port set loses a reference.
213 * Conditions:
214 * Both port and port set are locked.
215 * The port must be active.
216 */
217
218kern_return_t
219ipc_pset_remove(
316670eb
A
220 ipc_pset_t pset,
221 ipc_port_t port,
222 wait_queue_link_t *wqlp)
1c79356b 223{
9bccf70c 224 kern_return_t kr;
1c79356b
A
225
226 assert(ip_active(port));
227
228 if (port->ip_pset_count == 0)
229 return KERN_NOT_IN_SET;
230
316670eb 231 kr = ipc_mqueue_remove(&port->ip_messages, &pset->ips_messages, wqlp);
1c79356b 232
9bccf70c 233 if (kr == KERN_SUCCESS)
1c79356b 234 port->ip_pset_count--;
9bccf70c
A
235
236 return kr;
1c79356b
A
237}
238
239/*
9bccf70c 240 * Routine: ipc_pset_remove_from_all
1c79356b
A
241 * Purpose:
242 * Removes a port from all it's port sets.
1c79356b
A
243 * Conditions:
244 * port is locked and active.
245 */
246
247kern_return_t
9bccf70c 248ipc_pset_remove_from_all(
316670eb
A
249 ipc_port_t port,
250 queue_t links)
1c79356b 251{
1c79356b
A
252 assert(ip_active(port));
253
254 if (port->ip_pset_count == 0)
255 return KERN_NOT_IN_SET;
256
257 /*
9bccf70c 258 * Remove the port's mqueue from all sets
1c79356b 259 */
316670eb 260 ipc_mqueue_remove_from_all(&port->ip_messages, links);
9bccf70c 261 port->ip_pset_count = 0;
1c79356b
A
262 return KERN_SUCCESS;
263}
264
265
266/*
267 * Routine: ipc_pset_destroy
268 * Purpose:
269 * Destroys a port_set.
1c79356b
A
270 * Conditions:
271 * The port_set is locked and alive.
272 * The caller has a reference, which is consumed.
273 * Afterwards, the port_set is unlocked and dead.
274 */
275
276void
277ipc_pset_destroy(
278 ipc_pset_t pset)
279{
280 spl_t s;
316670eb
A
281 queue_head_t link_data;
282 queue_t links = &link_data;
283 wait_queue_link_t wql;
284
285 queue_init(links);
1c79356b
A
286
287 assert(ips_active(pset));
288
289 pset->ips_object.io_bits &= ~IO_BITS_ACTIVE;
290
9bccf70c
A
291 /*
292 * remove all the member message queues
293 */
316670eb 294 ipc_mqueue_remove_all(&pset->ips_messages, links);
9bccf70c 295
b0d623f7
A
296 /*
297 * Set all waiters on the portset running to
298 * discover the change.
299 */
1c79356b
A
300 s = splsched();
301 imq_lock(&pset->ips_messages);
302 ipc_mqueue_changed(&pset->ips_messages);
303 imq_unlock(&pset->ips_messages);
304 splx(s);
305
316670eb
A
306 ips_unlock(pset);
307 ips_release(pset); /* consume the ref our caller gave us */
308
309 while(!queue_empty(links)) {
310 wql = (wait_queue_link_t) dequeue(links);
311 wait_queue_link_free(wql);
312 }
313
1c79356b
A
314}
315
b0d623f7
A
316/* Kqueue EVFILT_MACHPORT support */
317
318#include <sys/errno.h>
319
320static int filt_machportattach(struct knote *kn);
321static void filt_machportdetach(struct knote *kn);
322static int filt_machport(struct knote *kn, long hint);
323static void filt_machporttouch(struct knote *kn, struct kevent64_s *kev, long type);
6d2010ae 324static unsigned filt_machportpeek(struct knote *kn);
b0d623f7
A
325struct filterops machport_filtops = {
326 .f_attach = filt_machportattach,
327 .f_detach = filt_machportdetach,
328 .f_event = filt_machport,
329 .f_touch = filt_machporttouch,
330 .f_peek = filt_machportpeek,
331};
332
333static int
334filt_machportattach(
335 struct knote *kn)
336{
337 mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident;
316670eb 338 wait_queue_link_t wql = wait_queue_link_allocate();
b0d623f7
A
339 ipc_pset_t pset = IPS_NULL;
340 int result = ENOSYS;
341 kern_return_t kr;
342
343 kr = ipc_object_translate(current_space(), name,
344 MACH_PORT_RIGHT_PORT_SET,
345 (ipc_object_t *)&pset);
346 if (kr != KERN_SUCCESS) {
316670eb
A
347 wait_queue_link_free(wql);
348 return (kr == KERN_INVALID_NAME ? ENOENT : ENOTSUP);
b0d623f7
A
349 }
350 /* We've got a lock on pset */
351
b0d623f7
A
352 /*
353 * Bind the portset wait queue directly to knote/kqueue.
354 * This allows us to just use wait_queue foo to effect a wakeup,
355 * rather than having to call knote() from the Mach code on each
356 * message.
357 */
316670eb
A
358 result = knote_link_wait_queue(kn, &pset->ips_messages.imq_wait_queue, wql);
359 if (result == 0) {
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);
316670eb 368 wait_queue_link_free(wql);
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;
316670eb 377 wait_queue_link_t wql = WAIT_QUEUE_LINK_NULL;
b0d623f7
A
378
379 /*
380 * Unlink the portset wait queue from knote/kqueue,
381 * and release our reference on the portset.
382 */
383 ips_lock(pset);
316670eb
A
384 (void)knote_unlink_wait_queue(kn, &pset->ips_messages.imq_wait_queue, &wql);
385 kn->kn_ptr.p_pset = IPS_NULL;
386 ips_unlock(pset);
387 ips_release(pset);
388 if (wql != WAIT_QUEUE_LINK_NULL)
389 wait_queue_link_free(wql);
b0d623f7
A
390}
391
392static int
393filt_machport(
394 struct knote *kn,
395 __unused long hint)
396{
397 mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident;
398 ipc_pset_t pset = IPS_NULL;
399 wait_result_t wresult;
400 thread_t self = current_thread();
401 kern_return_t kr;
402 mach_msg_option_t option;
403 mach_msg_size_t size;
404
405 /* never called from below */
406 assert(hint == 0);
407
408 /*
409 * called from user context. Have to validate the
410 * name. If it changed, we have an EOF situation.
411 */
412 kr = ipc_object_translate(current_space(), name,
413 MACH_PORT_RIGHT_PORT_SET,
414 (ipc_object_t *)&pset);
415 if (kr != KERN_SUCCESS || pset != kn->kn_ptr.p_pset || !ips_active(pset)) {
416 kn->kn_data = 0;
417 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
316670eb
A
418 if (pset != IPS_NULL) {
419 ips_unlock(pset);
420 }
b0d623f7
A
421 return(1);
422 }
423
424 /* just use the reference from here on out */
425 ips_reference(pset);
426 ips_unlock(pset);
427
428 /*
429 * Only honor supported receive options. If no options are
430 * provided, just force a MACH_RCV_TOO_LARGE to detect the
431 * name of the port and sizeof the waiting message.
432 */
fe8ab488
A
433 option = kn->kn_sfflags & (MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_LARGE_IDENTITY|
434 MACH_RCV_TRAILER_MASK|MACH_RCV_VOUCHER);
b0d623f7
A
435 if (option & MACH_RCV_MSG) {
436 self->ith_msg_addr = (mach_vm_address_t) kn->kn_ext[0];
437 size = (mach_msg_size_t)kn->kn_ext[1];
438 } else {
439 option = MACH_RCV_LARGE;
440 self->ith_msg_addr = 0;
441 size = 0;
442 }
443
444 /*
445 * Set up to receive a message or the notification of a
446 * too large message. But never allow this call to wait.
447 * If the user provided aditional options, like trailer
448 * options, pass those through here. But we don't support
449 * scatter lists through this interface.
450 */
451 self->ith_object = (ipc_object_t)pset;
452 self->ith_msize = size;
453 self->ith_option = option;
454 self->ith_scatter_list_size = 0;
455 self->ith_receiver_name = MACH_PORT_NULL;
456 self->ith_continuation = NULL;
457 option |= MACH_RCV_TIMEOUT; // never wait
39236c6e 458 self->ith_state = MACH_RCV_IN_PROGRESS;
b0d623f7
A
459
460 wresult = ipc_mqueue_receive_on_thread(
461 &pset->ips_messages,
462 option,
463 size, /* max_size */
464 0, /* immediate timeout */
465 THREAD_INTERRUPTIBLE,
466 self);
467 assert(wresult == THREAD_NOT_WAITING);
468 assert(self->ith_state != MACH_RCV_IN_PROGRESS);
469
470 /*
471 * If we timed out, just release the reference on the
472 * portset and return zero.
473 */
474 if (self->ith_state == MACH_RCV_TIMED_OUT) {
316670eb 475 ips_release(pset);
b0d623f7
A
476 return 0;
477 }
478
479 /*
480 * If we weren't attempting to receive a message
481 * directly, we need to return the port name in
482 * the kevent structure.
483 */
484 if ((option & MACH_RCV_MSG) != MACH_RCV_MSG) {
485 assert(self->ith_state == MACH_RCV_TOO_LARGE);
486 assert(self->ith_kmsg == IKM_NULL);
487 kn->kn_data = self->ith_receiver_name;
316670eb 488 ips_release(pset);
b0d623f7
A
489 return 1;
490 }
491
492 /*
493 * Attempt to receive the message directly, returning
494 * the results in the fflags field.
495 */
496 assert(option & MACH_RCV_MSG);
b0d623f7 497 kn->kn_ext[1] = self->ith_msize;
39236c6e 498 kn->kn_data = MACH_PORT_NULL;
b0d623f7
A
499 kn->kn_fflags = mach_msg_receive_results();
500 /* kmsg and pset reference consumed */
39236c6e
A
501
502 /*
503 * if the user asked for the identity of ports containing a
504 * a too-large message, return it in the data field (as we
505 * do for messages we didn't try to receive).
506 */
507 if ((kn->kn_fflags == MACH_RCV_TOO_LARGE) &&
508 (option & MACH_RCV_LARGE_IDENTITY))
509 kn->kn_data = self->ith_receiver_name;
510
b0d623f7
A
511 return 1;
512}
513
514static void
515filt_machporttouch(struct knote *kn, struct kevent64_s *kev, long type)
516{
517 switch (type) {
518 case EVENT_REGISTER:
519 kn->kn_sfflags = kev->fflags;
520 kn->kn_sdata = kev->data;
39236c6e
A
521 kn->kn_ext[0] = kev->ext[0];
522 kn->kn_ext[1] = kev->ext[1];
b0d623f7
A
523 break;
524 case EVENT_PROCESS:
525 *kev = kn->kn_kevent;
526 if (kn->kn_flags & EV_CLEAR) {
527 kn->kn_data = 0;
528 kn->kn_fflags = 0;
529 }
530 break;
531 default:
532 panic("filt_machporttouch() - invalid type (%ld)", type);
533 break;
534 }
535}
536
537/*
538 * Peek to see if the portset associated with the knote has any
539 * events. This pre-hook is called when a filter uses the stay-
540 * on-queue mechanism (as the knote_link_wait_queue mechanism
541 * does).
542 *
543 * This is called with the kqueue that the knote belongs to still
544 * locked (thus holding a reference on the knote, but restricting
545 * also restricting our ability to take other locks).
546 *
547 * Just peek at the pre-post status of the portset's wait queue
548 * to determine if it has anything interesting. We can do it
549 * without holding the lock, as it is just a snapshot in time
550 * (if this is used as part of really waiting for events, we
551 * will catch changes in this status when the event gets posted
552 * up to the knote's kqueue).
553 */
6d2010ae 554static unsigned
b0d623f7
A
555filt_machportpeek(struct knote *kn)
556{
557 ipc_pset_t pset = kn->kn_ptr.p_pset;
558 ipc_mqueue_t set_mq = &pset->ips_messages;
559
39236c6e 560 return (ipc_mqueue_set_peek(set_mq));
b0d623f7 561}