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