summaryrefslogtreecommitdiff
path: root/debian/patches/0005-ipc-replace-the-IPC-table-with-a-radix-tree.patch
diff options
context:
space:
mode:
authorJustus Winter <4winter@informatik.uni-hamburg.de>2015-04-07 18:01:06 +0200
committerJustus Winter <4winter@informatik.uni-hamburg.de>2015-04-07 18:01:06 +0200
commit3c93520fe3db24434b4e3b32a75c72dc16db6dda (patch)
treeae756f9f464439bcf685ae81250bd4ce63ec3ab0 /debian/patches/0005-ipc-replace-the-IPC-table-with-a-radix-tree.patch
parentcdcfc6bd47fedfb62ba68ea3ad3166c78cfc6e46 (diff)
add patch series
Diffstat (limited to 'debian/patches/0005-ipc-replace-the-IPC-table-with-a-radix-tree.patch')
-rw-r--r--debian/patches/0005-ipc-replace-the-IPC-table-with-a-radix-tree.patch854
1 files changed, 854 insertions, 0 deletions
diff --git a/debian/patches/0005-ipc-replace-the-IPC-table-with-a-radix-tree.patch b/debian/patches/0005-ipc-replace-the-IPC-table-with-a-radix-tree.patch
new file mode 100644
index 0000000..82494d0
--- /dev/null
+++ b/debian/patches/0005-ipc-replace-the-IPC-table-with-a-radix-tree.patch
@@ -0,0 +1,854 @@
+From a2a15e62e9e303fe26b834e44d0de6aa7648af96 Mon Sep 17 00:00:00 2001
+From: Justus Winter <4winter@informatik.uni-hamburg.de>
+Date: Fri, 20 Mar 2015 00:21:14 +0100
+Subject: [PATCH gnumach 5/7] ipc: replace the IPC table with a radix tree
+
+Currently, the port names are mapped to an IPC object (e.g. a port)
+using a table. This, however, requires large chunks of continuous
+memory, and leads to scalability problems as virtual kernel memory is
+a scarce resource. To avoid excessive overhead, non-contiguous port
+names are spilled into a splay tree.
+
+Replace the IPC table with a radix tree. As the radix tree is able to
+store non-contiguous names with reasonable overhead, we can drop the
+splay tree as well.
+
+* ipc/ipc_entry.c (ipc_entry_cache): New variable.
+(ipc_entry_lookup): Replace with a radix tree lookup.
+(ipc_entry_get): The free list handling is changed a little. Adopt
+accordingly.
+(ipc_entry_free_name): New function.
+(ipc_entry_alloc): Adopt accordingly.
+(ipc_entry_alloc_name): Likewise.
+(ipc_entry_dealloc): Likewise.
+* ipc/ipc_entry.h (struct ipc_entry): Update comment, add field for
+free list.
+(ipc_entry_cache, ie_alloc, ie_free): New declarations.
+* ipc/ipc_hash.c: We no longer use splay trees, so we do not need the
+global reverse hash table.
+* ipc/ipc_init.c (ipc_bootstrap): Initialize `ipc_entry' cache.
+* ipc/ipc_kmsg.c (ipc_kmsg_copyout_header): Use `ipc_entry_alloc'
+instead of re-coding it.
+* ipc/ipc_object.c (ipc_object_copyout): Likewise.
+(ipc_object_copyout_multiname): Likewise.
+* ipc/ipc_space.c (ipc_space_create): Initialize radix tree and free list.
+(ipc_space_destroy): Free ipc entries and radix tree.
+* ipc/ipc_space.h (struct ipc_space): Add radix tree, free list, and size.
+---
+ ipc/ipc_entry.c | 372 ++++++++++++++++---------------------------------------
+ ipc/ipc_entry.h | 11 +-
+ ipc/ipc_hash.c | 23 +---
+ ipc/ipc_init.c | 3 +
+ ipc/ipc_kmsg.c | 24 ++--
+ ipc/ipc_object.c | 22 +---
+ ipc/ipc_space.c | 42 +++----
+ ipc/ipc_space.h | 8 +-
+ 8 files changed, 162 insertions(+), 343 deletions(-)
+
+diff --git a/ipc/ipc_entry.c b/ipc/ipc_entry.c
+index 5b9fd98..58d453b 100644
+--- a/ipc/ipc_entry.c
++++ b/ipc/ipc_entry.c
+@@ -51,6 +51,8 @@
+ #include <ipc/ipc_table.h>
+ #include <ipc/ipc_object.h>
+
++struct kmem_cache ipc_entry_cache;
++
+ struct kmem_cache ipc_tree_entry_cache;
+
+ /*
+@@ -69,6 +71,7 @@ ipc_entry_tree_collision(
+ ipc_space_t space,
+ mach_port_t name)
+ {
++ assert(!"reached");
+ mach_port_index_t index;
+ mach_port_t lower, upper;
+
+@@ -100,29 +103,13 @@ ipc_entry_lookup(
+ ipc_space_t space,
+ mach_port_t name)
+ {
+- mach_port_index_t index;
+ ipc_entry_t entry;
+
+ assert(space->is_active);
+-
+- index = MACH_PORT_INDEX(name);
+- if (index < space->is_table_size) {
+- entry = &space->is_table[index];
+- if (IE_BITS_GEN(entry->ie_bits) != MACH_PORT_GEN(name))
+- if (entry->ie_bits & IE_BITS_COLLISION) {
+- assert(space->is_tree_total > 0);
+- goto tree_lookup;
+- } else
+- entry = IE_NULL;
+- else if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE)
+- entry = IE_NULL;
+- } else if (space->is_tree_total == 0)
+- entry = IE_NULL;
+- else
+- tree_lookup:
+- entry = (ipc_entry_t)
+- ipc_splay_tree_lookup(&space->is_tree, name);
+-
++ entry = rdxtree_lookup(&space->is_map, name);
++ if (entry != IE_NULL
++ && IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE)
++ entry = NULL;
+ assert((entry == IE_NULL) || IE_BITS_TYPE(entry->ie_bits));
+ return entry;
+ }
+@@ -145,21 +132,18 @@ ipc_entry_get(
+ mach_port_t *namep,
+ ipc_entry_t *entryp)
+ {
+- ipc_entry_t table;
+- mach_port_index_t first_free;
+ mach_port_t new_name;
+ ipc_entry_t free_entry;
+
+ assert(space->is_active);
+
+- table = space->is_table;
+- first_free = table->ie_next;
+-
+- if (first_free == 0)
++ /* Get entry from the free list. */
++ free_entry = space->is_free_list;
++ if (free_entry == IE_NULL)
+ return KERN_NO_SPACE;
+
+- free_entry = &table[first_free];
+- table->ie_next = free_entry->ie_next;
++ space->is_free_list = free_entry->ie_next_free;
++ space->is_free_list_size -= 1;
+
+ /*
+ * Initialize the new entry. We need only
+@@ -173,7 +157,7 @@ ipc_entry_get(
+ gen = free_entry->ie_bits + IE_BITS_GEN_ONE;
+ free_entry->ie_bits = gen;
+ free_entry->ie_request = 0;
+- new_name = MACH_PORT_MAKE(first_free, gen);
++ new_name = MACH_PORT_MAKE(free_entry->ie_name, gen);
+ }
+
+ /*
+@@ -186,6 +170,7 @@ ipc_entry_get(
+ assert(MACH_PORT_VALID(new_name));
+ assert(free_entry->ie_object == IO_NULL);
+
++ space->is_size += 1;
+ *namep = new_name;
+ *entryp = free_entry;
+ return KERN_SUCCESS;
+@@ -212,23 +197,44 @@ ipc_entry_alloc(
+ ipc_entry_t *entryp)
+ {
+ kern_return_t kr;
++ ipc_entry_t entry;
++ unsigned long long key;
+
+ is_write_lock(space);
+
+- for (;;) {
+- if (!space->is_active) {
+- is_write_unlock(space);
+- return KERN_INVALID_TASK;
+- }
++ if (!space->is_active) {
++ is_write_unlock(space);
++ return KERN_INVALID_TASK;
++ }
++
++ kr = ipc_entry_get(space, namep, entryp);
++ if (kr == KERN_SUCCESS)
++ /* Success. Space is write-locked. */
++ return kr;
+
+- kr = ipc_entry_get(space, namep, entryp);
+- if (kr == KERN_SUCCESS)
+- return kr;
++ entry = ie_alloc();
++ if (entry == IE_NULL) {
++ is_write_unlock(space);
++ return KERN_RESOURCE_SHORTAGE;
++ }
+
+- kr = ipc_entry_grow_table(space);
+- if (kr != KERN_SUCCESS)
+- return kr; /* space is unlocked */
++ kr = rdxtree_insert_alloc(&space->is_map, entry, &key);
++ if (kr) {
++ is_write_unlock(space);
++ ie_free(entry);
++ return kr;
+ }
++ space->is_size += 1;
++
++ entry->ie_bits = 0;
++ entry->ie_object = IO_NULL;
++ entry->ie_request = 0;
++ entry->ie_name = (mach_port_t) key;
++
++ *entryp = entry;
++ *namep = (mach_port_t) key;
++ /* Success. Space is write-locked. */
++ return KERN_SUCCESS;
+ }
+
+ /*
+@@ -252,166 +258,79 @@ ipc_entry_alloc_name(
+ mach_port_t name,
+ ipc_entry_t *entryp)
+ {
++ kern_return_t kr;
+ mach_port_index_t index = MACH_PORT_INDEX(name);
+ mach_port_gen_t gen = MACH_PORT_GEN(name);
+- ipc_tree_entry_t tree_entry = ITE_NULL;
+
++ ipc_entry_t entry, e, *prevp;
++ void **slot;
+ assert(MACH_PORT_VALID(name));
+
+-
+ is_write_lock(space);
+
+- for (;;) {
+- ipc_entry_t entry;
+- ipc_tree_entry_t tentry;
+- ipc_table_size_t its;
+-
+- if (!space->is_active) {
+- is_write_unlock(space);
+- if (tree_entry) ite_free(tree_entry);
+- return KERN_INVALID_TASK;
+- }
+-
+- /*
+- * If we are under the table cutoff,
+- * there are three cases:
+- * 1) The entry is inuse, for the same name
+- * 2) The entry is inuse, for a different name
+- * 3) The entry is free
+- */
+-
+- if ((0 < index) && (index < space->is_table_size)) {
+- ipc_entry_t table = space->is_table;
+-
+- entry = &table[index];
+-
+- if (IE_BITS_TYPE(entry->ie_bits)) {
+- if (IE_BITS_GEN(entry->ie_bits) == gen) {
+- *entryp = entry;
+- if (tree_entry) ite_free(tree_entry);
+- return KERN_SUCCESS;
+- }
+- } else {
+- mach_port_index_t free_index, next_index;
+-
+- /*
+- * Rip the entry out of the free list.
+- */
+-
+- for (free_index = 0;
+- (next_index = table[free_index].ie_next)
+- != index;
+- free_index = next_index)
+- continue;
+-
+- table[free_index].ie_next =
+- table[next_index].ie_next;
+-
+- entry->ie_bits = gen;
+- assert(entry->ie_object == IO_NULL);
+- entry->ie_request = 0;
+-
+- *entryp = entry;
+- if (tree_entry) ite_free(tree_entry);
+- return KERN_SUCCESS;
+- }
+- }
+-
+- /*
+- * Before trying to allocate any memory,
+- * check if the entry already exists in the tree.
+- * This avoids spurious resource errors.
+- * The splay tree makes a subsequent lookup/insert
+- * of the same name cheap, so this costs little.
+- */
++ if (!space->is_active) {
++ is_write_unlock(space);
++ return KERN_INVALID_TASK;
++ }
+
+- if ((space->is_tree_total > 0) &&
+- ((tentry = ipc_splay_tree_lookup(&space->is_tree, name))
+- != ITE_NULL)) {
+- assert(tentry->ite_space == space);
+- assert(IE_BITS_TYPE(tentry->ite_bits));
++ slot = rdxtree_lookup_slot(&space->is_map, name);
++ if (slot != NULL)
++ entry = *(ipc_entry_t *) slot;
+
+- *entryp = &tentry->ite_entry;
+- if (tree_entry) ite_free(tree_entry);
+- return KERN_SUCCESS;
++ if (slot == NULL || entry == IE_NULL) {
++ entry = ie_alloc();
++ if (entry == IE_NULL) {
++ is_write_unlock(space);
++ return KERN_RESOURCE_SHORTAGE;
+ }
+
+- its = space->is_table_next;
++ entry->ie_bits = 0;
++ entry->ie_object = IO_NULL;
++ entry->ie_request = 0;
++ entry->ie_name = name;
+
+- /*
+- * Check if the table should be grown.
+- *
+- * Note that if space->is_table_size == its->its_size,
+- * then we won't ever try to grow the table.
+- *
+- * Note that we are optimistically assuming that name
+- * doesn't collide with any existing names. (So if
+- * it were entered into the tree, is_tree_small would
+- * be incremented.) This is OK, because even in that
+- * case, we don't lose memory by growing the table.
+- */
+-
+- if ((space->is_table_size <= index) &&
+- (index < its->its_size) &&
+- (((its->its_size - space->is_table_size) *
+- sizeof(struct ipc_entry)) <
+- ((space->is_tree_small + 1) *
+- sizeof(struct ipc_tree_entry)))) {
+- kern_return_t kr;
+-
+- /*
+- * Can save space by growing the table.
+- * Because the space will be unlocked,
+- * we must restart.
+- */
+-
+- kr = ipc_entry_grow_table(space);
+- assert(kr != KERN_NO_SPACE);
++ if (slot != NULL)
++ rdxtree_replace_slot(slot, entry);
++ else {
++ kr = rdxtree_insert(&space->is_map, name, entry);
+ if (kr != KERN_SUCCESS) {
+- /* space is unlocked */
+- if (tree_entry) ite_free(tree_entry);
++ is_write_unlock(space);
++ ie_free(entry);
+ return kr;
+ }
+-
+- continue;
+ }
++ space->is_size += 1;
+
+- /*
+- * If a splay-tree entry was allocated previously,
+- * go ahead and insert it into the tree.
+- */
++ *entryp = entry;
++ /* Success. Space is write-locked. */
++ return KERN_SUCCESS;
++ }
+
+- if (tree_entry != ITE_NULL) {
+- space->is_tree_total++;
++ if (IE_BITS_TYPE(entry->ie_bits)) {
++ /* Used entry. */
++ *entryp = entry;
++ /* Success. Space is write-locked. */
++ return KERN_SUCCESS;
++ }
+
+- if (index < space->is_table_size)
+- space->is_table[index].ie_bits |=
+- IE_BITS_COLLISION;
+- else if ((index < its->its_size) &&
+- !ipc_entry_tree_collision(space, name))
+- space->is_tree_small++;
++ /* Free entry. Rip the entry out of the free list. */
++ for (prevp = &space->is_free_list, e = space->is_free_list;
++ e != entry;
++ ({ prevp = &e->ie_next_free; e = e->ie_next_free; }))
++ continue;
+
+- ipc_splay_tree_insert(&space->is_tree,
+- name, tree_entry);
++ *prevp = entry->ie_next_free;
++ space->is_free_list_size -= 1;
+
+- tree_entry->ite_bits = 0;
+- tree_entry->ite_object = IO_NULL;
+- tree_entry->ite_request = 0;
+- tree_entry->ite_space = space;
+- *entryp = &tree_entry->ite_entry;
+- return KERN_SUCCESS;
+- }
+-
+- /*
+- * Allocate a tree entry and try again.
+- */
++ entry->ie_bits = gen;
++ assert(entry->ie_object == IO_NULL);
++ assert(entry->ie_name == name);
++ entry->ie_request = 0;
+
+- is_write_unlock(space);
+- tree_entry = ite_alloc();
+- if (tree_entry == ITE_NULL)
+- return KERN_RESOURCE_SHORTAGE;
+- is_write_lock(space);
+- }
++ space->is_size += 1;
++ *entryp = entry;
++ /* Success. Space is write-locked. */
++ return KERN_SUCCESS;
+ }
+
+ /*
+@@ -429,100 +348,20 @@ ipc_entry_dealloc(
+ mach_port_t name,
+ ipc_entry_t entry)
+ {
+- ipc_entry_t table;
+- ipc_entry_num_t size;
+- mach_port_index_t index;
+-
+ assert(space->is_active);
+ assert(entry->ie_object == IO_NULL);
+ assert(entry->ie_request == 0);
+
+- index = MACH_PORT_INDEX(name);
+- table = space->is_table;
+- size = space->is_table_size;
+-
+- if ((index < size) && (entry == &table[index])) {
+- assert(IE_BITS_GEN(entry->ie_bits) == MACH_PORT_GEN(name));
+-
+- if (entry->ie_bits & IE_BITS_COLLISION) {
+- struct ipc_splay_tree small, collisions;
+- ipc_tree_entry_t tentry;
+- mach_port_t tname;
+- boolean_t pick;
+- ipc_entry_bits_t bits;
+- ipc_object_t obj;
+-
+- /* must move an entry from tree to table */
+-
+- ipc_splay_tree_split(&space->is_tree,
+- MACH_PORT_MAKE(index+1, 0),
+- &collisions);
+- ipc_splay_tree_split(&collisions,
+- MACH_PORT_MAKE(index, 0),
+- &small);
+-
+- pick = ipc_splay_tree_pick(&collisions,
+- &tname, &tentry);
+- assert(pick);
+- assert(MACH_PORT_INDEX(tname) == index);
+-
+- bits = tentry->ite_bits;
+- entry->ie_bits = bits | MACH_PORT_GEN(tname);
+- entry->ie_object = obj = tentry->ite_object;
+- entry->ie_request = tentry->ite_request;
+- assert(tentry->ite_space == space);
+-
+- if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND) {
+- ipc_hash_global_delete(space, obj,
+- tname, tentry);
+- ipc_hash_local_insert(space, obj,
+- index, entry);
+- }
+-
+- ipc_splay_tree_delete(&collisions, tname, tentry);
+-
+- assert(space->is_tree_total > 0);
+- space->is_tree_total--;
+-
+- /* check if collision bit should still be on */
+-
+- pick = ipc_splay_tree_pick(&collisions,
+- &tname, &tentry);
+- if (pick) {
+- entry->ie_bits |= IE_BITS_COLLISION;
+- ipc_splay_tree_join(&space->is_tree,
+- &collisions);
+- }
+-
+- ipc_splay_tree_join(&space->is_tree, &small);
+- } else {
+- entry->ie_bits &= IE_BITS_GEN_MASK;
+- entry->ie_next = table->ie_next;
+- table->ie_next = index;
+- }
++ if (space->is_free_list_size < IS_FREE_LIST_SIZE_LIMIT) {
++ space->is_free_list_size += 1;
++ entry->ie_bits &= IE_BITS_GEN_MASK;
++ entry->ie_next_free = space->is_free_list;
++ space->is_free_list = entry;
+ } else {
+- ipc_tree_entry_t tentry = (ipc_tree_entry_t) entry;
+-
+- assert(tentry->ite_space == space);
+-
+- ipc_splay_tree_delete(&space->is_tree, name, tentry);
+-
+- assert(space->is_tree_total > 0);
+- space->is_tree_total--;
+-
+- if (index < size) {
+- ipc_entry_t ientry = &table[index];
+-
+- assert(ientry->ie_bits & IE_BITS_COLLISION);
+-
+- if (!ipc_entry_tree_collision(space, name))
+- ientry->ie_bits &= ~IE_BITS_COLLISION;
+- } else if ((index < space->is_table_next->its_size) &&
+- !ipc_entry_tree_collision(space, name)) {
+- assert(space->is_tree_small > 0);
+- space->is_tree_small--;
+- }
++ rdxtree_remove(&space->is_map, (unsigned long long) name);
++ ie_free(entry);
+ }
++ space->is_size -= 1;
+ }
+
+ /*
+@@ -544,6 +383,7 @@ ipc_entry_dealloc(
+ kern_return_t
+ ipc_entry_grow_table(ipc_space_t space)
+ {
++ assert(!"reached");
+ ipc_entry_num_t osize, size, nsize;
+
+ do {
+diff --git a/ipc/ipc_entry.h b/ipc/ipc_entry.h
+index 0caa70b..09e1223 100644
+--- a/ipc/ipc_entry.h
++++ b/ipc/ipc_entry.h
+@@ -56,10 +56,7 @@
+ *
+ * Free (unallocated) entries in the table have null ie_object
+ * fields. The ie_bits field is zero except for IE_BITS_GEN.
+- * The ie_next (ie_request) field links free entries into a free list.
+- *
+- * The first entry in the table (index 0) is always free.
+- * It is used as the head of the free list.
++ * The ie_next_free field links free entries into a free list.
+ */
+
+ typedef unsigned int ipc_entry_bits_t;
+@@ -69,6 +66,7 @@ typedef struct ipc_entry {
+ ipc_entry_bits_t ie_bits;
+ struct ipc_object *ie_object;
+ union {
++ struct ipc_entry *next_free;
+ mach_port_index_t next;
+ /*XXX ipc_port_request_index_t request;*/
+ unsigned int request;
+@@ -84,6 +82,8 @@ typedef struct ipc_entry {
+ #define ie_request index.request
+ #define ie_next index.next
+ #define ie_index hash.table
++#define ie_name hash.table
++#define ie_next_free index.next_free
+
+ #define IE_BITS_UREFS_MASK 0x0000ffff /* 16 bits of user-reference */
+ #define IE_BITS_UREFS(bits) ((bits) & IE_BITS_UREFS_MASK)
+@@ -129,6 +129,9 @@ extern struct kmem_cache ipc_tree_entry_cache;
+ #define ite_alloc() ((ipc_tree_entry_t) kmem_cache_alloc(&ipc_tree_entry_cache))
+ #define ite_free(ite) kmem_cache_free(&ipc_tree_entry_cache, (vm_offset_t) (ite))
+
++extern struct kmem_cache ipc_entry_cache;
++#define ie_alloc() ((ipc_entry_t) kmem_cache_alloc(&ipc_entry_cache))
++#define ie_free(e) kmem_cache_free(&ipc_entry_cache, (vm_offset_t) (e))
+
+ extern ipc_entry_t
+ ipc_entry_lookup(ipc_space_t space, mach_port_t name);
+diff --git a/ipc/ipc_hash.c b/ipc/ipc_hash.c
+index 87952a7..682b854 100644
+--- a/ipc/ipc_hash.c
++++ b/ipc/ipc_hash.c
+@@ -70,10 +70,7 @@ ipc_hash_lookup(
+ mach_port_t *namep,
+ ipc_entry_t *entryp)
+ {
+- return (ipc_hash_local_lookup(space, obj, namep, entryp) ||
+- ((space->is_tree_hash > 0) &&
+- ipc_hash_global_lookup(space, obj, namep,
+- (ipc_tree_entry_t *) entryp)));
++ return ipc_hash_local_lookup(space, obj, namep, entryp);
+ }
+
+ /*
+@@ -95,12 +92,7 @@ ipc_hash_insert(
+ mach_port_index_t index;
+
+ index = MACH_PORT_INDEX(name);
+- if ((index < space->is_table_size) &&
+- (entry == &space->is_table[index]))
+- ipc_hash_local_insert(space, obj, index, entry);
+- else
+- ipc_hash_global_insert(space, obj, name,
+- (ipc_tree_entry_t) entry);
++ ipc_hash_local_insert(space, obj, index, entry);
+ }
+
+ /*
+@@ -121,12 +113,7 @@ ipc_hash_delete(
+ mach_port_index_t index;
+
+ index = MACH_PORT_INDEX(name);
+- if ((index < space->is_table_size) &&
+- (entry == &space->is_table[index]))
+- ipc_hash_local_delete(space, obj, index, entry);
+- else
+- ipc_hash_global_delete(space, obj, name,
+- (ipc_tree_entry_t) entry);
++ ipc_hash_local_delete(space, obj, index, entry);
+ }
+
+ /*
+@@ -174,6 +161,7 @@ ipc_hash_global_lookup(
+ mach_port_t *namep,
+ ipc_tree_entry_t *entryp)
+ {
++ assert(!"reached");
+ ipc_hash_global_bucket_t bucket;
+ ipc_tree_entry_t this, *last;
+
+@@ -227,6 +215,7 @@ ipc_hash_global_insert(
+ mach_port_t name,
+ ipc_tree_entry_t entry)
+ {
++ assert(!"reached");
+ ipc_hash_global_bucket_t bucket;
+
+
+@@ -265,6 +254,7 @@ ipc_hash_global_delete(
+ mach_port_t name,
+ ipc_tree_entry_t entry)
+ {
++ assert(!"reached");
+ ipc_hash_global_bucket_t bucket;
+ ipc_tree_entry_t this, *last;
+
+@@ -307,7 +297,6 @@ ipc_hash_global_delete(
+ IE_BITS_TYPE((E)->ie_bits) == MACH_PORT_TYPE_NONE); \
+ assert((E)->ie_object == (O)); \
+ assert((E)->ie_index == (N)); \
+- assert(&(S)->is_table[N] == (E)); \
+ MACRO_END
+
+ /*
+diff --git a/ipc/ipc_init.c b/ipc/ipc_init.c
+index debda47..096e0fb 100644
+--- a/ipc/ipc_init.c
++++ b/ipc/ipc_init.c
+@@ -79,6 +79,9 @@ ipc_bootstrap(void)
+ kmem_cache_init(&ipc_tree_entry_cache, "ipc_tree_entry",
+ sizeof(struct ipc_tree_entry), 0, NULL, NULL, NULL, 0);
+
++ kmem_cache_init(&ipc_entry_cache, "ipc_entry",
++ sizeof(struct ipc_entry), 0, NULL, NULL, NULL, 0);
++
+ kmem_cache_init(&ipc_object_caches[IOT_PORT], "ipc_port",
+ sizeof(struct ipc_port), 0, NULL, NULL, NULL, 0);
+
+diff --git a/ipc/ipc_kmsg.c b/ipc/ipc_kmsg.c
+index a4a3b42..c484a5d 100644
+--- a/ipc/ipc_kmsg.c
++++ b/ipc/ipc_kmsg.c
+@@ -1998,28 +1998,20 @@ ipc_kmsg_copyout_header(
+ goto copyout_dest;
+ }
+
+- kr = ipc_entry_get(space, &reply_name, &entry);
++ kr = ipc_entry_alloc(space, &reply_name, &entry);
+ if (kr != KERN_SUCCESS) {
+ ip_unlock(reply);
+
+ if (notify_port != IP_NULL)
+ ipc_port_release_sonce(notify_port);
+
+- /* space is locked */
+- kr = ipc_entry_grow_table(space);
+- if (kr != KERN_SUCCESS) {
+- /* space is unlocked */
+-
+- if (kr == KERN_RESOURCE_SHORTAGE)
+- return (MACH_RCV_HEADER_ERROR|
+- MACH_MSG_IPC_KERNEL);
+- else
+- return (MACH_RCV_HEADER_ERROR|
+- MACH_MSG_IPC_SPACE);
+- }
+- /* space is locked again; start over */
+-
+- continue;
++ is_write_unlock(space);
++ if (kr == KERN_RESOURCE_SHORTAGE)
++ return (MACH_RCV_HEADER_ERROR|
++ MACH_MSG_IPC_KERNEL);
++ else
++ return (MACH_RCV_HEADER_ERROR|
++ MACH_MSG_IPC_SPACE);
+ }
+
+ assert(IE_BITS_TYPE(entry->ie_bits)
+diff --git a/ipc/ipc_object.c b/ipc/ipc_object.c
+index db6ef01..ec40062 100644
+--- a/ipc/ipc_object.c
++++ b/ipc/ipc_object.c
+@@ -630,15 +630,10 @@ ipc_object_copyout(
+ break;
+ }
+
+- kr = ipc_entry_get(space, &name, &entry);
++ kr = ipc_entry_alloc(space, &name, &entry);
+ if (kr != KERN_SUCCESS) {
+- /* unlocks/locks space, so must start again */
+-
+- kr = ipc_entry_grow_table(space);
+- if (kr != KERN_SUCCESS)
+- return kr; /* space is unlocked */
+-
+- continue;
++ is_write_unlock(space);
++ return kr;
+ }
+
+ assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
+@@ -691,15 +686,10 @@ ipc_object_copyout_multiname(space, object, namep)
+ return KERN_INVALID_TASK;
+ }
+
+- kr = ipc_entry_get(space, &name, &entry);
++ kr = ipc_entry_alloc(space, &name, &entry);
+ if (kr != KERN_SUCCESS) {
+- /* unlocks/locks space, so must start again */
+-
+- kr = ipc_entry_grow_table(space);
+- if (kr != KERN_SUCCESS)
+- return kr; /* space is unlocked */
+-
+- continue;
++ is_write_unlock(space);
++ return kr; /* space is unlocked */
+ }
+
+ assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
+diff --git a/ipc/ipc_space.c b/ipc/ipc_space.c
+index dcc96b3..5f939bb 100644
+--- a/ipc/ipc_space.c
++++ b/ipc/ipc_space.c
+@@ -82,6 +82,9 @@ ipc_space_release(
+ ipc_space_release_macro(space);
+ }
+
++/* A place-holder object for the zeroth entry. */
++struct ipc_entry zero_entry;
++
+ /*
+ * Routine: ipc_space_create
+ * Purpose:
+@@ -148,7 +151,13 @@ ipc_space_create(
+ space->is_tree_total = 0;
+ space->is_tree_small = 0;
+ space->is_tree_hash = 0;
++ rdxtree_init(&space->is_map);
+ rdxtree_init(&space->is_reverse_map);
++ /* The zeroth entry is reserved. */
++ rdxtree_insert(&space->is_map, 0, &zero_entry);
++ space->is_size = 1;
++ space->is_free_list = NULL;
++ space->is_free_list_size = 0;
+
+ *spacep = space;
+ return KERN_SUCCESS;
+@@ -218,30 +227,12 @@ ipc_space_destroy(
+ if (!active)
+ return;
+
+- /*
+- * If somebody is trying to grow the table,
+- * we must wait until they finish and figure
+- * out the space died.
+- */
+-
+- is_read_lock(space);
+- while (space->is_growing) {
+- assert_wait((event_t) space, FALSE);
+- is_read_unlock(space);
+- thread_block((void (*)(void)) 0);
+- is_read_lock(space);
+- }
+- is_read_unlock(space);
+-
+- /*
+- * Now we can futz with it without having it locked.
+- */
++ ipc_entry_t entry;
++ struct rdxtree_iter iter;
++ rdxtree_for_each(&space->is_map, &iter, entry) {
++ if (entry->ie_name == MACH_PORT_NULL)
++ continue;
+
+- table = space->is_table;
+- size = space->is_table_size;
+-
+- for (index = 0; index < size; index++) {
+- ipc_entry_t entry = &table[index];
+ mach_port_type_t type = IE_BITS_TYPE(entry->ie_bits);
+
+ if (type != MACH_PORT_TYPE_NONE) {
+@@ -272,6 +263,11 @@ ipc_space_destroy(
+ }
+ ipc_splay_traverse_finish(&space->is_tree);
+
++ ipc_entry_t entry;
++ struct rdxtree_iter iter;
++ rdxtree_for_each(&space->is_map, &iter, entry)
++ ie_free(entry);
++ rdxtree_remove_all(&space->is_map);
+ rdxtree_remove_all(&space->is_reverse_map);
+
+ /*
+diff --git a/ipc/ipc_space.h b/ipc/ipc_space.h
+index 51c093b..f959042 100644
+--- a/ipc/ipc_space.h
++++ b/ipc/ipc_space.h
+@@ -80,10 +80,16 @@ struct ipc_space {
+ ipc_entry_num_t is_tree_total; /* number of entries in the tree */
+ ipc_entry_num_t is_tree_small; /* # of small entries in the tree */
+ ipc_entry_num_t is_tree_hash; /* # of hashed entries in the tree */
++ struct rdxtree is_map; /* a map of entries */
++ size_t is_size; /* number of entries */
+ struct rdxtree is_reverse_map; /* maps objects to entries */
+-
++ ipc_entry_t is_free_list; /* a linked list of free entries */
++ size_t is_free_list_size; /* number of free entries */
++#define IS_FREE_LIST_SIZE_LIMIT 64 /* maximum number of entries
++ in the free list */
+ };
+
++
+ #define IS_NULL ((ipc_space_t) 0)
+
+ extern struct kmem_cache ipc_space_cache;
+--
+2.1.4
+