ipc_hash.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  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 Software.Distribution@CS.CMU.EDU
  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. #pragma GCC diagnostic error "-Wundef"
  34. #include <glue/gnulinux.h>
  35. #include <kern/printf.h>
  36. #include <mach/boolean.h>
  37. #include <mach/port.h>
  38. #include <kern/lock.h>
  39. #include <kern/kalloc.h>
  40. #include <ipc/port.h>
  41. #include <ipc/ipc_space.h>
  42. #include <ipc/ipc_object.h>
  43. #include <ipc/ipc_entry.h>
  44. #include <ipc/ipc_hash.h>
  45. #include <ipc/ipc_init.h>
  46. #include <ipc/ipc_types.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. * Routine: ipc_hash_lookup
  56. * Purpose:
  57. * Converts (space, obj) -> (name, entry).
  58. * Returns TRUE if an entry was found.
  59. * Conditions:
  60. * The space must be locked (read or write) throughout.
  61. */
  62. boolean_t
  63. ipc_hash_lookup(space, obj, namep, entryp)
  64. ipc_space_t space;
  65. ipc_object_t obj;
  66. mach_port_t *namep;
  67. ipc_entry_t *entryp;
  68. {
  69. return (ipc_hash_local_lookup(space, obj, namep, entryp) ||
  70. ((space->is_tree_hash > 0) &&
  71. ipc_hash_global_lookup(space, obj, namep,
  72. (ipc_tree_entry_t *) entryp)));
  73. }
  74. /*
  75. * Routine: ipc_hash_insert
  76. * Purpose:
  77. * Inserts an entry into the appropriate reverse hash table,
  78. * so that ipc_hash_lookup will find it.
  79. * Conditions:
  80. * The space must be write-locked.
  81. */
  82. void
  83. ipc_hash_insert(
  84. ipc_space_t space,
  85. ipc_object_t obj,
  86. mach_port_t name,
  87. ipc_entry_t entry)
  88. {
  89. mach_port_index_t index;
  90. index = MACH_PORT_INDEX(name);
  91. if ((index < space->is_table_size) &&
  92. (entry == &space->is_table[index]))
  93. ipc_hash_local_insert(space, obj, index, entry);
  94. else
  95. ipc_hash_global_insert(space, obj, name,
  96. (ipc_tree_entry_t) entry);
  97. }
  98. /*
  99. * Routine: ipc_hash_delete
  100. * Purpose:
  101. * Deletes an entry from the appropriate reverse hash table.
  102. * Conditions:
  103. * The space must be write-locked.
  104. */
  105. void
  106. ipc_hash_delete(
  107. ipc_space_t space,
  108. ipc_object_t obj,
  109. mach_port_t name,
  110. ipc_entry_t entry)
  111. {
  112. mach_port_index_t index;
  113. index = MACH_PORT_INDEX(name);
  114. if ((index < space->is_table_size) &&
  115. (entry == &space->is_table[index]))
  116. ipc_hash_local_delete(space, obj, index, entry);
  117. else
  118. ipc_hash_global_delete(space, obj, name,
  119. (ipc_tree_entry_t) entry);
  120. }
  121. /*
  122. * The global reverse hash table holds splay tree entries.
  123. * It is a simple open-chaining hash table with singly-linked buckets.
  124. * Each bucket is locked separately, with an exclusive lock.
  125. * Within each bucket, move-to-front is used.
  126. */
  127. ipc_hash_index_t ipc_hash_global_size;
  128. ipc_hash_index_t ipc_hash_global_mask;
  129. #define IH_GLOBAL_HASH(space, obj) \
  130. (((((ipc_hash_index_t) ((vm_offset_t)space)) >> 4) + \
  131. (((ipc_hash_index_t) ((vm_offset_t)obj)) >> 6)) & \
  132. ipc_hash_global_mask)
  133. typedef struct ipc_hash_global_bucket {
  134. decl_simple_lock_data(, ihgb_lock_data)
  135. ipc_tree_entry_t ihgb_head;
  136. } *ipc_hash_global_bucket_t;
  137. #define IHGB_NULL ((ipc_hash_global_bucket_t) 0)
  138. #define ihgb_lock_init(ihgb) simple_lock_init(&(ihgb)->ihgb_lock_data)
  139. #define ihgb_lock(ihgb) simple_lock(&(ihgb)->ihgb_lock_data)
  140. #define ihgb_unlock(ihgb) simple_unlock(&(ihgb)->ihgb_lock_data)
  141. ipc_hash_global_bucket_t ipc_hash_global_table;
  142. /*
  143. * Routine: ipc_hash_global_lookup
  144. * Purpose:
  145. * Converts (space, obj) -> (name, entry).
  146. * Looks in the global table, for splay tree entries.
  147. * Returns TRUE if an entry was found.
  148. * Conditions:
  149. * The space must be locked (read or write) throughout.
  150. */
  151. boolean_t
  152. ipc_hash_global_lookup(
  153. ipc_space_t space,
  154. ipc_object_t obj,
  155. mach_port_t *namep,
  156. ipc_tree_entry_t *entryp)
  157. {
  158. ipc_hash_global_bucket_t bucket;
  159. ipc_tree_entry_t this, *last;
  160. assert(space != IS_NULL);
  161. assert(obj != IO_NULL);
  162. bucket = &ipc_hash_global_table[IH_GLOBAL_HASH(space, obj)];
  163. ihgb_lock(bucket);
  164. if ((this = bucket->ihgb_head) != ITE_NULL) {
  165. if ((this->ite_object == obj) &&
  166. (this->ite_space == space)) {
  167. /* found it at front; no need to move */
  168. *namep = this->ite_name;
  169. *entryp = this;
  170. } else for (last = &this->ite_next;
  171. (this = *last) != ITE_NULL;
  172. last = &this->ite_next) {
  173. if ((this->ite_object == obj) &&
  174. (this->ite_space == space)) {
  175. /* found it; move to front */
  176. *last = this->ite_next;
  177. this->ite_next = bucket->ihgb_head;
  178. bucket->ihgb_head = this;
  179. *namep = this->ite_name;
  180. *entryp = this;
  181. break;
  182. }
  183. }
  184. }
  185. ihgb_unlock(bucket);
  186. return this != ITE_NULL;
  187. }
  188. /*
  189. * Routine: ipc_hash_global_insert
  190. * Purpose:
  191. * Inserts an entry into the global reverse hash table.
  192. * Conditions:
  193. * The space must be write-locked.
  194. */
  195. void
  196. ipc_hash_global_insert(
  197. ipc_space_t space,
  198. ipc_object_t obj,
  199. mach_port_t name,
  200. ipc_tree_entry_t entry)
  201. {
  202. ipc_hash_global_bucket_t bucket;
  203. assert(entry->ite_name == name);
  204. assert(space != IS_NULL);
  205. assert(entry->ite_space == space);
  206. assert(obj != IO_NULL);
  207. assert(entry->ite_object == obj);
  208. space->is_tree_hash++;
  209. assert(space->is_tree_hash <= space->is_tree_total);
  210. bucket = &ipc_hash_global_table[IH_GLOBAL_HASH(space, obj)];
  211. ihgb_lock(bucket);
  212. /* insert at front of bucket */
  213. entry->ite_next = bucket->ihgb_head;
  214. bucket->ihgb_head = entry;
  215. ihgb_unlock(bucket);
  216. }
  217. /*
  218. * Routine: ipc_hash_global_delete
  219. * Purpose:
  220. * Deletes an entry from the global reverse hash table.
  221. * Conditions:
  222. * The space must be write-locked.
  223. */
  224. void
  225. ipc_hash_global_delete(
  226. ipc_space_t space,
  227. ipc_object_t obj,
  228. mach_port_t name,
  229. ipc_tree_entry_t entry)
  230. {
  231. ipc_hash_global_bucket_t bucket;
  232. ipc_tree_entry_t this, *last;
  233. assert(entry->ite_name == name);
  234. assert(space != IS_NULL);
  235. assert(entry->ite_space == space);
  236. assert(obj != IO_NULL);
  237. assert(entry->ite_object == obj);
  238. assert(space->is_tree_hash > 0);
  239. space->is_tree_hash--;
  240. bucket = &ipc_hash_global_table[IH_GLOBAL_HASH(space, obj)];
  241. ihgb_lock(bucket);
  242. for (last = &bucket->ihgb_head;
  243. (this = *last) != ITE_NULL;
  244. last = &this->ite_next) {
  245. if (this == entry) {
  246. /* found it; remove from bucket */
  247. *last = this->ite_next;
  248. break;
  249. }
  250. }
  251. assert(this != ITE_NULL);
  252. ihgb_unlock(bucket);
  253. }
  254. /*
  255. * Each space has a local reverse hash table, which holds
  256. * entries from the space's table. In fact, the hash table
  257. * just uses a field (ie_index) in the table itself.
  258. *
  259. * The local hash table is an open-addressing hash table,
  260. * which means that when a collision occurs, instead of
  261. * throwing the entry into a bucket, the entry is rehashed
  262. * to another position in the table. In this case the rehash
  263. * is very simple: linear probing (ie, just increment the position).
  264. * This simple rehash makes deletions tractable (they're still a pain),
  265. * but it means that collisions tend to build up into clumps.
  266. *
  267. * Because at least one entry in the table (index 0) is always unused,
  268. * there will always be room in the reverse hash table. If a table
  269. * with n slots gets completely full, the reverse hash table will
  270. * have one giant clump of n-1 slots and one free slot somewhere.
  271. * Because entries are only entered into the reverse table if they
  272. * are pure send rights (not receive, send-once, port-set,
  273. * or dead-name rights), and free entries of course aren't entered,
  274. * I expect the reverse hash table won't get unreasonably full.
  275. *
  276. * Ordered hash tables (Amble & Knuth, Computer Journal, v. 17, no. 2,
  277. * pp. 135-142.) may be desirable here. They can dramatically help
  278. * unsuccessful lookups. But unsuccessful lookups are almost always
  279. * followed by insertions, and those slow down somewhat. They
  280. * also can help deletions somewhat. Successful lookups aren't affected.
  281. * So possibly a small win; probably nothing significant.
  282. */
  283. #define IH_LOCAL_HASH(obj, size) \
  284. ((((mach_port_index_t) (vm_offset_t) (obj)) >> 6) % (size))
  285. /*
  286. * Routine: ipc_hash_local_lookup
  287. * Purpose:
  288. * Converts (space, obj) -> (name, entry).
  289. * Looks in the space's local table, for table entries.
  290. * Returns TRUE if an entry was found.
  291. * Conditions:
  292. * The space must be locked (read or write) throughout.
  293. */
  294. boolean_t
  295. ipc_hash_local_lookup(
  296. ipc_space_t space,
  297. ipc_object_t obj,
  298. mach_port_t *namep,
  299. ipc_entry_t *entryp)
  300. {
  301. ipc_entry_t table;
  302. ipc_entry_num_t size;
  303. mach_port_index_t hindex, index;
  304. assert(space != IS_NULL);
  305. assert(obj != IO_NULL);
  306. table = space->is_table;
  307. size = space->is_table_size;
  308. hindex = IH_LOCAL_HASH(obj, size);
  309. /*
  310. * Ideally, table[hindex].ie_index is the name we want.
  311. * However, must check ie_object to verify this,
  312. * because collisions can happen. In case of a collision,
  313. * search farther along in the clump.
  314. */
  315. while ((index = table[hindex].ie_index) != 0) {
  316. ipc_entry_t entry = &table[index];
  317. if (entry->ie_object == obj) {
  318. *namep = MACH_PORT_MAKEB(index, entry->ie_bits);
  319. *entryp = entry;
  320. return TRUE;
  321. }
  322. if (++hindex == size)
  323. hindex = 0;
  324. }
  325. return FALSE;
  326. }
  327. /*
  328. * Routine: ipc_hash_local_insert
  329. * Purpose:
  330. * Inserts an entry into the space's reverse hash table.
  331. * Conditions:
  332. * The space must be write-locked.
  333. */
  334. void
  335. ipc_hash_local_insert(
  336. ipc_space_t space,
  337. ipc_object_t obj,
  338. mach_port_index_t index,
  339. ipc_entry_t entry)
  340. {
  341. ipc_entry_t table;
  342. ipc_entry_num_t size;
  343. mach_port_index_t hindex;
  344. assert(index != 0);
  345. assert(space != IS_NULL);
  346. assert(obj != IO_NULL);
  347. table = space->is_table;
  348. size = space->is_table_size;
  349. hindex = IH_LOCAL_HASH(obj, size);
  350. assert(entry == &table[index]);
  351. assert(entry->ie_object == obj);
  352. /*
  353. * We want to insert at hindex, but there may be collisions.
  354. * If a collision occurs, search for the end of the clump
  355. * and insert there.
  356. */
  357. while (table[hindex].ie_index != 0) {
  358. if (++hindex == size)
  359. hindex = 0;
  360. }
  361. table[hindex].ie_index = index;
  362. }
  363. /*
  364. * Routine: ipc_hash_local_delete
  365. * Purpose:
  366. * Deletes an entry from the space's reverse hash table.
  367. * Conditions:
  368. * The space must be write-locked.
  369. */
  370. void
  371. ipc_hash_local_delete(
  372. ipc_space_t space,
  373. ipc_object_t obj,
  374. mach_port_index_t index,
  375. ipc_entry_t entry)
  376. {
  377. ipc_entry_t table;
  378. ipc_entry_num_t size;
  379. mach_port_index_t hindex, dindex;
  380. assert(index != MACH_PORT_NULL);
  381. assert(space != IS_NULL);
  382. assert(obj != IO_NULL);
  383. table = space->is_table;
  384. size = space->is_table_size;
  385. hindex = IH_LOCAL_HASH(obj, size);
  386. assert(entry == &table[index]);
  387. assert(entry->ie_object == obj);
  388. /*
  389. * First check we have the right hindex for this index.
  390. * In case of collision, we have to search farther
  391. * along in this clump.
  392. */
  393. while (table[hindex].ie_index != index) {
  394. if (table[hindex].ie_index == 0)
  395. {
  396. static int gak = 0;
  397. if (gak == 0)
  398. {
  399. printf("gak! entry wasn't in hash table!\n");
  400. gak = 1;
  401. }
  402. return;
  403. }
  404. if (++hindex == size)
  405. hindex = 0;
  406. }
  407. /*
  408. * Now we want to set table[hindex].ie_index = 0.
  409. * But if we aren't the last index in a clump,
  410. * this might cause problems for lookups of objects
  411. * farther along in the clump that are displaced
  412. * due to collisions. Searches for them would fail
  413. * at hindex instead of succeeding.
  414. *
  415. * So we must check the clump after hindex for objects
  416. * that are so displaced, and move one up to the new hole.
  417. *
  418. * hindex - index of new hole in the clump
  419. * dindex - index we are checking for a displaced object
  420. *
  421. * When we move a displaced object up into the hole,
  422. * it creates a new hole, and we have to repeat the process
  423. * until we get to the end of the clump.
  424. */
  425. for (dindex = hindex; index != 0; hindex = dindex) {
  426. for (;;) {
  427. mach_port_index_t tindex;
  428. ipc_object_t tobj;
  429. if (++dindex == size)
  430. dindex = 0;
  431. assert(dindex != hindex);
  432. /* are we at the end of the clump? */
  433. index = table[dindex].ie_index;
  434. if (index == 0)
  435. break;
  436. /* is this a displaced object? */
  437. tobj = table[index].ie_object;
  438. assert(tobj != IO_NULL);
  439. tindex = IH_LOCAL_HASH(tobj, size);
  440. if ((dindex < hindex) ?
  441. ((dindex < tindex) && (tindex <= hindex)) :
  442. ((dindex < tindex) || (tindex <= hindex)))
  443. break;
  444. }
  445. table[hindex].ie_index = index;
  446. }
  447. }
  448. /*
  449. * Routine: ipc_hash_init
  450. * Purpose:
  451. * Initialize the reverse hash table implementation.
  452. */
  453. void
  454. ipc_hash_init(void)
  455. {
  456. ipc_hash_index_t i;
  457. /* initialize ipc_hash_global_size */
  458. ipc_hash_global_size = IPC_HASH_GLOBAL_SIZE;
  459. /* make sure it is a power of two */
  460. ipc_hash_global_mask = ipc_hash_global_size - 1;
  461. if ((ipc_hash_global_size & ipc_hash_global_mask) != 0) {
  462. natural_t bit;
  463. /* round up to closest power of two */
  464. for (bit = 1;; bit <<= 1) {
  465. ipc_hash_global_mask |= bit;
  466. ipc_hash_global_size = ipc_hash_global_mask + 1;
  467. if ((ipc_hash_global_size & ipc_hash_global_mask) == 0)
  468. break;
  469. }
  470. }
  471. /* allocate ipc_hash_global_table */
  472. ipc_hash_global_table = (ipc_hash_global_bucket_t)
  473. kalloc((vm_size_t) (ipc_hash_global_size *
  474. sizeof(struct ipc_hash_global_bucket)));
  475. assert(ipc_hash_global_table != IHGB_NULL);
  476. /* and initialize it */
  477. for (i = 0; i < ipc_hash_global_size; i++) {
  478. ipc_hash_global_bucket_t bucket;
  479. bucket = &ipc_hash_global_table[i];
  480. ihgb_lock_init(bucket);
  481. bucket->ihgb_head = ITE_NULL;
  482. }
  483. }
  484. #if MACH_IPC_DEBUG
  485. /*
  486. * Routine: ipc_hash_info
  487. * Purpose:
  488. * Return information about the global reverse hash table.
  489. * Fills the buffer with as much information as possible
  490. * and returns the desired size of the buffer.
  491. * Conditions:
  492. * Nothing locked. The caller should provide
  493. * possibly-pageable memory.
  494. */
  495. ipc_hash_index_t
  496. ipc_hash_info(
  497. hash_info_bucket_t *info,
  498. mach_msg_type_number_t count)
  499. {
  500. ipc_hash_index_t i;
  501. if (ipc_hash_global_size < count)
  502. count = ipc_hash_global_size;
  503. for (i = 0; i < count; i++) {
  504. ipc_hash_global_bucket_t bucket = &ipc_hash_global_table[i];
  505. unsigned int bucket_count = 0;
  506. ipc_tree_entry_t entry;
  507. ihgb_lock(bucket);
  508. for (entry = bucket->ihgb_head;
  509. entry != ITE_NULL;
  510. entry = entry->ite_next)
  511. bucket_count++;
  512. ihgb_unlock(bucket);
  513. /* don't touch pageable memory while holding locks */
  514. info[i].hib_count = bucket_count;
  515. }
  516. return ipc_hash_global_size;
  517. }
  518. #endif /* MACH_IPC_DEBUG */