2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
27 * Mach Operating System
28 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
29 * All Rights Reserved.
31 * Permission to use, copy, modify and distribute this software and its
32 * documentation is hereby granted, provided that both the copyright
33 * notice and this permission notice appear in all copies of the
34 * software, derivative works or modified versions, and any portions
35 * thereof, and that both notices appear in supporting documentation.
37 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
38 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
39 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
41 * Carnegie Mellon requests users of this software to return to
43 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
44 * School of Computer Science
45 * Carnegie Mellon University
46 * Pittsburgh PA 15213-3890
48 * any improvements or extensions that they make and grant Carnegie Mellon
49 * the rights to redistribute these changes.
54 * File: ipc/ipc_hash.c
58 * Entry hash table operations.
61 #include <mach/boolean.h>
62 #include <mach/port.h>
63 #include <kern/lock.h>
64 #include <kern/kalloc.h>
66 #include <ipc/ipc_space.h>
67 #include <ipc/ipc_object.h>
68 #include <ipc/ipc_entry.h>
69 #include <ipc/ipc_hash.h>
70 #include <ipc/ipc_init.h>
72 #include <mach_ipc_debug.h>
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 #endif /* MACH_IPC_DEBUG */
82 * Forward declarations
85 /* Lookup (space, obj) in global hash table */
86 boolean_t
ipc_hash_global_lookup(
89 mach_port_name_t
*namep
,
90 ipc_tree_entry_t
*entryp
);
92 /* Insert an entry into the global reverse hash table */
93 void ipc_hash_global_insert(
96 mach_port_name_t name
,
97 ipc_tree_entry_t entry
);
99 /* Delete an entry from the local reverse hash table */
100 void ipc_hash_local_delete(
103 mach_port_index_t index
,
107 * Routine: ipc_hash_lookup
109 * Converts (space, obj) -> (name, entry).
110 * Returns TRUE if an entry was found.
112 * The space must be locked (read or write) throughout.
119 mach_port_name_t
*namep
,
124 rv
= ipc_hash_local_lookup(space
, obj
, namep
, entryp
);
126 assert(!is_fast_space(space
) || space
->is_tree_hash
== 0);
127 if (space
->is_tree_hash
> 0)
128 rv
= ipc_hash_global_lookup(space
, obj
, namep
,
129 (ipc_tree_entry_t
*) entryp
);
135 * Routine: ipc_hash_insert
137 * Inserts an entry into the appropriate reverse hash table,
138 * so that ipc_hash_lookup will find it.
140 * The space must be write-locked.
147 mach_port_name_t name
,
150 mach_port_index_t index
;
152 index
= MACH_PORT_INDEX(name
);
153 if ((index
< space
->is_table_size
) &&
154 (entry
== &space
->is_table
[index
]))
155 ipc_hash_local_insert(space
, obj
, index
, entry
);
157 assert(!is_fast_space(space
));
158 ipc_hash_global_insert(space
, obj
, name
,
159 (ipc_tree_entry_t
) entry
);
164 * Routine: ipc_hash_delete
166 * Deletes an entry from the appropriate reverse hash table.
168 * The space must be write-locked.
175 mach_port_name_t name
,
178 mach_port_index_t index
;
180 index
= MACH_PORT_INDEX(name
);
181 if ((index
< space
->is_table_size
) &&
182 (entry
== &space
->is_table
[index
]))
183 ipc_hash_local_delete(space
, obj
, index
, entry
);
185 assert(!is_fast_space(space
));
186 ipc_hash_global_delete(space
, obj
, name
,
187 (ipc_tree_entry_t
) entry
);
192 * The global reverse hash table holds splay tree entries.
193 * It is a simple open-chaining hash table with singly-linked buckets.
194 * Each bucket is locked separately, with an exclusive lock.
195 * Within each bucket, move-to-front is used.
198 typedef natural_t ipc_hash_index_t
;
200 ipc_hash_index_t ipc_hash_global_size
;
201 ipc_hash_index_t ipc_hash_global_mask
;
203 #define IH_GLOBAL_HASH(space, obj) \
204 (((((ipc_hash_index_t) ((vm_offset_t)space)) >> 4) + \
205 (((ipc_hash_index_t) ((vm_offset_t)obj)) >> 6)) & \
206 ipc_hash_global_mask)
208 typedef struct ipc_hash_global_bucket
{
209 decl_mutex_data(, ihgb_lock_data
)
210 ipc_tree_entry_t ihgb_head
;
211 } *ipc_hash_global_bucket_t
;
213 #define IHGB_NULL ((ipc_hash_global_bucket_t) 0)
215 #define ihgb_lock_init(ihgb) mutex_init(&(ihgb)->ihgb_lock_data, 0)
216 #define ihgb_lock(ihgb) mutex_lock(&(ihgb)->ihgb_lock_data)
217 #define ihgb_unlock(ihgb) mutex_unlock(&(ihgb)->ihgb_lock_data)
219 ipc_hash_global_bucket_t ipc_hash_global_table
;
222 * Routine: ipc_hash_global_lookup
224 * Converts (space, obj) -> (name, entry).
225 * Looks in the global table, for splay tree entries.
226 * Returns TRUE if an entry was found.
228 * The space must be locked (read or write) throughout.
232 ipc_hash_global_lookup(
235 mach_port_name_t
*namep
,
236 ipc_tree_entry_t
*entryp
)
238 ipc_hash_global_bucket_t bucket
;
239 ipc_tree_entry_t
this, *last
;
241 assert(space
!= IS_NULL
);
242 assert(obj
!= IO_NULL
);
244 assert(!is_fast_space(space
));
245 bucket
= &ipc_hash_global_table
[IH_GLOBAL_HASH(space
, obj
)];
248 if ((this = bucket
->ihgb_head
) != ITE_NULL
) {
249 if ((this->ite_object
== obj
) &&
250 (this->ite_space
== space
)) {
251 /* found it at front; no need to move */
253 *namep
= this->ite_name
;
255 } else for (last
= &this->ite_next
;
256 (this = *last
) != ITE_NULL
;
257 last
= &this->ite_next
) {
258 if ((this->ite_object
== obj
) &&
259 (this->ite_space
== space
)) {
260 /* found it; move to front */
262 *last
= this->ite_next
;
263 this->ite_next
= bucket
->ihgb_head
;
264 bucket
->ihgb_head
= this;
266 *namep
= this->ite_name
;
274 return this != ITE_NULL
;
278 * Routine: ipc_hash_global_insert
280 * Inserts an entry into the global reverse hash table.
282 * The space must be write-locked.
286 ipc_hash_global_insert(
289 __assert_only mach_port_name_t name
,
290 ipc_tree_entry_t entry
)
292 ipc_hash_global_bucket_t bucket
;
294 assert(!is_fast_space(space
));
295 assert(entry
->ite_name
== name
);
296 assert(space
!= IS_NULL
);
297 assert(entry
->ite_space
== space
);
298 assert(obj
!= IO_NULL
);
299 assert(entry
->ite_object
== obj
);
301 space
->is_tree_hash
++;
302 assert(space
->is_tree_hash
<= space
->is_tree_total
);
304 bucket
= &ipc_hash_global_table
[IH_GLOBAL_HASH(space
, obj
)];
307 /* insert at front of bucket */
309 entry
->ite_next
= bucket
->ihgb_head
;
310 bucket
->ihgb_head
= entry
;
316 * Routine: ipc_hash_global_delete
318 * Deletes an entry from the global reverse hash table.
320 * The space must be write-locked.
324 ipc_hash_global_delete(
327 __assert_only mach_port_name_t name
,
328 ipc_tree_entry_t entry
)
330 ipc_hash_global_bucket_t bucket
;
331 ipc_tree_entry_t
this, *last
;
333 assert(!is_fast_space(space
));
334 assert(entry
->ite_name
== name
);
335 assert(space
!= IS_NULL
);
336 assert(entry
->ite_space
== space
);
337 assert(obj
!= IO_NULL
);
338 assert(entry
->ite_object
== obj
);
340 assert(space
->is_tree_hash
> 0);
341 space
->is_tree_hash
--;
343 bucket
= &ipc_hash_global_table
[IH_GLOBAL_HASH(space
, obj
)];
346 for (last
= &bucket
->ihgb_head
;
347 (this = *last
) != ITE_NULL
;
348 last
= &this->ite_next
) {
350 /* found it; remove from bucket */
352 *last
= this->ite_next
;
356 assert(this != ITE_NULL
);
362 * Each space has a local reverse hash table, which holds
363 * entries from the space's table. In fact, the hash table
364 * just uses a field (ie_index) in the table itself.
366 * The local hash table is an open-addressing hash table,
367 * which means that when a collision occurs, instead of
368 * throwing the entry into a bucket, the entry is rehashed
369 * to another position in the table. In this case the rehash
370 * is very simple: linear probing (ie, just increment the position).
371 * This simple rehash makes deletions tractable (they're still a pain),
372 * but it means that collisions tend to build up into clumps.
374 * Because at least one entry in the table (index 0) is always unused,
375 * there will always be room in the reverse hash table. If a table
376 * with n slots gets completely full, the reverse hash table will
377 * have one giant clump of n-1 slots and one free slot somewhere.
378 * Because entries are only entered into the reverse table if they
379 * are pure send rights (not receive, send-once, port-set,
380 * or dead-name rights), and free entries of course aren't entered,
381 * I expect the reverse hash table won't get unreasonably full.
383 * Ordered hash tables (Amble & Knuth, Computer Journal, v. 17, no. 2,
384 * pp. 135-142.) may be desirable here. They can dramatically help
385 * unsuccessful lookups. But unsuccessful lookups are almost always
386 * followed by insertions, and those slow down somewhat. They
387 * also can help deletions somewhat. Successful lookups aren't affected.
388 * So possibly a small win; probably nothing significant.
391 #define IH_LOCAL_HASH(obj, size) \
392 ((((mach_port_index_t) (obj)) >> 6) % (size))
395 * Routine: ipc_hash_local_lookup
397 * Converts (space, obj) -> (name, entry).
398 * Looks in the space's local table, for table entries.
399 * Returns TRUE if an entry was found.
401 * The space must be locked (read or write) throughout.
405 ipc_hash_local_lookup(
408 mach_port_name_t
*namep
,
412 ipc_entry_num_t size
;
413 mach_port_index_t hindex
, index
;
415 assert(space
!= IS_NULL
);
416 assert(obj
!= IO_NULL
);
418 table
= space
->is_table
;
419 size
= space
->is_table_size
;
420 hindex
= IH_LOCAL_HASH(obj
, size
);
423 * Ideally, table[hindex].ie_index is the name we want.
424 * However, must check ie_object to verify this,
425 * because collisions can happen. In case of a collision,
426 * search farther along in the clump.
429 while ((index
= table
[hindex
].ie_index
) != 0) {
430 ipc_entry_t entry
= &table
[index
];
432 if (entry
->ie_object
== obj
) {
434 *namep
= MACH_PORT_MAKE(index
,
435 IE_BITS_GEN(entry
->ie_bits
));
439 if (++hindex
== size
)
447 * Routine: ipc_hash_local_insert
449 * Inserts an entry into the space's reverse hash table.
451 * The space must be write-locked.
455 ipc_hash_local_insert(
458 mach_port_index_t index
,
459 __assert_only ipc_entry_t entry
)
462 ipc_entry_num_t size
;
463 mach_port_index_t hindex
;
466 assert(space
!= IS_NULL
);
467 assert(obj
!= IO_NULL
);
469 table
= space
->is_table
;
470 size
= space
->is_table_size
;
471 hindex
= IH_LOCAL_HASH(obj
, size
);
473 assert(entry
== &table
[index
]);
474 assert(entry
->ie_object
== obj
);
477 * We want to insert at hindex, but there may be collisions.
478 * If a collision occurs, search for the end of the clump
482 while (table
[hindex
].ie_index
!= 0) {
483 if (++hindex
== size
)
487 table
[hindex
].ie_index
= index
;
491 * Routine: ipc_hash_local_delete
493 * Deletes an entry from the space's reverse hash table.
495 * The space must be write-locked.
499 ipc_hash_local_delete(
502 mach_port_index_t index
,
503 __assert_only ipc_entry_t entry
)
506 ipc_entry_num_t size
;
507 mach_port_index_t hindex
, dindex
;
509 assert(index
!= MACH_PORT_NULL
);
510 assert(space
!= IS_NULL
);
511 assert(obj
!= IO_NULL
);
513 table
= space
->is_table
;
514 size
= space
->is_table_size
;
515 hindex
= IH_LOCAL_HASH(obj
, size
);
517 assert(entry
== &table
[index
]);
518 assert(entry
->ie_object
== obj
);
521 * First check we have the right hindex for this index.
522 * In case of collision, we have to search farther
523 * along in this clump.
526 while (table
[hindex
].ie_index
!= index
) {
527 if (++hindex
== size
)
532 * Now we want to set table[hindex].ie_index = 0.
533 * But if we aren't the last index in a clump,
534 * this might cause problems for lookups of objects
535 * farther along in the clump that are displaced
536 * due to collisions. Searches for them would fail
537 * at hindex instead of succeeding.
539 * So we must check the clump after hindex for objects
540 * that are so displaced, and move one up to the new hole.
542 * hindex - index of new hole in the clump
543 * dindex - index we are checking for a displaced object
545 * When we move a displaced object up into the hole,
546 * it creates a new hole, and we have to repeat the process
547 * until we get to the end of the clump.
550 for (dindex
= hindex
; index
!= 0; hindex
= dindex
) {
552 mach_port_index_t tindex
;
555 if (++dindex
== size
)
557 assert(dindex
!= hindex
);
559 /* are we at the end of the clump? */
561 index
= table
[dindex
].ie_index
;
565 /* is this a displaced object? */
567 tobj
= table
[index
].ie_object
;
568 assert(tobj
!= IO_NULL
);
569 tindex
= IH_LOCAL_HASH(tobj
, size
);
571 if ((dindex
< hindex
) ?
572 ((dindex
< tindex
) && (tindex
<= hindex
)) :
573 ((dindex
< tindex
) || (tindex
<= hindex
)))
577 table
[hindex
].ie_index
= index
;
582 * Routine: ipc_hash_init
584 * Initialize the reverse hash table implementation.
592 /* if not configured, initialize ipc_hash_global_size */
594 if (ipc_hash_global_size
== 0) {
595 ipc_hash_global_size
= ipc_tree_entry_max
>> 8;
596 if (ipc_hash_global_size
< 32)
597 ipc_hash_global_size
= 32;
600 /* make sure it is a power of two */
602 ipc_hash_global_mask
= ipc_hash_global_size
- 1;
603 if ((ipc_hash_global_size
& ipc_hash_global_mask
) != 0) {
606 /* round up to closest power of two */
608 for (bit
= 1;; bit
<<= 1) {
609 ipc_hash_global_mask
|= bit
;
610 ipc_hash_global_size
= ipc_hash_global_mask
+ 1;
612 if ((ipc_hash_global_size
& ipc_hash_global_mask
) == 0)
617 /* allocate ipc_hash_global_table */
619 ipc_hash_global_table
= (ipc_hash_global_bucket_t
)
620 kalloc((vm_size_t
) (ipc_hash_global_size
*
621 sizeof(struct ipc_hash_global_bucket
)));
622 assert(ipc_hash_global_table
!= IHGB_NULL
);
624 /* and initialize it */
626 for (i
= 0; i
< ipc_hash_global_size
; i
++) {
627 ipc_hash_global_bucket_t bucket
;
629 bucket
= &ipc_hash_global_table
[i
];
630 ihgb_lock_init(bucket
);
631 bucket
->ihgb_head
= ITE_NULL
;
638 * Routine: ipc_hash_info
640 * Return information about the global reverse hash table.
641 * Fills the buffer with as much information as possible
642 * and returns the desired size of the buffer.
644 * Nothing locked. The caller should provide
645 * possibly-pageable memory.
651 hash_info_bucket_t
*info
,
652 mach_msg_type_number_t count
)
656 if (ipc_hash_global_size
< count
)
657 count
= ipc_hash_global_size
;
659 for (i
= 0; i
< count
; i
++) {
660 ipc_hash_global_bucket_t bucket
= &ipc_hash_global_table
[i
];
661 unsigned int bucket_count
= 0;
662 ipc_tree_entry_t entry
;
665 for (entry
= bucket
->ihgb_head
;
667 entry
= entry
->ite_next
)
671 /* don't touch pageable memory while holding locks */
672 info
[i
].hib_count
= bucket_count
;
675 return ipc_hash_global_size
;
678 #endif /* MACH_IPC_DEBUG */