]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ipc/ipc_pset.c
xnu-1504.9.17.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>
75#include <ipc/ipc_print.h>
1c79356b 76
91447636
A
77#include <kern/kern_types.h>
78#include <kern/spl.h>
b0d623f7
A
79
80#include <vm/vm_map.h>
81
1c79356b
A
82/*
83 * Routine: ipc_pset_alloc
84 * Purpose:
85 * Allocate a port set.
86 * Conditions:
87 * Nothing locked. If successful, the port set is returned
88 * locked. (The caller doesn't have a reference.)
89 * Returns:
90 * KERN_SUCCESS The port set is allocated.
91 * KERN_INVALID_TASK The space is dead.
92 * KERN_NO_SPACE No room for an entry in the space.
93 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
94 */
95
96kern_return_t
97ipc_pset_alloc(
98 ipc_space_t space,
99 mach_port_name_t *namep,
100 ipc_pset_t *psetp)
101{
102 ipc_pset_t pset;
103 mach_port_name_t name;
104 kern_return_t kr;
105
106 kr = ipc_object_alloc(space, IOT_PORT_SET,
107 MACH_PORT_TYPE_PORT_SET, 0,
108 &name, (ipc_object_t *) &pset);
109 if (kr != KERN_SUCCESS)
110 return kr;
111 /* pset is locked */
112
113 pset->ips_local_name = name;
1c79356b 114 ipc_mqueue_init(&pset->ips_messages, TRUE /* set */);
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(
189 ipc_pset_t pset,
190 ipc_port_t port)
191{
9bccf70c
A
192 kern_return_t kr;
193
1c79356b
A
194 assert(ips_active(pset));
195 assert(ip_active(port));
196
9bccf70c 197 kr = ipc_mqueue_add(&port->ip_messages, &pset->ips_messages);
1c79356b 198
9bccf70c
A
199 if (kr == KERN_SUCCESS)
200 port->ip_pset_count++;
1c79356b 201
9bccf70c 202 return kr;
1c79356b
A
203}
204
205
206
207/*
208 * Routine: ipc_pset_remove
209 * Purpose:
210 * Removes a port from a port set.
211 * The port set loses a reference.
212 * Conditions:
213 * Both port and port set are locked.
214 * The port must be active.
215 */
216
217kern_return_t
218ipc_pset_remove(
219 ipc_pset_t pset,
220 ipc_port_t port)
221{
9bccf70c 222 kern_return_t kr;
1c79356b
A
223
224 assert(ip_active(port));
225
226 if (port->ip_pset_count == 0)
227 return KERN_NOT_IN_SET;
228
9bccf70c 229 kr = ipc_mqueue_remove(&port->ip_messages, &pset->ips_messages);
1c79356b 230
9bccf70c 231 if (kr == KERN_SUCCESS)
1c79356b 232 port->ip_pset_count--;
9bccf70c
A
233
234 return kr;
1c79356b
A
235}
236
237/*
9bccf70c 238 * Routine: ipc_pset_remove_from_all
1c79356b
A
239 * Purpose:
240 * Removes a port from all it's port sets.
1c79356b
A
241 * Conditions:
242 * port is locked and active.
243 */
244
245kern_return_t
9bccf70c 246ipc_pset_remove_from_all(
1c79356b
A
247 ipc_port_t port)
248{
1c79356b
A
249 assert(ip_active(port));
250
251 if (port->ip_pset_count == 0)
252 return KERN_NOT_IN_SET;
253
254 /*
9bccf70c 255 * Remove the port's mqueue from all sets
1c79356b 256 */
9bccf70c
A
257 ipc_mqueue_remove_from_all(&port->ip_messages);
258 port->ip_pset_count = 0;
1c79356b
A
259 return KERN_SUCCESS;
260}
261
262
263/*
264 * Routine: ipc_pset_destroy
265 * Purpose:
266 * Destroys a port_set.
1c79356b
A
267 * Conditions:
268 * The port_set is locked and alive.
269 * The caller has a reference, which is consumed.
270 * Afterwards, the port_set is unlocked and dead.
271 */
272
273void
274ipc_pset_destroy(
275 ipc_pset_t pset)
276{
277 spl_t s;
278
279 assert(ips_active(pset));
280
281 pset->ips_object.io_bits &= ~IO_BITS_ACTIVE;
282
9bccf70c
A
283 /*
284 * remove all the member message queues
285 */
286 ipc_mqueue_remove_all(&pset->ips_messages);
287
b0d623f7
A
288 /*
289 * Set all waiters on the portset running to
290 * discover the change.
291 */
1c79356b
A
292 s = splsched();
293 imq_lock(&pset->ips_messages);
294 ipc_mqueue_changed(&pset->ips_messages);
295 imq_unlock(&pset->ips_messages);
296 splx(s);
297
1c79356b
A
298 ips_release(pset); /* consume the ref our caller gave us */
299 ips_check_unlock(pset);
300}
301
b0d623f7
A
302/* Kqueue EVFILT_MACHPORT support */
303
304#include <sys/errno.h>
305
306static int filt_machportattach(struct knote *kn);
307static void filt_machportdetach(struct knote *kn);
308static int filt_machport(struct knote *kn, long hint);
309static void filt_machporttouch(struct knote *kn, struct kevent64_s *kev, long type);
310static int filt_machportpeek(struct knote *kn);
311struct filterops machport_filtops = {
312 .f_attach = filt_machportattach,
313 .f_detach = filt_machportdetach,
314 .f_event = filt_machport,
315 .f_touch = filt_machporttouch,
316 .f_peek = filt_machportpeek,
317};
318
319static int
320filt_machportattach(
321 struct knote *kn)
322{
323 mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident;
324 ipc_pset_t pset = IPS_NULL;
325 int result = ENOSYS;
326 kern_return_t kr;
327
328 kr = ipc_object_translate(current_space(), name,
329 MACH_PORT_RIGHT_PORT_SET,
330 (ipc_object_t *)&pset);
331 if (kr != KERN_SUCCESS) {
332 result = (kr == KERN_INVALID_NAME ? ENOENT : ENOTSUP);
333 goto done;
334 }
335 /* We've got a lock on pset */
336
337 /* keep a reference for the knote */
338 kn->kn_ptr.p_pset = pset;
339 ips_reference(pset);
340
341 /*
342 * Bind the portset wait queue directly to knote/kqueue.
343 * This allows us to just use wait_queue foo to effect a wakeup,
344 * rather than having to call knote() from the Mach code on each
345 * message.
346 */
347 result = knote_link_wait_queue(kn, &pset->ips_messages.imq_wait_queue);
348 ips_unlock(pset);
349done:
350 return result;
351}
352
353static void
354filt_machportdetach(
355 struct knote *kn)
356{
357 ipc_pset_t pset = kn->kn_ptr.p_pset;
358
359 /*
360 * Unlink the portset wait queue from knote/kqueue,
361 * and release our reference on the portset.
362 */
363 ips_lock(pset);
364 knote_unlink_wait_queue(kn, &pset->ips_messages.imq_wait_queue);
365 ips_release(kn->kn_ptr.p_pset);
366 kn->kn_ptr.p_pset = IPS_NULL;
367 ips_check_unlock(pset);
368}
369
370static int
371filt_machport(
372 struct knote *kn,
373 __unused long hint)
374{
375 mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident;
376 ipc_pset_t pset = IPS_NULL;
377 wait_result_t wresult;
378 thread_t self = current_thread();
379 kern_return_t kr;
380 mach_msg_option_t option;
381 mach_msg_size_t size;
382
383 /* never called from below */
384 assert(hint == 0);
385
386 /*
387 * called from user context. Have to validate the
388 * name. If it changed, we have an EOF situation.
389 */
390 kr = ipc_object_translate(current_space(), name,
391 MACH_PORT_RIGHT_PORT_SET,
392 (ipc_object_t *)&pset);
393 if (kr != KERN_SUCCESS || pset != kn->kn_ptr.p_pset || !ips_active(pset)) {
394 kn->kn_data = 0;
395 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
396 if (pset != IPS_NULL)
397 ips_check_unlock(pset);
398 return(1);
399 }
400
401 /* just use the reference from here on out */
402 ips_reference(pset);
403 ips_unlock(pset);
404
405 /*
406 * Only honor supported receive options. If no options are
407 * provided, just force a MACH_RCV_TOO_LARGE to detect the
408 * name of the port and sizeof the waiting message.
409 */
410 option = kn->kn_sfflags & (MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_TRAILER_MASK);
411 if (option & MACH_RCV_MSG) {
412 self->ith_msg_addr = (mach_vm_address_t) kn->kn_ext[0];
413 size = (mach_msg_size_t)kn->kn_ext[1];
414 } else {
415 option = MACH_RCV_LARGE;
416 self->ith_msg_addr = 0;
417 size = 0;
418 }
419
420 /*
421 * Set up to receive a message or the notification of a
422 * too large message. But never allow this call to wait.
423 * If the user provided aditional options, like trailer
424 * options, pass those through here. But we don't support
425 * scatter lists through this interface.
426 */
427 self->ith_object = (ipc_object_t)pset;
428 self->ith_msize = size;
429 self->ith_option = option;
430 self->ith_scatter_list_size = 0;
431 self->ith_receiver_name = MACH_PORT_NULL;
432 self->ith_continuation = NULL;
433 option |= MACH_RCV_TIMEOUT; // never wait
434 assert((self->ith_state = MACH_RCV_IN_PROGRESS) == MACH_RCV_IN_PROGRESS);
435
436 wresult = ipc_mqueue_receive_on_thread(
437 &pset->ips_messages,
438 option,
439 size, /* max_size */
440 0, /* immediate timeout */
441 THREAD_INTERRUPTIBLE,
442 self);
443 assert(wresult == THREAD_NOT_WAITING);
444 assert(self->ith_state != MACH_RCV_IN_PROGRESS);
445
446 /*
447 * If we timed out, just release the reference on the
448 * portset and return zero.
449 */
450 if (self->ith_state == MACH_RCV_TIMED_OUT) {
451 ipc_pset_release(pset);
452 return 0;
453 }
454
455 /*
456 * If we weren't attempting to receive a message
457 * directly, we need to return the port name in
458 * the kevent structure.
459 */
460 if ((option & MACH_RCV_MSG) != MACH_RCV_MSG) {
461 assert(self->ith_state == MACH_RCV_TOO_LARGE);
462 assert(self->ith_kmsg == IKM_NULL);
463 kn->kn_data = self->ith_receiver_name;
464 ipc_pset_release(pset);
465 return 1;
466 }
467
468 /*
469 * Attempt to receive the message directly, returning
470 * the results in the fflags field.
471 */
472 assert(option & MACH_RCV_MSG);
473 kn->kn_data = MACH_PORT_NULL;
474 kn->kn_ext[1] = self->ith_msize;
475 kn->kn_fflags = mach_msg_receive_results();
476 /* kmsg and pset reference consumed */
477 return 1;
478}
479
480static void
481filt_machporttouch(struct knote *kn, struct kevent64_s *kev, long type)
482{
483 switch (type) {
484 case EVENT_REGISTER:
485 kn->kn_sfflags = kev->fflags;
486 kn->kn_sdata = kev->data;
487 break;
488 case EVENT_PROCESS:
489 *kev = kn->kn_kevent;
490 if (kn->kn_flags & EV_CLEAR) {
491 kn->kn_data = 0;
492 kn->kn_fflags = 0;
493 }
494 break;
495 default:
496 panic("filt_machporttouch() - invalid type (%ld)", type);
497 break;
498 }
499}
500
501/*
502 * Peek to see if the portset associated with the knote has any
503 * events. This pre-hook is called when a filter uses the stay-
504 * on-queue mechanism (as the knote_link_wait_queue mechanism
505 * does).
506 *
507 * This is called with the kqueue that the knote belongs to still
508 * locked (thus holding a reference on the knote, but restricting
509 * also restricting our ability to take other locks).
510 *
511 * Just peek at the pre-post status of the portset's wait queue
512 * to determine if it has anything interesting. We can do it
513 * without holding the lock, as it is just a snapshot in time
514 * (if this is used as part of really waiting for events, we
515 * will catch changes in this status when the event gets posted
516 * up to the knote's kqueue).
517 */
518static int
519filt_machportpeek(struct knote *kn)
520{
521 ipc_pset_t pset = kn->kn_ptr.p_pset;
522 ipc_mqueue_t set_mq = &pset->ips_messages;
523
524 return (ipc_mqueue_peek(set_mq));
525}
526
527
1c79356b
A
528#include <mach_kdb.h>
529#if MACH_KDB
530
531#include <ddb/db_output.h>
532
533#define printf kdbprintf
534
535int
536ipc_list_count(
537 struct ipc_kmsg *base)
538{
539 register int count = 0;
540
541 if (base) {
542 struct ipc_kmsg *kmsg = base;
543
544 ++count;
545 while (kmsg && kmsg->ikm_next != base
546 && kmsg->ikm_next != IKM_BOGUS){
547 kmsg = kmsg->ikm_next;
548 ++count;
549 }
550 }
551 return(count);
552}
553
554/*
555 * Routine: ipc_pset_print
556 * Purpose:
557 * Pretty-print a port set for kdb.
558 */
1c79356b
A
559void
560ipc_pset_print(
561 ipc_pset_t pset)
562{
1c79356b
A
563 printf("pset 0x%x\n", pset);
564
565 db_indent += 2;
566
567 ipc_object_print(&pset->ips_object);
568 iprintf("local_name = 0x%x\n", pset->ips_local_name);
569 iprintf("%d kmsgs => 0x%x",
570 ipc_list_count(pset->ips_messages.imq_messages.ikmq_base),
571 pset->ips_messages.imq_messages.ikmq_base);
572 printf(",rcvrs queue= 0x%x\n", &pset->ips_messages.imq_wait_queue);
573
574 db_indent -=2;
575}
576
577#endif /* MACH_KDB */