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