2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
34 * All Rights Reserved.
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.
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.
46 * Carnegie Mellon requests users of this software to return to
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
59 * File: ipc/ipc_entry.c
63 * Primitive functions to manipulate translation entries.
66 #include <mach_debug.h>
68 #include <mach/kern_return.h>
69 #include <mach/port.h>
70 #include <kern/assert.h>
71 #include <kern/sched_prim.h>
72 #include <kern/zalloc.h>
73 #include <kern/misc_protos.h>
75 #include <ipc/ipc_entry.h>
76 #include <ipc/ipc_space.h>
77 #include <ipc/ipc_object.h>
78 #include <ipc/ipc_hash.h>
79 #include <ipc/ipc_table.h>
80 #include <ipc/ipc_port.h>
84 * Routine: ipc_entry_lookup
86 * Searches for an entry, given its name.
88 * The space must be read or write locked throughout.
89 * The space must be active.
95 mach_port_name_t name
)
97 mach_port_index_t index
;
100 assert(is_active(space
));
103 index
= MACH_PORT_INDEX(name
);
104 if (index
< space
->is_table_size
) {
105 entry
= &space
->is_table
[index
];
106 if (IE_BITS_GEN(entry
->ie_bits
) != MACH_PORT_GEN(name
) ||
107 IE_BITS_TYPE(entry
->ie_bits
) == MACH_PORT_TYPE_NONE
)
114 assert((entry
== IE_NULL
) || IE_BITS_TYPE(entry
->ie_bits
));
119 * Routine: ipc_entry_get
121 * Tries to allocate an entry out of the space.
123 * The space is write-locked and active throughout.
124 * An object may be locked. Will not allocate memory.
126 * KERN_SUCCESS A free entry was found.
127 * KERN_NO_SPACE No entry allocated.
133 mach_port_name_t
*namep
,
137 mach_port_index_t first_free
;
138 ipc_entry_t free_entry
;
140 assert(is_active(space
));
143 table
= space
->is_table
;
144 first_free
= table
->ie_next
;
147 return KERN_NO_SPACE
;
149 assert(first_free
< space
->is_table_size
);
150 free_entry
= &table
[first_free
];
151 table
->ie_next
= free_entry
->ie_next
;
155 * Initialize the new entry. We need only
156 * increment the generation number and clear ie_request.
159 mach_port_name_t new_name
;
162 gen
= IE_BITS_NEW_GEN(free_entry
->ie_bits
);
163 free_entry
->ie_bits
= gen
;
164 free_entry
->ie_request
= IE_REQ_NONE
;
167 * The new name can't be MACH_PORT_NULL because index
168 * is non-zero. It can't be MACH_PORT_DEAD because
169 * the table isn't allowed to grow big enough.
170 * (See comment in ipc/ipc_table.h.)
172 new_name
= MACH_PORT_MAKE(first_free
, gen
);
173 assert(MACH_PORT_VALID(new_name
));
177 assert(free_entry
->ie_object
== IO_NULL
);
179 *entryp
= free_entry
;
184 * Routine: ipc_entry_alloc
186 * Allocate an entry out of the space.
188 * The space is not locked before, but it is write-locked after
189 * if the call is successful. May allocate memory.
191 * KERN_SUCCESS An entry was allocated.
192 * KERN_INVALID_TASK The space is dead.
193 * KERN_NO_SPACE No room for an entry in the space.
194 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory for an entry.
200 mach_port_name_t
*namep
,
205 is_write_lock(space
);
208 if (!is_active(space
)) {
209 is_write_unlock(space
);
210 return KERN_INVALID_TASK
;
213 kr
= ipc_entry_get(space
, namep
, entryp
);
214 if (kr
== KERN_SUCCESS
)
217 kr
= ipc_entry_grow_table(space
, ITS_SIZE_NONE
);
218 if (kr
!= KERN_SUCCESS
)
219 return kr
; /* space is unlocked */
224 * Routine: ipc_entry_alloc_name
226 * Allocates/finds an entry with a specific name.
227 * If an existing entry is returned, its type will be nonzero.
229 * The space is not locked before, but it is write-locked after
230 * if the call is successful. May allocate memory.
232 * KERN_SUCCESS Found existing entry with same name.
233 * KERN_SUCCESS Allocated a new entry.
234 * KERN_INVALID_TASK The space is dead.
235 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
236 * KERN_FAILURE Couldn't allocate requested name.
240 ipc_entry_alloc_name(
242 mach_port_name_t name
,
245 mach_port_index_t index
= MACH_PORT_INDEX(name
);
246 mach_port_gen_t gen
= MACH_PORT_GEN(name
);
248 assert(MACH_PORT_VALID(name
));
251 is_write_lock(space
);
256 if (!is_active(space
)) {
257 is_write_unlock(space
);
258 return KERN_INVALID_TASK
;
262 * If we are under the table cutoff,
263 * there are usually four cases:
264 * 1) The entry is reserved (index 0)
265 * 2) The entry is inuse, for the same name
266 * 3) The entry is inuse, for a different name
267 * 4) The entry is free
268 * For a task with a "fast" IPC space, we disallow
269 * cases 1) and 3), because ports cannot be renamed.
271 if (index
< space
->is_table_size
) {
272 ipc_entry_t table
= space
->is_table
;
274 entry
= &table
[index
];
277 /* case #1 - the entry is reserved */
278 assert(!IE_BITS_TYPE(entry
->ie_bits
));
279 assert(!IE_BITS_GEN(entry
->ie_bits
));
280 is_write_unlock(space
);
282 } else if (IE_BITS_TYPE(entry
->ie_bits
)) {
283 if (IE_BITS_GEN(entry
->ie_bits
) == gen
) {
284 /* case #2 -- the entry is inuse, for the same name */
288 /* case #3 -- the entry is inuse, for a different name. */
289 /* Collisions are not allowed */
290 is_write_unlock(space
);
294 mach_port_index_t free_index
, next_index
;
297 * case #4 -- the entry is free
298 * Rip the entry out of the free list.
302 (next_index
= table
[free_index
].ie_next
)
304 free_index
= next_index
)
307 table
[free_index
].ie_next
=
308 table
[next_index
].ie_next
;
310 /* mark the previous entry modified - reconstructing the name */
311 ipc_entry_modified(space
,
312 MACH_PORT_MAKE(free_index
,
313 IE_BITS_GEN(table
[free_index
].ie_bits
)),
316 entry
->ie_bits
= gen
;
317 entry
->ie_request
= IE_REQ_NONE
;
320 assert(entry
->ie_object
== IO_NULL
);
326 * We grow the table so that the name
327 * index fits in the array space.
328 * Because the space will be unlocked,
332 kr
= ipc_entry_grow_table(space
, index
+ 1);
333 assert(kr
!= KERN_NO_SPACE
);
334 if (kr
!= KERN_SUCCESS
) {
335 /* space is unlocked */
343 * Routine: ipc_entry_dealloc
345 * Deallocates an entry from a space.
347 * The space must be write-locked throughout.
348 * The space must be active.
354 mach_port_name_t name
,
358 ipc_entry_num_t size
;
359 mach_port_index_t index
;
361 assert(is_active(space
));
362 assert(entry
->ie_object
== IO_NULL
);
363 assert(entry
->ie_request
== IE_REQ_NONE
);
366 if (entry
->ie_request
!= IE_REQ_NONE
)
367 panic("ipc_entry_dealloc()\n");
370 index
= MACH_PORT_INDEX(name
);
371 table
= space
->is_table
;
372 size
= space
->is_table_size
;
374 if ((index
< size
) && (entry
== &table
[index
])) {
375 assert(IE_BITS_GEN(entry
->ie_bits
) == MACH_PORT_GEN(name
));
376 entry
->ie_bits
&= IE_BITS_GEN_MASK
;
377 entry
->ie_next
= table
->ie_next
;
378 table
->ie_next
= index
;
381 * Nothing to do. The entry does not match
382 * so there is nothing to deallocate.
384 assert(index
< size
);
385 assert(entry
== &table
[index
]);
386 assert(IE_BITS_GEN(entry
->ie_bits
) == MACH_PORT_GEN(name
));
388 ipc_entry_modified(space
, name
, entry
);
392 * Routine: ipc_entry_modified
394 * Note that an entry was modified in a space.
396 * Assumes exclusive write access to the space,
397 * either through a write lock or being the cleaner
398 * on an inactive space.
404 mach_port_name_t name
,
405 __assert_only ipc_entry_t entry
)
408 ipc_entry_num_t size
;
409 mach_port_index_t index
;
411 index
= MACH_PORT_INDEX(name
);
412 table
= space
->is_table
;
413 size
= space
->is_table_size
;
415 assert(index
< size
);
416 assert(entry
== &table
[index
]);
418 assert(space
->is_low_mod
<= size
);
419 assert(space
->is_high_mod
< size
);
421 if (index
< space
->is_low_mod
)
422 space
->is_low_mod
= index
;
423 if (index
> space
->is_high_mod
)
424 space
->is_high_mod
= index
;
427 #define IPC_ENTRY_GROW_STATS 1
428 #if IPC_ENTRY_GROW_STATS
429 static uint64_t ipc_entry_grow_count
= 0;
430 static uint64_t ipc_entry_grow_rescan
= 0;
431 static uint64_t ipc_entry_grow_rescan_max
= 0;
432 static uint64_t ipc_entry_grow_rescan_entries
= 0;
433 static uint64_t ipc_entry_grow_rescan_entries_max
= 0;
434 static uint64_t ipc_entry_grow_freelist_entries
= 0;
435 static uint64_t ipc_entry_grow_freelist_entries_max
= 0;
439 * Routine: ipc_entry_grow_table
441 * Grows the table in a space.
443 * The space must be write-locked and active before.
444 * If successful, the space is also returned locked.
445 * On failure, the space is returned unlocked.
448 * KERN_SUCCESS Grew the table.
449 * KERN_SUCCESS Somebody else grew the table.
450 * KERN_SUCCESS The space died.
451 * KERN_NO_SPACE Table has maximum size already.
452 * KERN_RESOURCE_SHORTAGE Couldn't allocate a new table.
456 ipc_entry_grow_table(
458 ipc_table_elems_t target_size
)
460 ipc_entry_num_t osize
, size
, nsize
, psize
;
462 ipc_entry_t otable
, table
;
463 ipc_table_size_t oits
, its
, nits
;
464 mach_port_index_t i
, free_index
;
465 mach_port_index_t low_mod
, hi_mod
;
466 ipc_table_index_t sanity
;
467 #if IPC_ENTRY_GROW_STATS
468 uint64_t rescan_count
= 0;
470 assert(is_active(space
));
472 if (is_growing(space
)) {
474 * Somebody else is growing the table.
475 * We just wait for them to finish.
478 is_write_sleep(space
);
482 otable
= space
->is_table
;
484 its
= space
->is_table_next
;
485 size
= its
->its_size
;
488 * Since is_table_next points to the next natural size
489 * we can identify the current size entry.
492 osize
= oits
->its_size
;
495 * If there is no target size, then the new size is simply
496 * specified by is_table_next. If there is a target
497 * size, then search for the next entry.
499 if (target_size
!= ITS_SIZE_NONE
) {
500 if (target_size
<= osize
) {
501 /* the space is locked */
506 while ((psize
!= size
) && (target_size
> size
)) {
509 size
= its
->its_size
;
512 is_write_unlock(space
);
513 return KERN_NO_SPACE
;
518 is_write_unlock(space
);
519 return KERN_NO_SPACE
;
523 nsize
= nits
->its_size
;
524 assert((osize
< size
) && (size
<= nsize
));
527 * We'll attempt to grow the table.
529 * Because we will be copying without the space lock, reset
530 * the lowest_mod index to just beyond the end of the current
531 * table. Modification of entries (other than hashes) will
532 * bump this downward, and we only have to reprocess entries
533 * above that mark. Eventually, we'll get done.
535 is_start_growing(space
);
536 space
->is_low_mod
= osize
;
537 space
->is_high_mod
= 0;
538 #if IPC_ENTRY_GROW_STATS
539 ipc_entry_grow_count
++;
541 is_write_unlock(space
);
543 table
= it_entries_alloc(its
);
544 if (table
== IE_NULL
) {
545 is_write_lock(space
);
546 is_done_growing(space
);
547 is_write_unlock(space
);
548 thread_wakeup((event_t
) space
);
549 return KERN_RESOURCE_SHORTAGE
;
552 /* initialize new entries (free chain in backwards order) */
553 for (i
= osize
; i
< size
; i
++) {
554 table
[i
].ie_object
= IO_NULL
;
555 table
[i
].ie_bits
= IE_BITS_GEN_MASK
;
556 table
[i
].ie_index
= 0;
557 table
[i
].ie_next
= i
+ 1;
559 table
[size
-1].ie_next
= 0;
561 /* clear out old entries in new table */
562 memset((void *)table
, 0, osize
* sizeof(*table
));
568 * Within the range of the table that changed, determine what we
569 * have to take action on. For each entry, take a snapshot of the
570 * corresponding entry in the old table (so it won't change
571 * during this iteration). The snapshot may not be self-consistent
572 * (if we caught it in the middle of being changed), so be very
573 * cautious with the values.
575 for (i
= low_mod
; i
<= hi_mod
; i
++) {
576 ipc_entry_t entry
= &table
[i
];
577 struct ipc_entry osnap
= otable
[i
];
579 if (entry
->ie_object
!= osnap
.ie_object
||
580 IE_BITS_TYPE(entry
->ie_bits
) != IE_BITS_TYPE(osnap
.ie_bits
)) {
582 if (entry
->ie_object
!= IO_NULL
&&
583 IE_BITS_TYPE(entry
->ie_bits
) == MACH_PORT_TYPE_SEND
)
584 ipc_hash_table_delete(table
, size
, entry
->ie_object
, i
, entry
);
586 entry
->ie_object
= osnap
.ie_object
;
587 entry
->ie_bits
= osnap
.ie_bits
;
588 entry
->ie_request
= osnap
.ie_request
; /* or ie_next */
590 if (entry
->ie_object
!= IO_NULL
&&
591 IE_BITS_TYPE(entry
->ie_bits
) == MACH_PORT_TYPE_SEND
)
592 ipc_hash_table_insert(table
, size
, entry
->ie_object
, i
, entry
);
594 assert(entry
->ie_object
== osnap
.ie_object
);
595 entry
->ie_bits
= osnap
.ie_bits
;
596 entry
->ie_request
= osnap
.ie_request
; /* or ie_next */
600 table
[0].ie_next
= otable
[0].ie_next
; /* always rebase the freelist */
603 * find the end of the freelist (should be short). But be careful,
604 * the list items can change so only follow through truly free entries
605 * (no problem stopping short in those cases, because we'll rescan).
608 for (sanity
= 0; sanity
< osize
; sanity
++) {
609 if (table
[free_index
].ie_object
!= IPC_OBJECT_NULL
)
611 i
= table
[free_index
].ie_next
;
612 if (i
== 0 || i
>= osize
)
616 #if IPC_ENTRY_GROW_STATS
617 ipc_entry_grow_freelist_entries
+= sanity
;
618 if (sanity
> ipc_entry_grow_freelist_entries_max
)
619 ipc_entry_grow_freelist_entries_max
= sanity
;
622 is_write_lock(space
);
625 * We need to do a wakeup on the space,
626 * to rouse waiting threads. We defer
627 * this until the space is unlocked,
628 * because we don't want them to spin.
631 if (!is_active(space
)) {
633 * The space died while it was unlocked.
636 is_done_growing(space
);
637 is_write_unlock(space
);
638 thread_wakeup((event_t
) space
);
639 it_entries_free(its
, table
);
640 is_write_lock(space
);
644 /* If the space changed while unlocked, go back and process the changes */
645 if (space
->is_low_mod
< osize
) {
646 assert(space
->is_high_mod
> 0);
647 low_mod
= space
->is_low_mod
;
648 space
->is_low_mod
= osize
;
649 hi_mod
= space
->is_high_mod
;
650 space
->is_high_mod
= 0;
651 is_write_unlock(space
);
652 #if IPC_ENTRY_GROW_STATS
654 if (rescan_count
> ipc_entry_grow_rescan_max
)
655 ipc_entry_grow_rescan_max
= rescan_count
;
657 ipc_entry_grow_rescan
++;
658 ipc_entry_grow_rescan_entries
+= hi_mod
- low_mod
+ 1;
659 if (hi_mod
- low_mod
+ 1 > ipc_entry_grow_rescan_entries_max
)
660 ipc_entry_grow_rescan_entries_max
= hi_mod
- low_mod
+ 1;
665 /* link new free entries onto the rest of the freelist */
666 assert(table
[free_index
].ie_next
== 0 &&
667 table
[free_index
].ie_object
== IO_NULL
);
668 table
[free_index
].ie_next
= osize
;
670 assert(space
->is_table
== otable
);
671 assert((space
->is_table_next
== its
) ||
672 (target_size
!= ITS_SIZE_NONE
));
673 assert(space
->is_table_size
== osize
);
675 space
->is_table
= table
;
676 space
->is_table_size
= size
;
677 space
->is_table_next
= nits
;
679 is_done_growing(space
);
680 is_write_unlock(space
);
682 thread_wakeup((event_t
) space
);
685 * Now we need to free the old table.
687 it_entries_free(oits
, otable
);
688 is_write_lock(space
);