1/* 2 * Copyright (c) 2000-2004 Apple Computer, 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/* 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 76#include <kern/kern_types.h> 77#include <kern/spl.h> 78 79#include <vm/vm_map.h> 80 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; 110 /* pset and space are locked */ 111 112 pset->ips_local_name = name; 113 ipc_mqueue_init(&pset->ips_messages, TRUE /* set */); 114 is_write_unlock(space); 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; 153 ipc_mqueue_init(&pset->ips_messages, TRUE /* set */); 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. 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 wait_queue_link_t wql) 192{ 193 kern_return_t kr; 194 195 assert(ips_active(pset)); 196 assert(ip_active(port)); 197 198 kr = ipc_mqueue_add(&port->ip_messages, &pset->ips_messages, wql); 199 200 if (kr == KERN_SUCCESS) 201 port->ip_pset_count++; 202 203 return kr; 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( 220 ipc_pset_t pset, 221 ipc_port_t port, 222 wait_queue_link_t *wqlp) 223{ 224 kern_return_t kr; 225 226 assert(ip_active(port)); 227 228 if (port->ip_pset_count == 0) 229 return KERN_NOT_IN_SET; 230 231 kr = ipc_mqueue_remove(&port->ip_messages, &pset->ips_messages, wqlp); 232 233 if (kr == KERN_SUCCESS) 234 port->ip_pset_count--; 235 236 return kr; 237} 238 239/* 240 * Routine: ipc_pset_remove_from_all 241 * Purpose: 242 * Removes a port from all it's port sets. 243 * Conditions: 244 * port is locked and active. 245 */ 246 247kern_return_t 248ipc_pset_remove_from_all( 249 ipc_port_t port, 250 queue_t links) 251{ 252 assert(ip_active(port)); 253 254 if (port->ip_pset_count == 0) 255 return KERN_NOT_IN_SET; 256 257 /* 258 * Remove the port's mqueue from all sets 259 */ 260 ipc_mqueue_remove_from_all(&port->ip_messages, links); 261 port->ip_pset_count = 0; 262 return KERN_SUCCESS; 263} 264 265 266/* 267 * Routine: ipc_pset_destroy 268 * Purpose: 269 * Destroys a port_set. 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; 281 queue_head_t link_data; 282 queue_t links = &link_data; 283 wait_queue_link_t wql; 284 285 queue_init(links); 286 287 assert(ips_active(pset)); 288 289 pset->ips_object.io_bits &= ~IO_BITS_ACTIVE; 290 291 /* 292 * remove all the member message queues 293 */ 294 ipc_mqueue_remove_all(&pset->ips_messages, links); 295 296 /* 297 * Set all waiters on the portset running to 298 * discover the change. 299 */ 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 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 314} 315 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); 324static unsigned filt_machportpeek(struct knote *kn); 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; 338 wait_queue_link_t wql = wait_queue_link_allocate(); 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) { 347 wait_queue_link_free(wql); 348 return (kr == KERN_INVALID_NAME ? ENOENT : ENOTSUP); 349 } 350 /* We've got a lock on pset */ 351 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 */ 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 367 ips_unlock(pset); 368 wait_queue_link_free(wql); 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 wait_queue_link_t wql = WAIT_QUEUE_LINK_NULL; 378 379 /* 380 * Unlink the portset wait queue from knote/kqueue, 381 * and release our reference on the portset. 382 */ 383 ips_lock(pset); 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); 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); 418 if (pset != IPS_NULL) { 419 ips_unlock(pset); 420 } 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 */ 433 option = kn->kn_sfflags & (MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_TRAILER_MASK); 434 if (option & MACH_RCV_MSG) { 435 self->ith_msg_addr = (mach_vm_address_t) kn->kn_ext[0]; 436 size = (mach_msg_size_t)kn->kn_ext[1]; 437 } else { 438 option = MACH_RCV_LARGE; 439 self->ith_msg_addr = 0; 440 size = 0; 441 } 442 443 /* 444 * Set up to receive a message or the notification of a 445 * too large message. But never allow this call to wait. 446 * If the user provided aditional options, like trailer 447 * options, pass those through here. But we don't support 448 * scatter lists through this interface. 449 */ 450 self->ith_object = (ipc_object_t)pset; 451 self->ith_msize = size; 452 self->ith_option = option; 453 self->ith_scatter_list_size = 0; 454 self->ith_receiver_name = MACH_PORT_NULL; 455 self->ith_continuation = NULL; 456 option |= MACH_RCV_TIMEOUT; // never wait 457 assert((self->ith_state = MACH_RCV_IN_PROGRESS) == MACH_RCV_IN_PROGRESS); 458 459 wresult = ipc_mqueue_receive_on_thread( 460 &pset->ips_messages, 461 option, 462 size, /* max_size */ 463 0, /* immediate timeout */ 464 THREAD_INTERRUPTIBLE, 465 self); 466 assert(wresult == THREAD_NOT_WAITING); 467 assert(self->ith_state != MACH_RCV_IN_PROGRESS); 468 469 /* 470 * If we timed out, just release the reference on the 471 * portset and return zero. 472 */ 473 if (self->ith_state == MACH_RCV_TIMED_OUT) { 474 ips_release(pset); 475 return 0; 476 } 477 478 /* 479 * If we weren't attempting to receive a message 480 * directly, we need to return the port name in 481 * the kevent structure. 482 */ 483 if ((option & MACH_RCV_MSG) != MACH_RCV_MSG) { 484 assert(self->ith_state == MACH_RCV_TOO_LARGE); 485 assert(self->ith_kmsg == IKM_NULL); 486 kn->kn_data = self->ith_receiver_name; 487 ips_release(pset); 488 return 1; 489 } 490 491 /* 492 * Attempt to receive the message directly, returning 493 * the results in the fflags field. 494 */ 495 assert(option & MACH_RCV_MSG); 496 kn->kn_data = MACH_PORT_NULL; 497 kn->kn_ext[1] = self->ith_msize; 498 kn->kn_fflags = mach_msg_receive_results(); 499 /* kmsg and pset reference consumed */ 500 return 1; 501} 502 503static void 504filt_machporttouch(struct knote *kn, struct kevent64_s *kev, long type) 505{ 506 switch (type) { 507 case EVENT_REGISTER: 508 kn->kn_sfflags = kev->fflags; 509 kn->kn_sdata = kev->data; 510 break; 511 case EVENT_PROCESS: 512 *kev = kn->kn_kevent; 513 if (kn->kn_flags & EV_CLEAR) { 514 kn->kn_data = 0; 515 kn->kn_fflags = 0; 516 } 517 break; 518 default: 519 panic("filt_machporttouch() - invalid type (%ld)", type); 520 break; 521 } 522} 523 524/* 525 * Peek to see if the portset associated with the knote has any 526 * events. This pre-hook is called when a filter uses the stay- 527 * on-queue mechanism (as the knote_link_wait_queue mechanism 528 * does). 529 * 530 * This is called with the kqueue that the knote belongs to still 531 * locked (thus holding a reference on the knote, but restricting 532 * also restricting our ability to take other locks). 533 * 534 * Just peek at the pre-post status of the portset's wait queue 535 * to determine if it has anything interesting. We can do it 536 * without holding the lock, as it is just a snapshot in time 537 * (if this is used as part of really waiting for events, we 538 * will catch changes in this status when the event gets posted 539 * up to the knote's kqueue). 540 */ 541static unsigned 542filt_machportpeek(struct knote *kn) 543{ 544 ipc_pset_t pset = kn->kn_ptr.p_pset; 545 ipc_mqueue_t set_mq = &pset->ips_messages; 546 547 return (ipc_mqueue_peek(set_mq)); 548} 549