|
|
1.1 ! root 1: /* ! 2: * Mach Operating System ! 3: * Copyright (c) 1991,1990,1989 Carnegie Mellon University ! 4: * All Rights Reserved. ! 5: * ! 6: * Permission to use, copy, modify and distribute this software and its ! 7: * documentation is hereby granted, provided that both the copyright ! 8: * notice and this permission notice appear in all copies of the ! 9: * software, derivative works or modified versions, and any portions ! 10: * thereof, and that both notices appear in supporting documentation. ! 11: * ! 12: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" ! 13: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR ! 14: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. ! 15: * ! 16: * Carnegie Mellon requests users of this software to return to ! 17: * ! 18: * Software Distribution Coordinator or [email protected] ! 19: * School of Computer Science ! 20: * Carnegie Mellon University ! 21: * Pittsburgh PA 15213-3890 ! 22: * ! 23: * any improvements or extensions that they make and grant Carnegie Mellon ! 24: * the rights to redistribute these changes. ! 25: */ ! 26: /* ! 27: * File: ipc/ipc_hash.c ! 28: * Author: Rich Draves ! 29: * Date: 1989 ! 30: * ! 31: * Entry hash table operations. ! 32: */ ! 33: ! 34: #include <mach/boolean.h> ! 35: #include <mach/port.h> ! 36: #include <kern/lock.h> ! 37: #include <kern/kalloc.h> ! 38: #include <ipc/port.h> ! 39: #include <ipc/ipc_space.h> ! 40: #include <ipc/ipc_object.h> ! 41: #include <ipc/ipc_entry.h> ! 42: #include <ipc/ipc_hash.h> ! 43: #include <ipc/ipc_init.h> ! 44: #include <ipc/ipc_types.h> ! 45: ! 46: #include <mach_ipc_debug.h> ! 47: #if MACH_IPC_DEBUG ! 48: #include <mach/kern_return.h> ! 49: #include <mach_debug/hash_info.h> ! 50: #include <vm/vm_map.h> ! 51: #include <vm/vm_kern.h> ! 52: #include <vm/vm_user.h> ! 53: #endif ! 54: ! 55: ! 56: ! 57: /* ! 58: * Routine: ipc_hash_lookup ! 59: * Purpose: ! 60: * Converts (space, obj) -> (name, entry). ! 61: * Returns TRUE if an entry was found. ! 62: * Conditions: ! 63: * The space must be locked (read or write) throughout. ! 64: */ ! 65: ! 66: boolean_t ! 67: ipc_hash_lookup(space, obj, namep, entryp) ! 68: ipc_space_t space; ! 69: ipc_object_t obj; ! 70: mach_port_t *namep; ! 71: ipc_entry_t *entryp; ! 72: { ! 73: return (ipc_hash_local_lookup(space, obj, namep, entryp) || ! 74: ((space->is_tree_hash > 0) && ! 75: ipc_hash_global_lookup(space, obj, namep, ! 76: (ipc_tree_entry_t *) entryp))); ! 77: } ! 78: ! 79: /* ! 80: * Routine: ipc_hash_insert ! 81: * Purpose: ! 82: * Inserts an entry into the appropriate reverse hash table, ! 83: * so that ipc_hash_lookup will find it. ! 84: * Conditions: ! 85: * The space must be write-locked. ! 86: */ ! 87: ! 88: void ! 89: ipc_hash_insert( ! 90: ipc_space_t space, ! 91: ipc_object_t obj, ! 92: mach_port_t name, ! 93: ipc_entry_t entry) ! 94: { ! 95: mach_port_index_t index; ! 96: ! 97: index = MACH_PORT_INDEX(name); ! 98: if ((index < space->is_table_size) && ! 99: (entry == &space->is_table[index])) ! 100: ipc_hash_local_insert(space, obj, index, entry); ! 101: else ! 102: ipc_hash_global_insert(space, obj, name, ! 103: (ipc_tree_entry_t) entry); ! 104: } ! 105: ! 106: /* ! 107: * Routine: ipc_hash_delete ! 108: * Purpose: ! 109: * Deletes an entry from the appropriate reverse hash table. ! 110: * Conditions: ! 111: * The space must be write-locked. ! 112: */ ! 113: ! 114: void ! 115: ipc_hash_delete( ! 116: ipc_space_t space, ! 117: ipc_object_t obj, ! 118: mach_port_t name, ! 119: ipc_entry_t entry) ! 120: { ! 121: mach_port_index_t index; ! 122: ! 123: index = MACH_PORT_INDEX(name); ! 124: if ((index < space->is_table_size) && ! 125: (entry == &space->is_table[index])) ! 126: ipc_hash_local_delete(space, obj, index, entry); ! 127: else ! 128: ipc_hash_global_delete(space, obj, name, ! 129: (ipc_tree_entry_t) entry); ! 130: } ! 131: ! 132: /* ! 133: * The global reverse hash table holds splay tree entries. ! 134: * It is a simple open-chaining hash table with singly-linked buckets. ! 135: * Each bucket is locked separately, with an exclusive lock. ! 136: * Within each bucket, move-to-front is used. ! 137: */ ! 138: ! 139: typedef natural_t ipc_hash_index_t; ! 140: ! 141: ipc_hash_index_t ipc_hash_global_size; ! 142: ipc_hash_index_t ipc_hash_global_mask; ! 143: ! 144: #define IH_GLOBAL_HASH(space, obj) \ ! 145: (((((ipc_hash_index_t) ((vm_offset_t)space)) >> 4) + \ ! 146: (((ipc_hash_index_t) ((vm_offset_t)obj)) >> 6)) & \ ! 147: ipc_hash_global_mask) ! 148: ! 149: typedef struct ipc_hash_global_bucket { ! 150: decl_simple_lock_data(, ihgb_lock_data) ! 151: ipc_tree_entry_t ihgb_head; ! 152: } *ipc_hash_global_bucket_t; ! 153: ! 154: #define IHGB_NULL ((ipc_hash_global_bucket_t) 0) ! 155: ! 156: #define ihgb_lock_init(ihgb) simple_lock_init(&(ihgb)->ihgb_lock_data) ! 157: #define ihgb_lock(ihgb) simple_lock(&(ihgb)->ihgb_lock_data) ! 158: #define ihgb_unlock(ihgb) simple_unlock(&(ihgb)->ihgb_lock_data) ! 159: ! 160: ipc_hash_global_bucket_t ipc_hash_global_table; ! 161: ! 162: /* ! 163: * Routine: ipc_hash_global_lookup ! 164: * Purpose: ! 165: * Converts (space, obj) -> (name, entry). ! 166: * Looks in the global table, for splay tree entries. ! 167: * Returns TRUE if an entry was found. ! 168: * Conditions: ! 169: * The space must be locked (read or write) throughout. ! 170: */ ! 171: ! 172: boolean_t ! 173: ipc_hash_global_lookup( ! 174: ipc_space_t space, ! 175: ipc_object_t obj, ! 176: mach_port_t *namep, ! 177: ipc_tree_entry_t *entryp) ! 178: { ! 179: ipc_hash_global_bucket_t bucket; ! 180: ipc_tree_entry_t this, *last; ! 181: ! 182: assert(space != IS_NULL); ! 183: assert(obj != IO_NULL); ! 184: ! 185: bucket = &ipc_hash_global_table[IH_GLOBAL_HASH(space, obj)]; ! 186: ihgb_lock(bucket); ! 187: ! 188: if ((this = bucket->ihgb_head) != ITE_NULL) { ! 189: if ((this->ite_object == obj) && ! 190: (this->ite_space == space)) { ! 191: /* found it at front; no need to move */ ! 192: ! 193: *namep = this->ite_name; ! 194: *entryp = this; ! 195: } else for (last = &this->ite_next; ! 196: (this = *last) != ITE_NULL; ! 197: last = &this->ite_next) { ! 198: if ((this->ite_object == obj) && ! 199: (this->ite_space == space)) { ! 200: /* found it; move to front */ ! 201: ! 202: *last = this->ite_next; ! 203: this->ite_next = bucket->ihgb_head; ! 204: bucket->ihgb_head = this; ! 205: ! 206: *namep = this->ite_name; ! 207: *entryp = this; ! 208: break; ! 209: } ! 210: } ! 211: } ! 212: ! 213: ihgb_unlock(bucket); ! 214: return this != ITE_NULL; ! 215: } ! 216: ! 217: /* ! 218: * Routine: ipc_hash_global_insert ! 219: * Purpose: ! 220: * Inserts an entry into the global reverse hash table. ! 221: * Conditions: ! 222: * The space must be write-locked. ! 223: */ ! 224: ! 225: void ! 226: ipc_hash_global_insert( ! 227: ipc_space_t space, ! 228: ipc_object_t obj, ! 229: mach_port_t name, ! 230: ipc_tree_entry_t entry) ! 231: { ! 232: ipc_hash_global_bucket_t bucket; ! 233: ! 234: ! 235: assert(entry->ite_name == name); ! 236: assert(space != IS_NULL); ! 237: assert(entry->ite_space == space); ! 238: assert(obj != IO_NULL); ! 239: assert(entry->ite_object == obj); ! 240: ! 241: space->is_tree_hash++; ! 242: assert(space->is_tree_hash <= space->is_tree_total); ! 243: ! 244: bucket = &ipc_hash_global_table[IH_GLOBAL_HASH(space, obj)]; ! 245: ihgb_lock(bucket); ! 246: ! 247: /* insert at front of bucket */ ! 248: ! 249: entry->ite_next = bucket->ihgb_head; ! 250: bucket->ihgb_head = entry; ! 251: ! 252: ihgb_unlock(bucket); ! 253: } ! 254: ! 255: /* ! 256: * Routine: ipc_hash_global_delete ! 257: * Purpose: ! 258: * Deletes an entry from the global reverse hash table. ! 259: * Conditions: ! 260: * The space must be write-locked. ! 261: */ ! 262: ! 263: void ! 264: ipc_hash_global_delete( ! 265: ipc_space_t space, ! 266: ipc_object_t obj, ! 267: mach_port_t name, ! 268: ipc_tree_entry_t entry) ! 269: { ! 270: ipc_hash_global_bucket_t bucket; ! 271: ipc_tree_entry_t this, *last; ! 272: ! 273: assert(entry->ite_name == name); ! 274: assert(space != IS_NULL); ! 275: assert(entry->ite_space == space); ! 276: assert(obj != IO_NULL); ! 277: assert(entry->ite_object == obj); ! 278: ! 279: assert(space->is_tree_hash > 0); ! 280: space->is_tree_hash--; ! 281: ! 282: bucket = &ipc_hash_global_table[IH_GLOBAL_HASH(space, obj)]; ! 283: ihgb_lock(bucket); ! 284: ! 285: for (last = &bucket->ihgb_head; ! 286: (this = *last) != ITE_NULL; ! 287: last = &this->ite_next) { ! 288: if (this == entry) { ! 289: /* found it; remove from bucket */ ! 290: ! 291: *last = this->ite_next; ! 292: break; ! 293: } ! 294: } ! 295: assert(this != ITE_NULL); ! 296: ! 297: ihgb_unlock(bucket); ! 298: } ! 299: ! 300: /* ! 301: * Each space has a local reverse hash table, which holds ! 302: * entries from the space's table. In fact, the hash table ! 303: * just uses a field (ie_index) in the table itself. ! 304: * ! 305: * The local hash table is an open-addressing hash table, ! 306: * which means that when a collision occurs, instead of ! 307: * throwing the entry into a bucket, the entry is rehashed ! 308: * to another position in the table. In this case the rehash ! 309: * is very simple: linear probing (ie, just increment the position). ! 310: * This simple rehash makes deletions tractable (they're still a pain), ! 311: * but it means that collisions tend to build up into clumps. ! 312: * ! 313: * Because at least one entry in the table (index 0) is always unused, ! 314: * there will always be room in the reverse hash table. If a table ! 315: * with n slots gets completely full, the reverse hash table will ! 316: * have one giant clump of n-1 slots and one free slot somewhere. ! 317: * Because entries are only entered into the reverse table if they ! 318: * are pure send rights (not receive, send-once, port-set, ! 319: * or dead-name rights), and free entries of course aren't entered, ! 320: * I expect the reverse hash table won't get unreasonably full. ! 321: * ! 322: * Ordered hash tables (Amble & Knuth, Computer Journal, v. 17, no. 2, ! 323: * pp. 135-142.) may be desirable here. They can dramatically help ! 324: * unsuccessful lookups. But unsuccessful lookups are almost always ! 325: * followed by insertions, and those slow down somewhat. They ! 326: * also can help deletions somewhat. Successful lookups aren't affected. ! 327: * So possibly a small win; probably nothing significant. ! 328: */ ! 329: ! 330: #define IH_LOCAL_HASH(obj, size) \ ! 331: ((((mach_port_index_t) (obj)) >> 6) % (size)) ! 332: ! 333: /* ! 334: * Routine: ipc_hash_local_lookup ! 335: * Purpose: ! 336: * Converts (space, obj) -> (name, entry). ! 337: * Looks in the space's local table, for table entries. ! 338: * Returns TRUE if an entry was found. ! 339: * Conditions: ! 340: * The space must be locked (read or write) throughout. ! 341: */ ! 342: ! 343: boolean_t ! 344: ipc_hash_local_lookup( ! 345: ipc_space_t space, ! 346: ipc_object_t obj, ! 347: mach_port_t *namep, ! 348: ipc_entry_t *entryp) ! 349: { ! 350: ipc_entry_t table; ! 351: ipc_entry_num_t size; ! 352: mach_port_index_t hindex, index; ! 353: ! 354: assert(space != IS_NULL); ! 355: assert(obj != IO_NULL); ! 356: ! 357: table = space->is_table; ! 358: size = space->is_table_size; ! 359: hindex = IH_LOCAL_HASH(obj, size); ! 360: ! 361: /* ! 362: * Ideally, table[hindex].ie_index is the name we want. ! 363: * However, must check ie_object to verify this, ! 364: * because collisions can happen. In case of a collision, ! 365: * search farther along in the clump. ! 366: */ ! 367: ! 368: while ((index = table[hindex].ie_index) != 0) { ! 369: ipc_entry_t entry = &table[index]; ! 370: ! 371: if (entry->ie_object == obj) { ! 372: *namep = MACH_PORT_MAKEB(index, entry->ie_bits); ! 373: *entryp = entry; ! 374: return TRUE; ! 375: } ! 376: ! 377: if (++hindex == size) ! 378: hindex = 0; ! 379: } ! 380: ! 381: return FALSE; ! 382: } ! 383: ! 384: /* ! 385: * Routine: ipc_hash_local_insert ! 386: * Purpose: ! 387: * Inserts an entry into the space's reverse hash table. ! 388: * Conditions: ! 389: * The space must be write-locked. ! 390: */ ! 391: ! 392: void ! 393: ipc_hash_local_insert( ! 394: ipc_space_t space, ! 395: ipc_object_t obj, ! 396: mach_port_index_t index, ! 397: ipc_entry_t entry) ! 398: { ! 399: ipc_entry_t table; ! 400: ipc_entry_num_t size; ! 401: mach_port_index_t hindex; ! 402: ! 403: assert(index != 0); ! 404: assert(space != IS_NULL); ! 405: assert(obj != IO_NULL); ! 406: ! 407: table = space->is_table; ! 408: size = space->is_table_size; ! 409: hindex = IH_LOCAL_HASH(obj, size); ! 410: ! 411: assert(entry == &table[index]); ! 412: assert(entry->ie_object == obj); ! 413: ! 414: /* ! 415: * We want to insert at hindex, but there may be collisions. ! 416: * If a collision occurs, search for the end of the clump ! 417: * and insert there. ! 418: */ ! 419: ! 420: while (table[hindex].ie_index != 0) { ! 421: if (++hindex == size) ! 422: hindex = 0; ! 423: } ! 424: ! 425: table[hindex].ie_index = index; ! 426: } ! 427: ! 428: /* ! 429: * Routine: ipc_hash_local_delete ! 430: * Purpose: ! 431: * Deletes an entry from the space's reverse hash table. ! 432: * Conditions: ! 433: * The space must be write-locked. ! 434: */ ! 435: ! 436: void ! 437: ipc_hash_local_delete( ! 438: ipc_space_t space, ! 439: ipc_object_t obj, ! 440: mach_port_index_t index, ! 441: ipc_entry_t entry) ! 442: { ! 443: ipc_entry_t table; ! 444: ipc_entry_num_t size; ! 445: mach_port_index_t hindex, dindex; ! 446: ! 447: assert(index != MACH_PORT_NULL); ! 448: assert(space != IS_NULL); ! 449: assert(obj != IO_NULL); ! 450: ! 451: table = space->is_table; ! 452: size = space->is_table_size; ! 453: hindex = IH_LOCAL_HASH(obj, size); ! 454: ! 455: assert(entry == &table[index]); ! 456: assert(entry->ie_object == obj); ! 457: ! 458: /* ! 459: * First check we have the right hindex for this index. ! 460: * In case of collision, we have to search farther ! 461: * along in this clump. ! 462: */ ! 463: ! 464: while (table[hindex].ie_index != index) { ! 465: if (table[hindex].ie_index == 0) ! 466: { ! 467: static int gak = 0; ! 468: if (gak == 0) ! 469: { ! 470: printf("gak! entry wasn't in hash table!\n"); ! 471: gak = 1; ! 472: } ! 473: return; ! 474: } ! 475: if (++hindex == size) ! 476: hindex = 0; ! 477: } ! 478: ! 479: /* ! 480: * Now we want to set table[hindex].ie_index = 0. ! 481: * But if we aren't the last index in a clump, ! 482: * this might cause problems for lookups of objects ! 483: * farther along in the clump that are displaced ! 484: * due to collisions. Searches for them would fail ! 485: * at hindex instead of succeeding. ! 486: * ! 487: * So we must check the clump after hindex for objects ! 488: * that are so displaced, and move one up to the new hole. ! 489: * ! 490: * hindex - index of new hole in the clump ! 491: * dindex - index we are checking for a displaced object ! 492: * ! 493: * When we move a displaced object up into the hole, ! 494: * it creates a new hole, and we have to repeat the process ! 495: * until we get to the end of the clump. ! 496: */ ! 497: ! 498: for (dindex = hindex; index != 0; hindex = dindex) { ! 499: for (;;) { ! 500: mach_port_index_t tindex; ! 501: ipc_object_t tobj; ! 502: ! 503: if (++dindex == size) ! 504: dindex = 0; ! 505: assert(dindex != hindex); ! 506: ! 507: /* are we at the end of the clump? */ ! 508: ! 509: index = table[dindex].ie_index; ! 510: if (index == 0) ! 511: break; ! 512: ! 513: /* is this a displaced object? */ ! 514: ! 515: tobj = table[index].ie_object; ! 516: assert(tobj != IO_NULL); ! 517: tindex = IH_LOCAL_HASH(tobj, size); ! 518: ! 519: if ((dindex < hindex) ? ! 520: ((dindex < tindex) && (tindex <= hindex)) : ! 521: ((dindex < tindex) || (tindex <= hindex))) ! 522: break; ! 523: } ! 524: ! 525: table[hindex].ie_index = index; ! 526: } ! 527: } ! 528: ! 529: /* ! 530: * Routine: ipc_hash_init ! 531: * Purpose: ! 532: * Initialize the reverse hash table implementation. ! 533: */ ! 534: ! 535: void ! 536: ipc_hash_init(void) ! 537: { ! 538: ipc_hash_index_t i; ! 539: ! 540: /* if not configured, initialize ipc_hash_global_size */ ! 541: ! 542: if (ipc_hash_global_size == 0) { ! 543: ipc_hash_global_size = ipc_tree_entry_max >> 8; ! 544: if (ipc_hash_global_size < 32) ! 545: ipc_hash_global_size = 32; ! 546: } ! 547: ! 548: /* make sure it is a power of two */ ! 549: ! 550: ipc_hash_global_mask = ipc_hash_global_size - 1; ! 551: if ((ipc_hash_global_size & ipc_hash_global_mask) != 0) { ! 552: natural_t bit; ! 553: ! 554: /* round up to closest power of two */ ! 555: ! 556: for (bit = 1;; bit <<= 1) { ! 557: ipc_hash_global_mask |= bit; ! 558: ipc_hash_global_size = ipc_hash_global_mask + 1; ! 559: ! 560: if ((ipc_hash_global_size & ipc_hash_global_mask) == 0) ! 561: break; ! 562: } ! 563: } ! 564: ! 565: /* allocate ipc_hash_global_table */ ! 566: ! 567: ipc_hash_global_table = (ipc_hash_global_bucket_t) ! 568: kalloc((vm_size_t) (ipc_hash_global_size * ! 569: sizeof(struct ipc_hash_global_bucket))); ! 570: assert(ipc_hash_global_table != IHGB_NULL); ! 571: ! 572: /* and initialize it */ ! 573: ! 574: for (i = 0; i < ipc_hash_global_size; i++) { ! 575: ipc_hash_global_bucket_t bucket; ! 576: ! 577: bucket = &ipc_hash_global_table[i]; ! 578: ihgb_lock_init(bucket); ! 579: bucket->ihgb_head = ITE_NULL; ! 580: } ! 581: } ! 582: ! 583: #if MACH_IPC_DEBUG ! 584: ! 585: /* ! 586: * Routine: ipc_hash_info ! 587: * Purpose: ! 588: * Return information about the global reverse hash table. ! 589: * Fills the buffer with as much information as possible ! 590: * and returns the desired size of the buffer. ! 591: * Conditions: ! 592: * Nothing locked. The caller should provide ! 593: * possibly-pageable memory. ! 594: */ ! 595: ! 596: ! 597: ipc_hash_index_t ! 598: ipc_hash_info( ! 599: hash_info_bucket_t *info, ! 600: mach_msg_type_number_t count) ! 601: { ! 602: ipc_hash_index_t i; ! 603: ! 604: if (ipc_hash_global_size < count) ! 605: count = ipc_hash_global_size; ! 606: ! 607: for (i = 0; i < count; i++) { ! 608: ipc_hash_global_bucket_t bucket = &ipc_hash_global_table[i]; ! 609: unsigned int bucket_count = 0; ! 610: ipc_tree_entry_t entry; ! 611: ! 612: ihgb_lock(bucket); ! 613: for (entry = bucket->ihgb_head; ! 614: entry != ITE_NULL; ! 615: entry = entry->ite_next) ! 616: bucket_count++; ! 617: ihgb_unlock(bucket); ! 618: ! 619: /* don't touch pageable memory while holding locks */ ! 620: info[i].hib_count = bucket_count; ! 621: } ! 622: ! 623: return ipc_hash_global_size; ! 624: } ! 625: ! 626: #endif /* MACH_IPC_DEBUG */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.