]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ipc/ipc_entry.c
xnu-6153.101.6.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_entry.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@
0a7de745 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.
0a7de745 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.
0a7de745 17 *
2d21ac55
A
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.
0a7de745 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
0a7de745 31/*
1c79356b
A
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
34 * All Rights Reserved.
0a7de745 35 *
1c79356b
A
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.
0a7de745 41 *
1c79356b
A
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.
0a7de745 45 *
1c79356b 46 * Carnegie Mellon requests users of this software to return to
0a7de745 47 *
1c79356b
A
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
0a7de745 52 *
1c79356b
A
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_entry.c
60 * Author: Rich Draves
61 * Date: 1989
62 *
63 * Primitive functions to manipulate translation entries.
64 */
65
1c79356b
A
66#include <mach_debug.h>
67
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>
1c79356b
A
74#include <ipc/port.h>
75#include <ipc/ipc_entry.h>
76#include <ipc/ipc_space.h>
1c79356b
A
77#include <ipc/ipc_object.h>
78#include <ipc/ipc_hash.h>
79#include <ipc/ipc_table.h>
80#include <ipc/ipc_port.h>
81#include <string.h>
d9a64523 82#include <sys/kdebug.h>
1c79356b 83
1c79356b
A
84/*
85 * Routine: ipc_entry_lookup
86 * Purpose:
87 * Searches for an entry, given its name.
88 * Conditions:
89 * The space must be read or write locked throughout.
90 * The space must be active.
91 */
92
93ipc_entry_t
94ipc_entry_lookup(
0a7de745
A
95 ipc_space_t space,
96 mach_port_name_t name)
1c79356b
A
97{
98 mach_port_index_t index;
99 ipc_entry_t entry;
100
316670eb 101 assert(is_active(space));
1c79356b 102
1c79356b 103 index = MACH_PORT_INDEX(name);
0a7de745
A
104 if (index < space->is_table_size) {
105 entry = &space->is_table[index];
1c79356b 106 if (IE_BITS_GEN(entry->ie_bits) != MACH_PORT_GEN(name) ||
5c9f4661 107 IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE) {
0a7de745 108 entry = IE_NULL;
5c9f4661 109 }
0a7de745 110 } else {
316670eb 111 entry = IE_NULL;
1c79356b
A
112 }
113
114 assert((entry == IE_NULL) || IE_BITS_TYPE(entry->ie_bits));
115 return entry;
116}
117
fe8ab488 118
1c79356b 119/*
fe8ab488 120 * Routine: ipc_entries_hold
1c79356b 121 * Purpose:
fe8ab488
A
122 * Verifies that there are at least 'entries_needed'
123 * free list members
1c79356b
A
124 * Conditions:
125 * The space is write-locked and active throughout.
126 * An object may be locked. Will not allocate memory.
127 * Returns:
fe8ab488 128 * KERN_SUCCESS Free entries were found.
1c79356b
A
129 * KERN_NO_SPACE No entry allocated.
130 */
131
132kern_return_t
fe8ab488 133ipc_entries_hold(
0a7de745
A
134 ipc_space_t space,
135 uint32_t entries_needed)
fe8ab488 136{
fe8ab488
A
137 ipc_entry_t table;
138 mach_port_index_t next_free = 0;
139 uint32_t i;
140
0a7de745
A
141 /*
142 * Assume that all new entries will need hashing.
143 * If the table is more than 87.5% full pretend we didn't have space.
144 */
145 if (space->is_table_hashed + entries_needed >
146 space->is_table_size * 7 / 8) {
147 return KERN_NO_SPACE;
148 }
149
fe8ab488
A
150 assert(is_active(space));
151
152 table = &space->is_table[0];
153
154 for (i = 0; i < entries_needed; i++) {
155 next_free = table[next_free].ie_next;
156 if (next_free == 0) {
157 return KERN_NO_SPACE;
158 }
159 assert(next_free < space->is_table_size);
160 assert(table[next_free].ie_object == IO_NULL);
161 }
162 return KERN_SUCCESS;
163}
164
165/*
166 * Routine: ipc_entry_claim
167 * Purpose:
168 * Take formal ownership of a held entry.
169 * Conditions:
170 * The space is write-locked and active throughout.
171 * An object may be locked. Will not allocate memory.
172 *
0a7de745
A
173 * Note: The returned entry must be marked as modified before
174 * releasing the space lock
fe8ab488
A
175 */
176
177kern_return_t
178ipc_entry_claim(
0a7de745
A
179 ipc_space_t space,
180 mach_port_name_t *namep,
181 ipc_entry_t *entryp)
1c79356b 182{
fe8ab488 183 ipc_entry_t entry;
1c79356b
A
184 ipc_entry_t table;
185 mach_port_index_t first_free;
fe8ab488
A
186 mach_port_gen_t gen;
187 mach_port_name_t new_name;
1c79356b 188
fe8ab488 189 table = &space->is_table[0];
1c79356b 190
fe8ab488
A
191 first_free = table->ie_next;
192 assert(first_free != 0);
1c79356b 193
fe8ab488
A
194 entry = &table[first_free];
195 table->ie_next = entry->ie_next;
196 space->is_table_free--;
1c79356b 197
fe8ab488 198 assert(table->ie_next < space->is_table_size);
1c79356b
A
199
200 /*
5c9f4661
A
201 * Initialize the new entry: increment gencount and reset
202 * rollover point if it rolled over, and clear ie_request.
1c79356b 203 */
5c9f4661
A
204 gen = ipc_entry_new_gen(entry->ie_bits);
205 if (__improbable(ipc_entry_gen_rolled(entry->ie_bits, gen))) {
206 ipc_entry_bits_t roll = ipc_space_get_rollpoint(space);
207 gen = ipc_entry_new_rollpoint(roll);
208 }
fe8ab488
A
209 entry->ie_bits = gen;
210 entry->ie_request = IE_REQ_NONE;
1c79356b 211
fe8ab488
A
212 /*
213 * The new name can't be MACH_PORT_NULL because index
214 * is non-zero. It can't be MACH_PORT_DEAD because
215 * the table isn't allowed to grow big enough.
216 * (See comment in ipc/ipc_table.h.)
217 */
218 new_name = MACH_PORT_MAKE(first_free, gen);
219 assert(MACH_PORT_VALID(new_name));
220 *namep = new_name;
221 *entryp = entry;
1c79356b 222
fe8ab488
A
223 return KERN_SUCCESS;
224}
1c79356b 225
fe8ab488
A
226/*
227 * Routine: ipc_entry_get
228 * Purpose:
229 * Tries to allocate an entry out of the space.
230 * Conditions:
231 * The space is write-locked and active throughout.
232 * An object may be locked. Will not allocate memory.
233 * Returns:
234 * KERN_SUCCESS A free entry was found.
235 * KERN_NO_SPACE No entry allocated.
236 */
1c79356b 237
fe8ab488
A
238kern_return_t
239ipc_entry_get(
0a7de745
A
240 ipc_space_t space,
241 mach_port_name_t *namep,
242 ipc_entry_t *entryp)
fe8ab488
A
243{
244 kern_return_t kr;
245
246 kr = ipc_entries_hold(space, 1);
0a7de745 247 if (KERN_SUCCESS != kr) {
fe8ab488 248 return kr;
0a7de745 249 }
fe8ab488
A
250
251 return ipc_entry_claim(space, namep, entryp);
1c79356b
A
252}
253
254/*
255 * Routine: ipc_entry_alloc
256 * Purpose:
257 * Allocate an entry out of the space.
258 * Conditions:
259 * The space is not locked before, but it is write-locked after
260 * if the call is successful. May allocate memory.
261 * Returns:
262 * KERN_SUCCESS An entry was allocated.
263 * KERN_INVALID_TASK The space is dead.
264 * KERN_NO_SPACE No room for an entry in the space.
265 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory for an entry.
266 */
267
268kern_return_t
269ipc_entry_alloc(
0a7de745
A
270 ipc_space_t space,
271 mach_port_name_t *namep,
272 ipc_entry_t *entryp)
1c79356b
A
273{
274 kern_return_t kr;
275
276 is_write_lock(space);
277
278 for (;;) {
316670eb 279 if (!is_active(space)) {
1c79356b
A
280 is_write_unlock(space);
281 return KERN_INVALID_TASK;
282 }
283
284 kr = ipc_entry_get(space, namep, entryp);
0a7de745 285 if (kr == KERN_SUCCESS) {
1c79356b 286 return kr;
0a7de745 287 }
1c79356b
A
288
289 kr = ipc_entry_grow_table(space, ITS_SIZE_NONE);
0a7de745 290 if (kr != KERN_SUCCESS) {
1c79356b 291 return kr; /* space is unlocked */
0a7de745 292 }
1c79356b
A
293 }
294}
295
296/*
297 * Routine: ipc_entry_alloc_name
298 * Purpose:
299 * Allocates/finds an entry with a specific name.
300 * If an existing entry is returned, its type will be nonzero.
301 * Conditions:
302 * The space is not locked before, but it is write-locked after
303 * if the call is successful. May allocate memory.
304 * Returns:
305 * KERN_SUCCESS Found existing entry with same name.
306 * KERN_SUCCESS Allocated a new entry.
307 * KERN_INVALID_TASK The space is dead.
308 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
316670eb 309 * KERN_FAILURE Couldn't allocate requested name.
1c79356b
A
310 */
311
312kern_return_t
313ipc_entry_alloc_name(
0a7de745
A
314 ipc_space_t space,
315 mach_port_name_t name,
316 ipc_entry_t *entryp)
1c79356b
A
317{
318 mach_port_index_t index = MACH_PORT_INDEX(name);
319 mach_port_gen_t gen = MACH_PORT_GEN(name);
1c79356b 320
0a7de745 321 if (index > ipc_table_max_entries()) {
cc8bc92a 322 return KERN_NO_SPACE;
0a7de745 323 }
cc8bc92a 324
1c79356b
A
325 assert(MACH_PORT_VALID(name));
326
327
328 is_write_lock(space);
329
330 for (;;) {
331 ipc_entry_t entry;
1c79356b 332
316670eb 333 if (!is_active(space)) {
1c79356b 334 is_write_unlock(space);
1c79356b
A
335 return KERN_INVALID_TASK;
336 }
337
338 /*
339 * If we are under the table cutoff,
340 * there are usually four cases:
341 * 1) The entry is reserved (index 0)
342 * 2) The entry is inuse, for the same name
343 * 3) The entry is inuse, for a different name
344 * 4) The entry is free
345 * For a task with a "fast" IPC space, we disallow
346 * cases 1) and 3), because ports cannot be renamed.
347 */
348 if (index < space->is_table_size) {
349 ipc_entry_t table = space->is_table;
350
351 entry = &table[index];
352
353 if (index == 0) {
316670eb 354 /* case #1 - the entry is reserved */
1c79356b
A
355 assert(!IE_BITS_TYPE(entry->ie_bits));
356 assert(!IE_BITS_GEN(entry->ie_bits));
0a7de745 357 is_write_unlock(space);
316670eb 358 return KERN_FAILURE;
1c79356b
A
359 } else if (IE_BITS_TYPE(entry->ie_bits)) {
360 if (IE_BITS_GEN(entry->ie_bits) == gen) {
316670eb 361 /* case #2 -- the entry is inuse, for the same name */
1c79356b 362 *entryp = entry;
1c79356b 363 return KERN_SUCCESS;
316670eb
A
364 } else {
365 /* case #3 -- the entry is inuse, for a different name. */
366 /* Collisions are not allowed */
0a7de745 367 is_write_unlock(space);
316670eb 368 return KERN_FAILURE;
1c79356b
A
369 }
370 } else {
371 mach_port_index_t free_index, next_index;
372
373 /*
316670eb 374 * case #4 -- the entry is free
1c79356b
A
375 * Rip the entry out of the free list.
376 */
377
378 for (free_index = 0;
0a7de745
A
379 (next_index = table[free_index].ie_next)
380 != index;
381 free_index = next_index) {
1c79356b 382 continue;
0a7de745 383 }
1c79356b
A
384
385 table[free_index].ie_next =
0a7de745 386 table[next_index].ie_next;
fe8ab488
A
387 space->is_table_free--;
388
316670eb 389 /* mark the previous entry modified - reconstructing the name */
0a7de745
A
390 ipc_entry_modified(space,
391 MACH_PORT_MAKE(free_index,
392 IE_BITS_GEN(table[free_index].ie_bits)),
393 &table[free_index]);
1c79356b
A
394
395 entry->ie_bits = gen;
6d2010ae 396 entry->ie_request = IE_REQ_NONE;
1c79356b
A
397 *entryp = entry;
398
399 assert(entry->ie_object == IO_NULL);
1c79356b
A
400 return KERN_SUCCESS;
401 }
402 }
403
404 /*
316670eb
A
405 * We grow the table so that the name
406 * index fits in the array space.
407 * Because the space will be unlocked,
408 * we must restart.
1c79356b 409 */
0a7de745 410 kern_return_t kr;
39236c6e 411 kr = ipc_entry_grow_table(space, index + 1);
316670eb
A
412 assert(kr != KERN_NO_SPACE);
413 if (kr != KERN_SUCCESS) {
414 /* space is unlocked */
415 return kr;
1c79356b 416 }
316670eb 417 continue;
1c79356b
A
418 }
419}
420
421/*
422 * Routine: ipc_entry_dealloc
423 * Purpose:
424 * Deallocates an entry from a space.
425 * Conditions:
426 * The space must be write-locked throughout.
427 * The space must be active.
428 */
429
430void
431ipc_entry_dealloc(
0a7de745
A
432 ipc_space_t space,
433 mach_port_name_t name,
434 ipc_entry_t entry)
1c79356b
A
435{
436 ipc_entry_t table;
437 ipc_entry_num_t size;
438 mach_port_index_t index;
439
316670eb 440 assert(is_active(space));
1c79356b 441 assert(entry->ie_object == IO_NULL);
6d2010ae
A
442 assert(entry->ie_request == IE_REQ_NONE);
443
444#if 1
0a7de745 445 if (entry->ie_request != IE_REQ_NONE) {
6d2010ae 446 panic("ipc_entry_dealloc()\n");
0a7de745 447 }
6d2010ae 448#endif
1c79356b
A
449
450 index = MACH_PORT_INDEX(name);
451 table = space->is_table;
452 size = space->is_table_size;
453
316670eb 454 if ((index < size) && (entry == &table[index])) {
1c79356b 455 assert(IE_BITS_GEN(entry->ie_bits) == MACH_PORT_GEN(name));
5c9f4661 456 entry->ie_bits &= (IE_BITS_GEN_MASK | IE_BITS_ROLL_MASK);
1c79356b
A
457 entry->ie_next = table->ie_next;
458 table->ie_next = index;
fe8ab488 459 space->is_table_free++;
1c79356b 460 } else {
316670eb
A
461 /*
462 * Nothing to do. The entry does not match
463 * so there is nothing to deallocate.
464 */
0a7de745 465 assert(index < size);
316670eb
A
466 assert(entry == &table[index]);
467 assert(IE_BITS_GEN(entry->ie_bits) == MACH_PORT_GEN(name));
468 }
469 ipc_entry_modified(space, name, entry);
470}
1c79356b 471
316670eb
A
472/*
473 * Routine: ipc_entry_modified
474 * Purpose:
475 * Note that an entry was modified in a space.
476 * Conditions:
477 * Assumes exclusive write access to the space,
478 * either through a write lock or being the cleaner
479 * on an inactive space.
480 */
1c79356b 481
316670eb
A
482void
483ipc_entry_modified(
0a7de745
A
484 ipc_space_t space,
485 mach_port_name_t name,
316670eb
A
486 __assert_only ipc_entry_t entry)
487{
488 ipc_entry_t table;
489 ipc_entry_num_t size;
490 mach_port_index_t index;
1c79356b 491
316670eb
A
492 index = MACH_PORT_INDEX(name);
493 table = space->is_table;
494 size = space->is_table_size;
1c79356b 495
316670eb
A
496 assert(index < size);
497 assert(entry == &table[index]);
1c79356b 498
316670eb
A
499 assert(space->is_low_mod <= size);
500 assert(space->is_high_mod < size);
1c79356b 501
0a7de745 502 if (index < space->is_low_mod) {
316670eb 503 space->is_low_mod = index;
0a7de745
A
504 }
505 if (index > space->is_high_mod) {
316670eb 506 space->is_high_mod = index;
0a7de745 507 }
d9a64523
A
508
509 KERNEL_DEBUG_CONSTANT(
0a7de745 510 MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_PORT_ENTRY_MODIFY) | DBG_FUNC_NONE,
d9a64523
A
511 space->is_task ? task_pid(space->is_task) : 0,
512 name,
513 entry->ie_bits,
514 0,
515 0);
1c79356b
A
516}
517
316670eb
A
518#define IPC_ENTRY_GROW_STATS 1
519#if IPC_ENTRY_GROW_STATS
520static uint64_t ipc_entry_grow_count = 0;
521static uint64_t ipc_entry_grow_rescan = 0;
522static uint64_t ipc_entry_grow_rescan_max = 0;
523static uint64_t ipc_entry_grow_rescan_entries = 0;
524static uint64_t ipc_entry_grow_rescan_entries_max = 0;
0a7de745
A
525static uint64_t ipc_entry_grow_freelist_entries = 0;
526static uint64_t ipc_entry_grow_freelist_entries_max = 0;
316670eb
A
527#endif
528
1c79356b
A
529/*
530 * Routine: ipc_entry_grow_table
531 * Purpose:
532 * Grows the table in a space.
533 * Conditions:
534 * The space must be write-locked and active before.
316670eb
A
535 * If successful, the space is also returned locked.
536 * On failure, the space is returned unlocked.
1c79356b
A
537 * Allocates memory.
538 * Returns:
539 * KERN_SUCCESS Grew the table.
540 * KERN_SUCCESS Somebody else grew the table.
541 * KERN_SUCCESS The space died.
542 * KERN_NO_SPACE Table has maximum size already.
543 * KERN_RESOURCE_SHORTAGE Couldn't allocate a new table.
544 */
545
546kern_return_t
547ipc_entry_grow_table(
0a7de745
A
548 ipc_space_t space,
549 ipc_table_elems_t target_size)
1c79356b
A
550{
551 ipc_entry_num_t osize, size, nsize, psize;
552
316670eb
A
553 ipc_entry_t otable, table;
554 ipc_table_size_t oits, its, nits;
555 mach_port_index_t i, free_index;
556 mach_port_index_t low_mod, hi_mod;
557 ipc_table_index_t sanity;
558#if IPC_ENTRY_GROW_STATS
559 uint64_t rescan_count = 0;
560#endif
561 assert(is_active(space));
1c79356b 562
316670eb
A
563 if (is_growing(space)) {
564 /*
565 * Somebody else is growing the table.
566 * We just wait for them to finish.
567 */
1c79356b 568
316670eb
A
569 is_write_sleep(space);
570 return KERN_SUCCESS;
571 }
1c79356b 572
316670eb 573 otable = space->is_table;
0a7de745 574
316670eb
A
575 its = space->is_table_next;
576 size = its->its_size;
0a7de745 577
316670eb
A
578 /*
579 * Since is_table_next points to the next natural size
580 * we can identify the current size entry.
581 */
582 oits = its - 1;
583 osize = oits->its_size;
0a7de745 584
316670eb
A
585 /*
586 * If there is no target size, then the new size is simply
587 * specified by is_table_next. If there is a target
588 * size, then search for the next entry.
589 */
590 if (target_size != ITS_SIZE_NONE) {
591 if (target_size <= osize) {
0a7de745 592 /* the space is locked */
316670eb 593 return KERN_SUCCESS;
1c79356b 594 }
1c79356b 595
316670eb
A
596 psize = osize;
597 while ((psize != size) && (target_size > size)) {
598 psize = size;
599 its++;
600 size = its->its_size;
601 }
602 if (psize == size) {
1c79356b
A
603 is_write_unlock(space);
604 return KERN_NO_SPACE;
605 }
316670eb 606 }
1c79356b 607
316670eb 608 if (osize == size) {
1c79356b 609 is_write_unlock(space);
316670eb
A
610 return KERN_NO_SPACE;
611 }
0a7de745 612
316670eb
A
613 nits = its + 1;
614 nsize = nits->its_size;
615 assert((osize < size) && (size <= nsize));
1c79356b 616
316670eb
A
617 /*
618 * We'll attempt to grow the table.
619 *
620 * Because we will be copying without the space lock, reset
621 * the lowest_mod index to just beyond the end of the current
622 * table. Modification of entries (other than hashes) will
623 * bump this downward, and we only have to reprocess entries
624 * above that mark. Eventually, we'll get done.
625 */
626 is_start_growing(space);
627 space->is_low_mod = osize;
628 space->is_high_mod = 0;
629#if IPC_ENTRY_GROW_STATS
630 ipc_entry_grow_count++;
631#endif
632 is_write_unlock(space);
1c79356b 633
316670eb
A
634 table = it_entries_alloc(its);
635 if (table == IE_NULL) {
1c79356b 636 is_write_lock(space);
316670eb
A
637 is_done_growing(space);
638 is_write_unlock(space);
639 thread_wakeup((event_t) space);
640 return KERN_RESOURCE_SHORTAGE;
641 }
1c79356b 642
5c9f4661 643 ipc_space_rand_freelist(space, table, osize, size);
1c79356b 644
316670eb
A
645 /* clear out old entries in new table */
646 memset((void *)table, 0, osize * sizeof(*table));
1c79356b 647
316670eb
A
648 low_mod = 0;
649 hi_mod = osize - 1;
0a7de745 650rescan:
316670eb
A
651 /*
652 * Within the range of the table that changed, determine what we
653 * have to take action on. For each entry, take a snapshot of the
654 * corresponding entry in the old table (so it won't change
655 * during this iteration). The snapshot may not be self-consistent
656 * (if we caught it in the middle of being changed), so be very
657 * cautious with the values.
658 */
659 for (i = low_mod; i <= hi_mod; i++) {
660 ipc_entry_t entry = &table[i];
0a7de745 661 struct ipc_entry osnap = otable[i];
1c79356b 662
316670eb
A
663 if (entry->ie_object != osnap.ie_object ||
664 IE_BITS_TYPE(entry->ie_bits) != IE_BITS_TYPE(osnap.ie_bits)) {
316670eb 665 if (entry->ie_object != IO_NULL &&
0a7de745 666 IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_SEND) {
316670eb 667 ipc_hash_table_delete(table, size, entry->ie_object, i, entry);
0a7de745 668 }
1c79356b 669
316670eb
A
670 entry->ie_object = osnap.ie_object;
671 entry->ie_bits = osnap.ie_bits;
672 entry->ie_request = osnap.ie_request; /* or ie_next */
1c79356b 673
316670eb 674 if (entry->ie_object != IO_NULL &&
0a7de745 675 IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_SEND) {
316670eb 676 ipc_hash_table_insert(table, size, entry->ie_object, i, entry);
0a7de745 677 }
316670eb
A
678 } else {
679 assert(entry->ie_object == osnap.ie_object);
680 entry->ie_bits = osnap.ie_bits;
681 entry->ie_request = osnap.ie_request; /* or ie_next */
1c79356b 682 }
316670eb
A
683 }
684 table[0].ie_next = otable[0].ie_next; /* always rebase the freelist */
1c79356b 685
316670eb
A
686 /*
687 * find the end of the freelist (should be short). But be careful,
688 * the list items can change so only follow through truly free entries
689 * (no problem stopping short in those cases, because we'll rescan).
690 */
691 free_index = 0;
692 for (sanity = 0; sanity < osize; sanity++) {
0a7de745 693 if (table[free_index].ie_object != IPC_OBJECT_NULL) {
316670eb 694 break;
0a7de745 695 }
316670eb 696 i = table[free_index].ie_next;
0a7de745 697 if (i == 0 || i >= osize) {
316670eb 698 break;
0a7de745 699 }
316670eb
A
700 free_index = i;
701 }
702#if IPC_ENTRY_GROW_STATS
703 ipc_entry_grow_freelist_entries += sanity;
0a7de745 704 if (sanity > ipc_entry_grow_freelist_entries_max) {
316670eb 705 ipc_entry_grow_freelist_entries_max = sanity;
0a7de745 706 }
316670eb 707#endif
0a7de745 708
316670eb 709 is_write_lock(space);
1c79356b 710
316670eb
A
711 /*
712 * We need to do a wakeup on the space,
713 * to rouse waiting threads. We defer
714 * this until the space is unlocked,
715 * because we don't want them to spin.
716 */
1c79356b 717
316670eb 718 if (!is_active(space)) {
1c79356b 719 /*
316670eb 720 * The space died while it was unlocked.
1c79356b
A
721 */
722
316670eb 723 is_done_growing(space);
1c79356b
A
724 is_write_unlock(space);
725 thread_wakeup((event_t) space);
316670eb 726 it_entries_free(its, table);
1c79356b 727 is_write_lock(space);
316670eb
A
728 return KERN_SUCCESS;
729 }
1c79356b 730
316670eb
A
731 /* If the space changed while unlocked, go back and process the changes */
732 if (space->is_low_mod < osize) {
733 assert(space->is_high_mod > 0);
734 low_mod = space->is_low_mod;
735 space->is_low_mod = osize;
736 hi_mod = space->is_high_mod;
737 space->is_high_mod = 0;
738 is_write_unlock(space);
739#if IPC_ENTRY_GROW_STATS
740 rescan_count++;
0a7de745 741 if (rescan_count > ipc_entry_grow_rescan_max) {
316670eb 742 ipc_entry_grow_rescan_max = rescan_count;
0a7de745 743 }
316670eb
A
744
745 ipc_entry_grow_rescan++;
746 ipc_entry_grow_rescan_entries += hi_mod - low_mod + 1;
0a7de745 747 if (hi_mod - low_mod + 1 > ipc_entry_grow_rescan_entries_max) {
316670eb 748 ipc_entry_grow_rescan_entries_max = hi_mod - low_mod + 1;
0a7de745 749 }
316670eb
A
750#endif
751 goto rescan;
752 }
1c79356b 753
316670eb
A
754 /* link new free entries onto the rest of the freelist */
755 assert(table[free_index].ie_next == 0 &&
0a7de745 756 table[free_index].ie_object == IO_NULL);
316670eb 757 table[free_index].ie_next = osize;
1c79356b 758
316670eb
A
759 assert(space->is_table == otable);
760 assert((space->is_table_next == its) ||
761 (target_size != ITS_SIZE_NONE));
762 assert(space->is_table_size == osize);
1c79356b 763
316670eb
A
764 space->is_table = table;
765 space->is_table_size = size;
766 space->is_table_next = nits;
fe8ab488 767 space->is_table_free += size - osize;
1c79356b 768
316670eb
A
769 is_done_growing(space);
770 is_write_unlock(space);
1c79356b 771
316670eb 772 thread_wakeup((event_t) space);
1c79356b 773
316670eb
A
774 /*
775 * Now we need to free the old table.
776 */
777 it_entries_free(oits, otable);
778 is_write_lock(space);
779
780 return KERN_SUCCESS;
1c79356b 781}
39037602
A
782
783
784/*
785 * Routine: ipc_entry_name_mask
786 * Purpose:
787 * Ensure a mach port name has the default ipc entry
788 * generation bits set. This can be used to ensure that
789 * a name passed in by user space matches names generated
790 * by the kernel.
791 * Conditions:
792 * None.
793 * Returns:
794 * 'name' input with default generation bits masked or added
795 * as appropriate.
796 */
797mach_port_name_t
798ipc_entry_name_mask(mach_port_name_t name)
799{
800#ifndef NO_PORT_GEN
5c9f4661 801 static mach_port_name_t null_name = MACH_PORT_MAKE(0, IE_BITS_GEN_MASK + IE_BITS_GEN_ONE);
39037602
A
802 return name | null_name;
803#else
5c9f4661 804 static mach_port_name_t null_name = MACH_PORT_MAKE(0, ~(IE_BITS_GEN_MASK + IE_BITS_GEN_ONE));
39037602
A
805 return name & ~null_name;
806#endif
807}