int-map.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright 2023 Red Hat
  4. */
  5. /**
  6. * DOC:
  7. *
  8. * Hash table implementation of a map from integers to pointers, implemented using the Hopscotch
  9. * Hashing algorithm by Herlihy, Shavit, and Tzafrir (see
  10. * http://en.wikipedia.org/wiki/Hopscotch_hashing). This implementation does not contain any of the
  11. * locking/concurrency features of the algorithm, just the collision resolution scheme.
  12. *
  13. * Hopscotch Hashing is based on hashing with open addressing and linear probing. All the entries
  14. * are stored in a fixed array of buckets, with no dynamic allocation for collisions. Unlike linear
  15. * probing, all the entries that hash to a given bucket are stored within a fixed neighborhood
  16. * starting at that bucket. Chaining is effectively represented as a bit vector relative to each
  17. * bucket instead of as pointers or explicit offsets.
  18. *
  19. * When an empty bucket cannot be found within a given neighborhood, subsequent neighborhoods are
  20. * searched, and one or more entries will "hop" into those neighborhoods. When this process works,
  21. * an empty bucket will move into the desired neighborhood, allowing the entry to be added. When
  22. * that process fails (typically when the buckets are around 90% full), the table must be resized
  23. * and the all entries rehashed and added to the expanded table.
  24. *
  25. * Unlike linear probing, the number of buckets that must be searched in the worst case has a fixed
  26. * upper bound (the size of the neighborhood). Those entries occupy a small number of memory cache
  27. * lines, leading to improved use of the cache (fewer misses on both successful and unsuccessful
  28. * searches). Hopscotch hashing outperforms linear probing at much higher load factors, so even
  29. * with the increased memory burden for maintaining the hop vectors, less memory is needed to
  30. * achieve that performance. Hopscotch is also immune to "contamination" from deleting entries
  31. * since entries are genuinely removed instead of being replaced by a placeholder.
  32. *
  33. * The published description of the algorithm used a bit vector, but the paper alludes to an offset
  34. * scheme which is used by this implementation. Since the entries in the neighborhood are within N
  35. * entries of the hash bucket at the start of the neighborhood, a pair of small offset fields each
  36. * log2(N) bits wide is all that's needed to maintain the hops as a linked list. In order to encode
  37. * "no next hop" (i.e. NULL) as the natural initial value of zero, the offsets are biased by one
  38. * (i.e. 0 => NULL, 1 => offset=0, 2 => offset=1, etc.) We can represent neighborhoods of up to 255
  39. * entries with just 8+8=16 bits per entry. The hop list is sorted by hop offset so the first entry
  40. * in the list is always the bucket closest to the start of the neighborhood.
  41. *
  42. * While individual accesses tend to be very fast, the table resize operations are very, very
  43. * expensive. If an upper bound on the latency of adding an entry to the table is needed, we either
  44. * need to ensure the table is pre-sized to be large enough so no resize is ever needed, or we'll
  45. * need to develop an approach to incrementally resize the table.
  46. */
  47. #include "int-map.h"
  48. #include <linux/minmax.h>
  49. #include "errors.h"
  50. #include "logger.h"
  51. #include "memory-alloc.h"
  52. #include "numeric.h"
  53. #include "permassert.h"
  54. #define DEFAULT_CAPACITY 16 /* the number of neighborhoods in a new table */
  55. #define NEIGHBORHOOD 255 /* the number of buckets in each neighborhood */
  56. #define MAX_PROBES 1024 /* limit on the number of probes for a free bucket */
  57. #define NULL_HOP_OFFSET 0 /* the hop offset value terminating the hop list */
  58. #define DEFAULT_LOAD 75 /* a compromise between memory use and performance */
  59. /**
  60. * struct bucket - hash bucket
  61. *
  62. * Buckets are packed together to reduce memory usage and improve cache efficiency. It would be
  63. * tempting to encode the hop offsets separately and maintain alignment of key/value pairs, but
  64. * it's crucial to keep the hop fields near the buckets that they use them so they'll tend to share
  65. * cache lines.
  66. */
  67. struct __packed bucket {
  68. /**
  69. * @first_hop: The biased offset of the first entry in the hop list of the neighborhood
  70. * that hashes to this bucket.
  71. */
  72. u8 first_hop;
  73. /** @next_hop: The biased offset of the next bucket in the hop list. */
  74. u8 next_hop;
  75. /** @key: The key stored in this bucket. */
  76. u64 key;
  77. /** @value: The value stored in this bucket (NULL if empty). */
  78. void *value;
  79. };
  80. /**
  81. * struct int_map - The concrete definition of the opaque int_map type.
  82. *
  83. * To avoid having to wrap the neighborhoods of the last entries back around to the start of the
  84. * bucket array, we allocate a few more buckets at the end of the array instead, which is why
  85. * capacity and bucket_count are different.
  86. */
  87. struct int_map {
  88. /** @size: The number of entries stored in the map. */
  89. size_t size;
  90. /** @capacity: The number of neighborhoods in the map. */
  91. size_t capacity;
  92. /** @bucket_count: The number of buckets in the bucket array. */
  93. size_t bucket_count;
  94. /** @buckets: The array of hash buckets. */
  95. struct bucket *buckets;
  96. };
  97. /**
  98. * mix() - The Google CityHash 16-byte hash mixing function.
  99. * @input1: The first input value.
  100. * @input2: The second input value.
  101. *
  102. * Return: A hash of the two inputs.
  103. */
  104. static u64 mix(u64 input1, u64 input2)
  105. {
  106. static const u64 CITY_MULTIPLIER = 0x9ddfea08eb382d69ULL;
  107. u64 hash = (input1 ^ input2);
  108. hash *= CITY_MULTIPLIER;
  109. hash ^= (hash >> 47);
  110. hash ^= input2;
  111. hash *= CITY_MULTIPLIER;
  112. hash ^= (hash >> 47);
  113. hash *= CITY_MULTIPLIER;
  114. return hash;
  115. }
  116. /**
  117. * hash_key() - Calculate a 64-bit non-cryptographic hash value for the provided 64-bit integer
  118. * key.
  119. * @key: The mapping key.
  120. *
  121. * The implementation is based on Google's CityHash, only handling the specific case of an 8-byte
  122. * input.
  123. *
  124. * Return: The hash of the mapping key.
  125. */
  126. static u64 hash_key(u64 key)
  127. {
  128. /*
  129. * Aliasing restrictions forbid us from casting pointer types, so use a union to convert a
  130. * single u64 to two u32 values.
  131. */
  132. union {
  133. u64 u64;
  134. u32 u32[2];
  135. } pun = {.u64 = key};
  136. return mix(sizeof(key) + (((u64) pun.u32[0]) << 3), pun.u32[1]);
  137. }
  138. /**
  139. * allocate_buckets() - Initialize an int_map.
  140. * @map: The map to initialize.
  141. * @capacity: The initial capacity of the map.
  142. *
  143. * Return: VDO_SUCCESS or an error code.
  144. */
  145. static int allocate_buckets(struct int_map *map, size_t capacity)
  146. {
  147. map->size = 0;
  148. map->capacity = capacity;
  149. /*
  150. * Allocate NEIGHBORHOOD - 1 extra buckets so the last bucket can have a full neighborhood
  151. * without have to wrap back around to element zero.
  152. */
  153. map->bucket_count = capacity + (NEIGHBORHOOD - 1);
  154. return vdo_allocate(map->bucket_count, struct bucket,
  155. "struct int_map buckets", &map->buckets);
  156. }
  157. /**
  158. * vdo_int_map_create() - Allocate and initialize an int_map.
  159. * @initial_capacity: The number of entries the map should initially be capable of holding (zero
  160. * tells the map to use its own small default).
  161. * @map_ptr: Output, a pointer to hold the new int_map.
  162. *
  163. * Return: VDO_SUCCESS or an error code.
  164. */
  165. int vdo_int_map_create(size_t initial_capacity, struct int_map **map_ptr)
  166. {
  167. struct int_map *map;
  168. int result;
  169. size_t capacity;
  170. result = vdo_allocate(1, struct int_map, "struct int_map", &map);
  171. if (result != VDO_SUCCESS)
  172. return result;
  173. /* Use the default capacity if the caller did not specify one. */
  174. capacity = (initial_capacity > 0) ? initial_capacity : DEFAULT_CAPACITY;
  175. /*
  176. * Scale up the capacity by the specified initial load factor. (i.e to hold 1000 entries at
  177. * 80% load we need a capacity of 1250)
  178. */
  179. capacity = capacity * 100 / DEFAULT_LOAD;
  180. result = allocate_buckets(map, capacity);
  181. if (result != VDO_SUCCESS) {
  182. vdo_int_map_free(vdo_forget(map));
  183. return result;
  184. }
  185. *map_ptr = map;
  186. return VDO_SUCCESS;
  187. }
  188. /**
  189. * vdo_int_map_free() - Free an int_map.
  190. * @map: The int_map to free.
  191. *
  192. * NOTE: The map does not own the pointer values stored in the map and they are not freed by this
  193. * call.
  194. */
  195. void vdo_int_map_free(struct int_map *map)
  196. {
  197. if (map == NULL)
  198. return;
  199. vdo_free(vdo_forget(map->buckets));
  200. vdo_free(vdo_forget(map));
  201. }
  202. /**
  203. * vdo_int_map_size() - Get the number of entries stored in an int_map.
  204. * @map: The int_map to query.
  205. *
  206. * Return: The number of entries in the map.
  207. */
  208. size_t vdo_int_map_size(const struct int_map *map)
  209. {
  210. return map->size;
  211. }
  212. /**
  213. * dereference_hop() - Convert a biased hop offset within a neighborhood to a pointer to the bucket
  214. * it references.
  215. * @neighborhood: The first bucket in the neighborhood.
  216. * @hop_offset: The biased hop offset to the desired bucket.
  217. *
  218. * Return: NULL if hop_offset is zero, otherwise a pointer to the bucket in the neighborhood at
  219. * hop_offset - 1.
  220. */
  221. static struct bucket *dereference_hop(struct bucket *neighborhood, unsigned int hop_offset)
  222. {
  223. BUILD_BUG_ON(NULL_HOP_OFFSET != 0);
  224. if (hop_offset == NULL_HOP_OFFSET)
  225. return NULL;
  226. return &neighborhood[hop_offset - 1];
  227. }
  228. /**
  229. * insert_in_hop_list() - Add a bucket into the hop list for the neighborhood.
  230. * @neighborhood: The first bucket in the neighborhood.
  231. * @new_bucket: The bucket to add to the hop list.
  232. *
  233. * The bucket is inserted it into the list so the hop list remains sorted by hop offset.
  234. */
  235. static void insert_in_hop_list(struct bucket *neighborhood, struct bucket *new_bucket)
  236. {
  237. /* Zero indicates a NULL hop offset, so bias the hop offset by one. */
  238. int hop_offset = 1 + (new_bucket - neighborhood);
  239. /* Handle the special case of adding a bucket at the start of the list. */
  240. int next_hop = neighborhood->first_hop;
  241. if ((next_hop == NULL_HOP_OFFSET) || (next_hop > hop_offset)) {
  242. new_bucket->next_hop = next_hop;
  243. neighborhood->first_hop = hop_offset;
  244. return;
  245. }
  246. /* Search the hop list for the insertion point that maintains the sort order. */
  247. for (;;) {
  248. struct bucket *bucket = dereference_hop(neighborhood, next_hop);
  249. next_hop = bucket->next_hop;
  250. if ((next_hop == NULL_HOP_OFFSET) || (next_hop > hop_offset)) {
  251. new_bucket->next_hop = next_hop;
  252. bucket->next_hop = hop_offset;
  253. return;
  254. }
  255. }
  256. }
  257. /**
  258. * select_bucket() - Select and return the hash bucket for a given search key.
  259. * @map: The map to search.
  260. * @key: The mapping key.
  261. */
  262. static struct bucket *select_bucket(const struct int_map *map, u64 key)
  263. {
  264. /*
  265. * Calculate a good hash value for the provided key. We want exactly 32 bits, so mask the
  266. * result.
  267. */
  268. u64 hash = hash_key(key) & 0xFFFFFFFF;
  269. /*
  270. * Scale the 32-bit hash to a bucket index by treating it as a binary fraction and
  271. * multiplying that by the capacity. If the hash is uniformly distributed over [0 ..
  272. * 2^32-1], then (hash * capacity / 2^32) should be uniformly distributed over [0 ..
  273. * capacity-1]. The multiply and shift is much faster than a divide (modulus) on X86 CPUs.
  274. */
  275. return &map->buckets[(hash * map->capacity) >> 32];
  276. }
  277. /**
  278. * search_hop_list() - Search the hop list associated with given hash bucket for a given search
  279. * key.
  280. * @map: The map being searched.
  281. * @bucket: The map bucket to search for the key.
  282. * @key: The mapping key.
  283. * @previous_ptr: Output. if not NULL, a pointer in which to store the bucket in the list preceding
  284. * the one that had the matching key
  285. *
  286. * If the key is found, returns a pointer to the entry (bucket or collision), otherwise returns
  287. * NULL.
  288. *
  289. * Return: An entry that matches the key, or NULL if not found.
  290. */
  291. static struct bucket *search_hop_list(struct int_map *map __always_unused,
  292. struct bucket *bucket,
  293. u64 key,
  294. struct bucket **previous_ptr)
  295. {
  296. struct bucket *previous = NULL;
  297. unsigned int next_hop = bucket->first_hop;
  298. while (next_hop != NULL_HOP_OFFSET) {
  299. /*
  300. * Check the neighboring bucket indexed by the offset for the
  301. * desired key.
  302. */
  303. struct bucket *entry = dereference_hop(bucket, next_hop);
  304. if ((key == entry->key) && (entry->value != NULL)) {
  305. if (previous_ptr != NULL)
  306. *previous_ptr = previous;
  307. return entry;
  308. }
  309. next_hop = entry->next_hop;
  310. previous = entry;
  311. }
  312. return NULL;
  313. }
  314. /**
  315. * vdo_int_map_get() - Retrieve the value associated with a given key from the int_map.
  316. * @map: The int_map to query.
  317. * @key: The key to look up.
  318. *
  319. * Return: The value associated with the given key, or NULL if the key is not mapped to any value.
  320. */
  321. void *vdo_int_map_get(struct int_map *map, u64 key)
  322. {
  323. struct bucket *match = search_hop_list(map, select_bucket(map, key), key, NULL);
  324. return ((match != NULL) ? match->value : NULL);
  325. }
  326. /**
  327. * resize_buckets() - Increase the number of hash buckets.
  328. * @map: The map to resize.
  329. *
  330. * Resizes and rehashes all the existing entries, storing them in the new buckets.
  331. *
  332. * Return: VDO_SUCCESS or an error code.
  333. */
  334. static int resize_buckets(struct int_map *map)
  335. {
  336. int result;
  337. size_t i;
  338. /* Copy the top-level map data to the stack. */
  339. struct int_map old_map = *map;
  340. /* Re-initialize the map to be empty and 50% larger. */
  341. size_t new_capacity = map->capacity / 2 * 3;
  342. vdo_log_info("%s: attempting resize from %zu to %zu, current size=%zu",
  343. __func__, map->capacity, new_capacity, map->size);
  344. result = allocate_buckets(map, new_capacity);
  345. if (result != VDO_SUCCESS) {
  346. *map = old_map;
  347. return result;
  348. }
  349. /* Populate the new hash table from the entries in the old bucket array. */
  350. for (i = 0; i < old_map.bucket_count; i++) {
  351. struct bucket *entry = &old_map.buckets[i];
  352. if (entry->value == NULL)
  353. continue;
  354. result = vdo_int_map_put(map, entry->key, entry->value, true, NULL);
  355. if (result != VDO_SUCCESS) {
  356. /* Destroy the new partial map and restore the map from the stack. */
  357. vdo_free(vdo_forget(map->buckets));
  358. *map = old_map;
  359. return result;
  360. }
  361. }
  362. /* Destroy the old bucket array. */
  363. vdo_free(vdo_forget(old_map.buckets));
  364. return VDO_SUCCESS;
  365. }
  366. /**
  367. * find_empty_bucket() - Probe the bucket array starting at the given bucket for the next empty
  368. * bucket, returning a pointer to it.
  369. * @map: The map containing the buckets to search.
  370. * @bucket: The bucket at which to start probing.
  371. * @max_probes: The maximum number of buckets to search.
  372. *
  373. * NULL will be returned if the search reaches the end of the bucket array or if the number of
  374. * linear probes exceeds a specified limit.
  375. *
  376. * Return: The next empty bucket, or NULL if the search failed.
  377. */
  378. static struct bucket *
  379. find_empty_bucket(struct int_map *map, struct bucket *bucket, unsigned int max_probes)
  380. {
  381. /*
  382. * Limit the search to either the nearer of the end of the bucket array or a fixed distance
  383. * beyond the initial bucket.
  384. */
  385. ptrdiff_t remaining = &map->buckets[map->bucket_count] - bucket;
  386. struct bucket *sentinel = &bucket[min_t(ptrdiff_t, remaining, max_probes)];
  387. struct bucket *entry;
  388. for (entry = bucket; entry < sentinel; entry++) {
  389. if (entry->value == NULL)
  390. return entry;
  391. }
  392. return NULL;
  393. }
  394. /**
  395. * move_empty_bucket() - Move an empty bucket closer to the start of the bucket array.
  396. * @map: The map containing the bucket.
  397. * @hole: The empty bucket to fill with an entry that precedes it in one of its enclosing
  398. * neighborhoods.
  399. *
  400. * This searches the neighborhoods that contain the empty bucket for a non-empty bucket closer to
  401. * the start of the array. If such a bucket is found, this swaps the two buckets by moving the
  402. * entry to the empty bucket.
  403. *
  404. * Return: The bucket that was vacated by moving its entry to the provided hole, or NULL if no
  405. * entry could be moved.
  406. */
  407. static struct bucket *move_empty_bucket(struct int_map *map __always_unused,
  408. struct bucket *hole)
  409. {
  410. /*
  411. * Examine every neighborhood that the empty bucket is part of, starting with the one in
  412. * which it is the last bucket. No boundary check is needed for the negative array
  413. * arithmetic since this function is only called when hole is at least NEIGHBORHOOD cells
  414. * deeper into the array than a valid bucket.
  415. */
  416. struct bucket *bucket;
  417. for (bucket = &hole[1 - NEIGHBORHOOD]; bucket < hole; bucket++) {
  418. /*
  419. * Find the entry that is nearest to the bucket, which means it will be nearest to
  420. * the hash bucket whose neighborhood is full.
  421. */
  422. struct bucket *new_hole = dereference_hop(bucket, bucket->first_hop);
  423. if (new_hole == NULL) {
  424. /*
  425. * There are no buckets in this neighborhood that are in use by this one
  426. * (they must all be owned by overlapping neighborhoods).
  427. */
  428. continue;
  429. }
  430. /*
  431. * Skip this bucket if its first entry is actually further away than the hole that
  432. * we're already trying to fill.
  433. */
  434. if (hole < new_hole)
  435. continue;
  436. /*
  437. * We've found an entry in this neighborhood that we can "hop" further away, moving
  438. * the hole closer to the hash bucket, if not all the way into its neighborhood.
  439. */
  440. /*
  441. * The entry that will be the new hole is the first bucket in the list, so setting
  442. * first_hop is all that's needed remove it from the list.
  443. */
  444. bucket->first_hop = new_hole->next_hop;
  445. new_hole->next_hop = NULL_HOP_OFFSET;
  446. /* Move the entry into the original hole. */
  447. hole->key = new_hole->key;
  448. hole->value = new_hole->value;
  449. new_hole->value = NULL;
  450. /* Insert the filled hole into the hop list for the neighborhood. */
  451. insert_in_hop_list(bucket, hole);
  452. return new_hole;
  453. }
  454. /* We couldn't find an entry to relocate to the hole. */
  455. return NULL;
  456. }
  457. /**
  458. * update_mapping() - Find and update any existing mapping for a given key, returning the value
  459. * associated with the key in the provided pointer.
  460. * @map: The int_map to attempt to modify.
  461. * @neighborhood: The first bucket in the neighborhood that would contain the search key
  462. * @key: The key with which to associate the new value.
  463. * @new_value: The value to be associated with the key.
  464. * @update: Whether to overwrite an existing value.
  465. * @old_value_ptr: a pointer in which to store the old value (unmodified if no mapping was found)
  466. *
  467. * Return: true if the map contains a mapping for the key, false if it does not.
  468. */
  469. static bool update_mapping(struct int_map *map, struct bucket *neighborhood,
  470. u64 key, void *new_value, bool update, void **old_value_ptr)
  471. {
  472. struct bucket *bucket = search_hop_list(map, neighborhood, key, NULL);
  473. if (bucket == NULL) {
  474. /* There is no bucket containing the key in the neighborhood. */
  475. return false;
  476. }
  477. /*
  478. * Return the value of the current mapping (if desired) and update the mapping with the new
  479. * value (if desired).
  480. */
  481. if (old_value_ptr != NULL)
  482. *old_value_ptr = bucket->value;
  483. if (update)
  484. bucket->value = new_value;
  485. return true;
  486. }
  487. /**
  488. * find_or_make_vacancy() - Find an empty bucket.
  489. * @map: The int_map to search or modify.
  490. * @neighborhood: The first bucket in the neighborhood in which an empty bucket is needed for a new
  491. * mapping.
  492. *
  493. * Find an empty bucket in a specified neighborhood for a new mapping or attempt to re-arrange
  494. * mappings so there is such a bucket. This operation may fail (returning NULL) if an empty bucket
  495. * is not available or could not be relocated to the neighborhood.
  496. *
  497. * Return: a pointer to an empty bucket in the desired neighborhood, or NULL if a vacancy could not
  498. * be found or arranged.
  499. */
  500. static struct bucket *find_or_make_vacancy(struct int_map *map,
  501. struct bucket *neighborhood)
  502. {
  503. /* Probe within and beyond the neighborhood for the first empty bucket. */
  504. struct bucket *hole = find_empty_bucket(map, neighborhood, MAX_PROBES);
  505. /*
  506. * Keep trying until the empty bucket is in the bucket's neighborhood or we are unable to
  507. * move it any closer by swapping it with a filled bucket.
  508. */
  509. while (hole != NULL) {
  510. int distance = hole - neighborhood;
  511. if (distance < NEIGHBORHOOD) {
  512. /*
  513. * We've found or relocated an empty bucket close enough to the initial
  514. * hash bucket to be referenced by its hop vector.
  515. */
  516. return hole;
  517. }
  518. /*
  519. * The nearest empty bucket isn't within the neighborhood that must contain the new
  520. * entry, so try to swap it with bucket that is closer.
  521. */
  522. hole = move_empty_bucket(map, hole);
  523. }
  524. return NULL;
  525. }
  526. /**
  527. * vdo_int_map_put() - Try to associate a value with an integer.
  528. * @map: The int_map to attempt to modify.
  529. * @key: The key with which to associate the new value.
  530. * @new_value: The value to be associated with the key.
  531. * @update: Whether to overwrite an existing value.
  532. * @old_value_ptr: A pointer in which to store either the old value (if the key was already mapped)
  533. * or NULL if the map did not contain the key; NULL may be provided if the caller
  534. * does not need to know the old value
  535. *
  536. * Try to associate a value (a pointer) with an integer in an int_map. If the map already contains
  537. * a mapping for the provided key, the old value is only replaced with the specified value if
  538. * update is true. In either case the old value is returned. If the map does not already contain a
  539. * value for the specified key, the new value is added regardless of the value of update.
  540. *
  541. * Return: VDO_SUCCESS or an error code.
  542. */
  543. int vdo_int_map_put(struct int_map *map, u64 key, void *new_value, bool update,
  544. void **old_value_ptr)
  545. {
  546. struct bucket *neighborhood, *bucket;
  547. if (unlikely(new_value == NULL))
  548. return -EINVAL;
  549. /*
  550. * Select the bucket at the start of the neighborhood that must contain any entry for the
  551. * provided key.
  552. */
  553. neighborhood = select_bucket(map, key);
  554. /*
  555. * Check whether the neighborhood already contains an entry for the key, in which case we
  556. * optionally update it, returning the old value.
  557. */
  558. if (update_mapping(map, neighborhood, key, new_value, update, old_value_ptr))
  559. return VDO_SUCCESS;
  560. /*
  561. * Find an empty bucket in the desired neighborhood for the new entry or re-arrange entries
  562. * in the map so there is such a bucket. This operation will usually succeed; the loop body
  563. * will only be executed on the rare occasions that we have to resize the map.
  564. */
  565. while ((bucket = find_or_make_vacancy(map, neighborhood)) == NULL) {
  566. int result;
  567. /*
  568. * There is no empty bucket in which to put the new entry in the current map, so
  569. * we're forced to allocate a new bucket array with a larger capacity, re-hash all
  570. * the entries into those buckets, and try again (a very expensive operation for
  571. * large maps).
  572. */
  573. result = resize_buckets(map);
  574. if (result != VDO_SUCCESS)
  575. return result;
  576. /*
  577. * Resizing the map invalidates all pointers to buckets, so recalculate the
  578. * neighborhood pointer.
  579. */
  580. neighborhood = select_bucket(map, key);
  581. }
  582. /* Put the new entry in the empty bucket, adding it to the neighborhood. */
  583. bucket->key = key;
  584. bucket->value = new_value;
  585. insert_in_hop_list(neighborhood, bucket);
  586. map->size += 1;
  587. /* There was no existing entry, so there was no old value to be returned. */
  588. if (old_value_ptr != NULL)
  589. *old_value_ptr = NULL;
  590. return VDO_SUCCESS;
  591. }
  592. /**
  593. * vdo_int_map_remove() - Remove the mapping for a given key from the int_map.
  594. * @map: The int_map from which to remove the mapping.
  595. * @key: The key whose mapping is to be removed.
  596. *
  597. * Return: the value that was associated with the key, or NULL if it was not mapped.
  598. */
  599. void *vdo_int_map_remove(struct int_map *map, u64 key)
  600. {
  601. void *value;
  602. /* Select the bucket to search and search it for an existing entry. */
  603. struct bucket *bucket = select_bucket(map, key);
  604. struct bucket *previous;
  605. struct bucket *victim = search_hop_list(map, bucket, key, &previous);
  606. if (victim == NULL) {
  607. /* There is no matching entry to remove. */
  608. return NULL;
  609. }
  610. /*
  611. * We found an entry to remove. Save the mapped value to return later and empty the bucket.
  612. */
  613. map->size -= 1;
  614. value = victim->value;
  615. victim->value = NULL;
  616. victim->key = 0;
  617. /* The victim bucket is now empty, but it still needs to be spliced out of the hop list. */
  618. if (previous == NULL) {
  619. /* The victim is the head of the list, so swing first_hop. */
  620. bucket->first_hop = victim->next_hop;
  621. } else {
  622. previous->next_hop = victim->next_hop;
  623. }
  624. victim->next_hop = NULL_HOP_OFFSET;
  625. return value;
  626. }