]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ipc/ipc_hash.c
xnu-7195.50.7.100.1.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_hash.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_hash.c
60 * Author: Rich Draves
61 * Date: 1989
62 *
63 * Entry hash table operations.
64 */
65
66#include <mach/boolean.h>
67#include <mach/port.h>
1c79356b
A
68#include <ipc/port.h>
69#include <ipc/ipc_space.h>
70#include <ipc/ipc_object.h>
71#include <ipc/ipc_entry.h>
72#include <ipc/ipc_hash.h>
73#include <ipc/ipc_init.h>
0a7de745 74#include <os/hash.h>
1c79356b
A
75
76#include <mach_ipc_debug.h>
77
0a7de745 78#if MACH_IPC_DEBUG
1c79356b
A
79#include <mach/kern_return.h>
80#include <mach_debug/hash_info.h>
81#include <vm/vm_map.h>
82#include <vm/vm_kern.h>
0a7de745 83#endif /* MACH_IPC_DEBUG */
1c79356b
A
84
85/*
0a7de745 86 * Forward declarations
1c79356b
A
87 */
88
1c79356b
A
89/* Delete an entry from the local reverse hash table */
90void ipc_hash_local_delete(
0a7de745
A
91 ipc_space_t space,
92 ipc_object_t obj,
93 mach_port_index_t index,
94 ipc_entry_t entry);
1c79356b
A
95
96/*
97 * Routine: ipc_hash_lookup
98 * Purpose:
99 * Converts (space, obj) -> (name, entry).
100 * Returns TRUE if an entry was found.
101 * Conditions:
102 * The space must be locked (read or write) throughout.
103 */
104
105boolean_t
106ipc_hash_lookup(
0a7de745
A
107 ipc_space_t space,
108 ipc_object_t obj,
109 mach_port_name_t *namep,
110 ipc_entry_t *entryp)
1c79356b 111{
316670eb 112 return ipc_hash_table_lookup(space->is_table, space->is_table_size, obj, namep, entryp);
1c79356b
A
113}
114
115/*
116 * Routine: ipc_hash_insert
117 * Purpose:
118 * Inserts an entry into the appropriate reverse hash table,
119 * so that ipc_hash_lookup will find it.
120 * Conditions:
121 * The space must be write-locked.
122 */
123
124void
125ipc_hash_insert(
0a7de745
A
126 ipc_space_t space,
127 ipc_object_t obj,
128 mach_port_name_t name,
129 ipc_entry_t entry)
1c79356b
A
130{
131 mach_port_index_t index;
132
133 index = MACH_PORT_INDEX(name);
0a7de745 134 space->is_table_hashed++;
316670eb 135 ipc_hash_table_insert(space->is_table, space->is_table_size, obj, index, entry);
1c79356b
A
136}
137
138/*
139 * Routine: ipc_hash_delete
140 * Purpose:
141 * Deletes an entry from the appropriate reverse hash table.
142 * Conditions:
143 * The space must be write-locked.
144 */
145
146void
147ipc_hash_delete(
0a7de745
A
148 ipc_space_t space,
149 ipc_object_t obj,
150 mach_port_name_t name,
151 ipc_entry_t entry)
1c79356b
A
152{
153 mach_port_index_t index;
154
155 index = MACH_PORT_INDEX(name);
0a7de745 156 space->is_table_hashed--;
316670eb 157 ipc_hash_table_delete(space->is_table, space->is_table_size, obj, index, entry);
1c79356b
A
158}
159
160/*
161 * Each space has a local reverse hash table, which holds
162 * entries from the space's table. In fact, the hash table
163 * just uses a field (ie_index) in the table itself.
164 *
165 * The local hash table is an open-addressing hash table,
166 * which means that when a collision occurs, instead of
167 * throwing the entry into a bucket, the entry is rehashed
168 * to another position in the table. In this case the rehash
169 * is very simple: linear probing (ie, just increment the position).
170 * This simple rehash makes deletions tractable (they're still a pain),
171 * but it means that collisions tend to build up into clumps.
172 *
173 * Because at least one entry in the table (index 0) is always unused,
174 * there will always be room in the reverse hash table. If a table
175 * with n slots gets completely full, the reverse hash table will
176 * have one giant clump of n-1 slots and one free slot somewhere.
177 * Because entries are only entered into the reverse table if they
178 * are pure send rights (not receive, send-once, port-set,
179 * or dead-name rights), and free entries of course aren't entered,
180 * I expect the reverse hash table won't get unreasonably full.
181 *
182 * Ordered hash tables (Amble & Knuth, Computer Journal, v. 17, no. 2,
183 * pp. 135-142.) may be desirable here. They can dramatically help
184 * unsuccessful lookups. But unsuccessful lookups are almost always
185 * followed by insertions, and those slow down somewhat. They
186 * also can help deletions somewhat. Successful lookups aren't affected.
187 * So possibly a small win; probably nothing significant.
188 */
189
0a7de745
A
190#define IH_TABLE_HASH(obj, size) \
191 ((mach_port_index_t)(os_hash_kernel_pointer(obj) % (size)))
1c79356b
A
192
193/*
316670eb 194 * Routine: ipc_hash_table_lookup
1c79356b 195 * Purpose:
316670eb 196 * Converts (table, obj) -> (name, entry).
1c79356b 197 * Conditions:
316670eb 198 * Must have read consistency on the table.
1c79356b
A
199 */
200
201boolean_t
316670eb 202ipc_hash_table_lookup(
0a7de745
A
203 ipc_entry_t table,
204 ipc_entry_num_t size,
205 ipc_object_t obj,
206 mach_port_name_t *namep,
207 ipc_entry_t *entryp)
1c79356b 208{
0a7de745 209 mach_port_index_t hindex, index, hdist;
1c79356b 210
39236c6e
A
211 if (obj == IO_NULL) {
212 return FALSE;
213 }
1c79356b 214
316670eb 215 hindex = IH_TABLE_HASH(obj, size);
0a7de745 216 hdist = 0;
1c79356b
A
217
218 /*
219 * Ideally, table[hindex].ie_index is the name we want.
220 * However, must check ie_object to verify this,
221 * because collisions can happen. In case of a collision,
222 * search farther along in the clump.
223 */
224
225 while ((index = table[hindex].ie_index) != 0) {
0a7de745
A
226 ipc_entry_t entry = &table[index];
227
228 /*
229 * if our current displacement is strictly larger
230 * than the current slot one, then insertion would
231 * have stolen his place so we can't possibly exist.
232 */
233 if (hdist > table[hindex].ie_dist) {
234 return FALSE;
1c79356b
A
235 }
236
0a7de745
A
237 /*
238 * If our current displacement is exactly the current
239 * slot displacement, then it can be a match, let's check.
240 */
241 if (hdist == table[hindex].ie_dist) {
242 assert(index < size);
243 if (entry->ie_object == obj) {
244 *entryp = entry;
245 *namep = MACH_PORT_MAKE(index,
246 IE_BITS_GEN(entry->ie_bits));
247 return TRUE;
248 }
249 } else {
250 assert(entry->ie_object != obj);
251 }
252
253 if (hdist < IPC_ENTRY_DIST_MAX) {
254 /* peg the displacement distance at IPC_ENTRY_DIST_MAX */
255 ++hdist;
256 }
257 if (++hindex == size) {
1c79356b 258 hindex = 0;
0a7de745 259 }
1c79356b
A
260 }
261
262 return FALSE;
263}
264
265/*
316670eb 266 * Routine: ipc_hash_table_insert
1c79356b
A
267 * Purpose:
268 * Inserts an entry into the space's reverse hash table.
269 * Conditions:
270 * The space must be write-locked.
271 */
272
273void
316670eb 274ipc_hash_table_insert(
0a7de745
A
275 ipc_entry_t table,
276 ipc_entry_num_t size,
277 ipc_object_t obj,
278 mach_port_index_t index,
279 __assert_only ipc_entry_t entry)
1c79356b 280{
0a7de745 281 mach_port_index_t hindex, hdist;
1c79356b
A
282
283 assert(index != 0);
1c79356b
A
284 assert(obj != IO_NULL);
285
316670eb 286 hindex = IH_TABLE_HASH(obj, size);
0a7de745 287 hdist = 0;
1c79356b
A
288
289 assert(entry == &table[index]);
290 assert(entry->ie_object == obj);
291
292 /*
293 * We want to insert at hindex, but there may be collisions.
294 * If a collision occurs, search for the end of the clump
295 * and insert there.
0a7de745
A
296 *
297 * However, Robin Hood steals from the rich, and as we go
298 * through the clump, if we go over an item that is less
299 * displaced than we'd be, we steal his slot and
300 * keep inserting him in our stead.
1c79356b 301 */
1c79356b 302 while (table[hindex].ie_index != 0) {
0a7de745
A
303 if (table[hindex].ie_dist < hdist) {
304#define swap(a, b) ({ typeof(a) _tmp = (b); (b) = (a); (a) = _tmp; })
305 swap(hdist, table[hindex].ie_dist);
306 swap(index, table[hindex].ie_index);
307#undef swap
308 }
309 if (hdist < IPC_ENTRY_DIST_MAX) {
310 /* peg the displacement distance at IPC_ENTRY_DIST_MAX */
311 ++hdist;
312 }
313 if (++hindex == size) {
1c79356b 314 hindex = 0;
0a7de745 315 }
1c79356b
A
316 }
317
318 table[hindex].ie_index = index;
0a7de745 319 table[hindex].ie_dist = hdist;
1c79356b
A
320}
321
322/*
316670eb 323 * Routine: ipc_hash_table_delete
1c79356b 324 * Purpose:
316670eb 325 * Deletes an entry from the table's reverse hash.
1c79356b 326 * Conditions:
316670eb 327 * Exclusive access to the table.
1c79356b
A
328 */
329
330void
316670eb 331ipc_hash_table_delete(
0a7de745
A
332 ipc_entry_t table,
333 ipc_entry_num_t size,
334 ipc_object_t obj,
335 mach_port_index_t index,
336 __assert_only ipc_entry_t entry)
1c79356b 337{
0a7de745 338 mach_port_index_t hindex, dindex, dist;
1c79356b
A
339
340 assert(index != MACH_PORT_NULL);
1c79356b
A
341 assert(obj != IO_NULL);
342
316670eb 343 hindex = IH_TABLE_HASH(obj, size);
1c79356b
A
344
345 assert(entry == &table[index]);
346 assert(entry->ie_object == obj);
347
348 /*
349 * First check we have the right hindex for this index.
350 * In case of collision, we have to search farther
351 * along in this clump.
352 */
353
354 while (table[hindex].ie_index != index) {
0a7de745 355 if (++hindex == size) {
1c79356b 356 hindex = 0;
0a7de745 357 }
1c79356b
A
358 }
359
360 /*
361 * Now we want to set table[hindex].ie_index = 0.
362 * But if we aren't the last index in a clump,
363 * this might cause problems for lookups of objects
364 * farther along in the clump that are displaced
365 * due to collisions. Searches for them would fail
366 * at hindex instead of succeeding.
367 *
368 * So we must check the clump after hindex for objects
369 * that are so displaced, and move one up to the new hole.
370 *
371 * hindex - index of new hole in the clump
372 * dindex - index we are checking for a displaced object
373 *
374 * When we move a displaced object up into the hole,
375 * it creates a new hole, and we have to repeat the process
376 * until we get to the end of the clump.
377 */
378
0a7de745
A
379 for (;;) {
380 dindex = hindex + 1;
381 if (dindex == size) {
382 dindex = 0;
383 }
1c79356b 384
0a7de745
A
385 /*
386 * If the next element is empty or isn't displaced,
387 * then lookup will end on the next element anyway,
388 * so we can leave the hole right here, we're done
389 */
390 index = table[dindex].ie_index;
391 dist = table[dindex].ie_dist;
392 if (index == 0 || dist == 0) {
393 table[hindex].ie_index = 0;
394 table[hindex].ie_dist = 0;
395 return;
396 }
1c79356b 397
0a7de745
A
398 /*
399 * Move this object closer to its own slot by occupying the hole.
400 * If its displacement was pegged, recompute it.
401 */
402 if (dist-- == IPC_ENTRY_DIST_MAX) {
403 uint32_t desired = IH_TABLE_HASH(table[index].ie_object, size);
404 if (hindex >= desired) {
405 dist = hindex - desired;
406 } else {
407 dist = hindex + size - desired;
408 }
409 if (dist > IPC_ENTRY_DIST_MAX) {
410 dist = IPC_ENTRY_DIST_MAX;
411 }
1c79356b
A
412 }
413
0a7de745
A
414 /*
415 * Move the displaced element closer to its ideal bucket,
416 * and keep shifting elements back.
417 */
1c79356b 418 table[hindex].ie_index = index;
0a7de745
A
419 table[hindex].ie_dist = dist;
420 hindex = dindex;
1c79356b
A
421 }
422}