162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Generic associative array implementation.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * See Documentation/core-api/assoc_array.rst for information.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
762306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci//#define DEBUG
1062306a36Sopenharmony_ci#include <linux/rcupdate.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/assoc_array_priv.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/*
1662306a36Sopenharmony_ci * Iterate over an associative array.  The caller must hold the RCU read lock
1762306a36Sopenharmony_ci * or better.
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_cistatic int assoc_array_subtree_iterate(const struct assoc_array_ptr *root,
2062306a36Sopenharmony_ci				       const struct assoc_array_ptr *stop,
2162306a36Sopenharmony_ci				       int (*iterator)(const void *leaf,
2262306a36Sopenharmony_ci						       void *iterator_data),
2362306a36Sopenharmony_ci				       void *iterator_data)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	const struct assoc_array_shortcut *shortcut;
2662306a36Sopenharmony_ci	const struct assoc_array_node *node;
2762306a36Sopenharmony_ci	const struct assoc_array_ptr *cursor, *ptr, *parent;
2862306a36Sopenharmony_ci	unsigned long has_meta;
2962306a36Sopenharmony_ci	int slot, ret;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	cursor = root;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cibegin_node:
3462306a36Sopenharmony_ci	if (assoc_array_ptr_is_shortcut(cursor)) {
3562306a36Sopenharmony_ci		/* Descend through a shortcut */
3662306a36Sopenharmony_ci		shortcut = assoc_array_ptr_to_shortcut(cursor);
3762306a36Sopenharmony_ci		cursor = READ_ONCE(shortcut->next_node); /* Address dependency. */
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	node = assoc_array_ptr_to_node(cursor);
4162306a36Sopenharmony_ci	slot = 0;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* We perform two passes of each node.
4462306a36Sopenharmony_ci	 *
4562306a36Sopenharmony_ci	 * The first pass does all the leaves in this node.  This means we
4662306a36Sopenharmony_ci	 * don't miss any leaves if the node is split up by insertion whilst
4762306a36Sopenharmony_ci	 * we're iterating over the branches rooted here (we may, however, see
4862306a36Sopenharmony_ci	 * some leaves twice).
4962306a36Sopenharmony_ci	 */
5062306a36Sopenharmony_ci	has_meta = 0;
5162306a36Sopenharmony_ci	for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
5262306a36Sopenharmony_ci		ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
5362306a36Sopenharmony_ci		has_meta |= (unsigned long)ptr;
5462306a36Sopenharmony_ci		if (ptr && assoc_array_ptr_is_leaf(ptr)) {
5562306a36Sopenharmony_ci			/* We need a barrier between the read of the pointer,
5662306a36Sopenharmony_ci			 * which is supplied by the above READ_ONCE().
5762306a36Sopenharmony_ci			 */
5862306a36Sopenharmony_ci			/* Invoke the callback */
5962306a36Sopenharmony_ci			ret = iterator(assoc_array_ptr_to_leaf(ptr),
6062306a36Sopenharmony_ci				       iterator_data);
6162306a36Sopenharmony_ci			if (ret)
6262306a36Sopenharmony_ci				return ret;
6362306a36Sopenharmony_ci		}
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	/* The second pass attends to all the metadata pointers.  If we follow
6762306a36Sopenharmony_ci	 * one of these we may find that we don't come back here, but rather go
6862306a36Sopenharmony_ci	 * back to a replacement node with the leaves in a different layout.
6962306a36Sopenharmony_ci	 *
7062306a36Sopenharmony_ci	 * We are guaranteed to make progress, however, as the slot number for
7162306a36Sopenharmony_ci	 * a particular portion of the key space cannot change - and we
7262306a36Sopenharmony_ci	 * continue at the back pointer + 1.
7362306a36Sopenharmony_ci	 */
7462306a36Sopenharmony_ci	if (!(has_meta & ASSOC_ARRAY_PTR_META_TYPE))
7562306a36Sopenharmony_ci		goto finished_node;
7662306a36Sopenharmony_ci	slot = 0;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cicontinue_node:
7962306a36Sopenharmony_ci	node = assoc_array_ptr_to_node(cursor);
8062306a36Sopenharmony_ci	for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
8162306a36Sopenharmony_ci		ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
8262306a36Sopenharmony_ci		if (assoc_array_ptr_is_meta(ptr)) {
8362306a36Sopenharmony_ci			cursor = ptr;
8462306a36Sopenharmony_ci			goto begin_node;
8562306a36Sopenharmony_ci		}
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cifinished_node:
8962306a36Sopenharmony_ci	/* Move up to the parent (may need to skip back over a shortcut) */
9062306a36Sopenharmony_ci	parent = READ_ONCE(node->back_pointer); /* Address dependency. */
9162306a36Sopenharmony_ci	slot = node->parent_slot;
9262306a36Sopenharmony_ci	if (parent == stop)
9362306a36Sopenharmony_ci		return 0;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (assoc_array_ptr_is_shortcut(parent)) {
9662306a36Sopenharmony_ci		shortcut = assoc_array_ptr_to_shortcut(parent);
9762306a36Sopenharmony_ci		cursor = parent;
9862306a36Sopenharmony_ci		parent = READ_ONCE(shortcut->back_pointer); /* Address dependency. */
9962306a36Sopenharmony_ci		slot = shortcut->parent_slot;
10062306a36Sopenharmony_ci		if (parent == stop)
10162306a36Sopenharmony_ci			return 0;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* Ascend to next slot in parent node */
10562306a36Sopenharmony_ci	cursor = parent;
10662306a36Sopenharmony_ci	slot++;
10762306a36Sopenharmony_ci	goto continue_node;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci/**
11162306a36Sopenharmony_ci * assoc_array_iterate - Pass all objects in the array to a callback
11262306a36Sopenharmony_ci * @array: The array to iterate over.
11362306a36Sopenharmony_ci * @iterator: The callback function.
11462306a36Sopenharmony_ci * @iterator_data: Private data for the callback function.
11562306a36Sopenharmony_ci *
11662306a36Sopenharmony_ci * Iterate over all the objects in an associative array.  Each one will be
11762306a36Sopenharmony_ci * presented to the iterator function.
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci * If the array is being modified concurrently with the iteration then it is
12062306a36Sopenharmony_ci * possible that some objects in the array will be passed to the iterator
12162306a36Sopenharmony_ci * callback more than once - though every object should be passed at least
12262306a36Sopenharmony_ci * once.  If this is undesirable then the caller must lock against modification
12362306a36Sopenharmony_ci * for the duration of this function.
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci * The function will return 0 if no objects were in the array or else it will
12662306a36Sopenharmony_ci * return the result of the last iterator function called.  Iteration stops
12762306a36Sopenharmony_ci * immediately if any call to the iteration function results in a non-zero
12862306a36Sopenharmony_ci * return.
12962306a36Sopenharmony_ci *
13062306a36Sopenharmony_ci * The caller should hold the RCU read lock or better if concurrent
13162306a36Sopenharmony_ci * modification is possible.
13262306a36Sopenharmony_ci */
13362306a36Sopenharmony_ciint assoc_array_iterate(const struct assoc_array *array,
13462306a36Sopenharmony_ci			int (*iterator)(const void *object,
13562306a36Sopenharmony_ci					void *iterator_data),
13662306a36Sopenharmony_ci			void *iterator_data)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct assoc_array_ptr *root = READ_ONCE(array->root); /* Address dependency. */
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (!root)
14162306a36Sopenharmony_ci		return 0;
14262306a36Sopenharmony_ci	return assoc_array_subtree_iterate(root, NULL, iterator, iterator_data);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cienum assoc_array_walk_status {
14662306a36Sopenharmony_ci	assoc_array_walk_tree_empty,
14762306a36Sopenharmony_ci	assoc_array_walk_found_terminal_node,
14862306a36Sopenharmony_ci	assoc_array_walk_found_wrong_shortcut,
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistruct assoc_array_walk_result {
15262306a36Sopenharmony_ci	struct {
15362306a36Sopenharmony_ci		struct assoc_array_node	*node;	/* Node in which leaf might be found */
15462306a36Sopenharmony_ci		int		level;
15562306a36Sopenharmony_ci		int		slot;
15662306a36Sopenharmony_ci	} terminal_node;
15762306a36Sopenharmony_ci	struct {
15862306a36Sopenharmony_ci		struct assoc_array_shortcut *shortcut;
15962306a36Sopenharmony_ci		int		level;
16062306a36Sopenharmony_ci		int		sc_level;
16162306a36Sopenharmony_ci		unsigned long	sc_segments;
16262306a36Sopenharmony_ci		unsigned long	dissimilarity;
16362306a36Sopenharmony_ci	} wrong_shortcut;
16462306a36Sopenharmony_ci};
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/*
16762306a36Sopenharmony_ci * Navigate through the internal tree looking for the closest node to the key.
16862306a36Sopenharmony_ci */
16962306a36Sopenharmony_cistatic enum assoc_array_walk_status
17062306a36Sopenharmony_ciassoc_array_walk(const struct assoc_array *array,
17162306a36Sopenharmony_ci		 const struct assoc_array_ops *ops,
17262306a36Sopenharmony_ci		 const void *index_key,
17362306a36Sopenharmony_ci		 struct assoc_array_walk_result *result)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct assoc_array_shortcut *shortcut;
17662306a36Sopenharmony_ci	struct assoc_array_node *node;
17762306a36Sopenharmony_ci	struct assoc_array_ptr *cursor, *ptr;
17862306a36Sopenharmony_ci	unsigned long sc_segments, dissimilarity;
17962306a36Sopenharmony_ci	unsigned long segments;
18062306a36Sopenharmony_ci	int level, sc_level, next_sc_level;
18162306a36Sopenharmony_ci	int slot;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	pr_devel("-->%s()\n", __func__);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	cursor = READ_ONCE(array->root);  /* Address dependency. */
18662306a36Sopenharmony_ci	if (!cursor)
18762306a36Sopenharmony_ci		return assoc_array_walk_tree_empty;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	level = 0;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* Use segments from the key for the new leaf to navigate through the
19262306a36Sopenharmony_ci	 * internal tree, skipping through nodes and shortcuts that are on
19362306a36Sopenharmony_ci	 * route to the destination.  Eventually we'll come to a slot that is
19462306a36Sopenharmony_ci	 * either empty or contains a leaf at which point we've found a node in
19562306a36Sopenharmony_ci	 * which the leaf we're looking for might be found or into which it
19662306a36Sopenharmony_ci	 * should be inserted.
19762306a36Sopenharmony_ci	 */
19862306a36Sopenharmony_cijumped:
19962306a36Sopenharmony_ci	segments = ops->get_key_chunk(index_key, level);
20062306a36Sopenharmony_ci	pr_devel("segments[%d]: %lx\n", level, segments);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (assoc_array_ptr_is_shortcut(cursor))
20362306a36Sopenharmony_ci		goto follow_shortcut;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ciconsider_node:
20662306a36Sopenharmony_ci	node = assoc_array_ptr_to_node(cursor);
20762306a36Sopenharmony_ci	slot = segments >> (level & ASSOC_ARRAY_KEY_CHUNK_MASK);
20862306a36Sopenharmony_ci	slot &= ASSOC_ARRAY_FAN_MASK;
20962306a36Sopenharmony_ci	ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	pr_devel("consider slot %x [ix=%d type=%lu]\n",
21262306a36Sopenharmony_ci		 slot, level, (unsigned long)ptr & 3);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (!assoc_array_ptr_is_meta(ptr)) {
21562306a36Sopenharmony_ci		/* The node doesn't have a node/shortcut pointer in the slot
21662306a36Sopenharmony_ci		 * corresponding to the index key that we have to follow.
21762306a36Sopenharmony_ci		 */
21862306a36Sopenharmony_ci		result->terminal_node.node = node;
21962306a36Sopenharmony_ci		result->terminal_node.level = level;
22062306a36Sopenharmony_ci		result->terminal_node.slot = slot;
22162306a36Sopenharmony_ci		pr_devel("<--%s() = terminal_node\n", __func__);
22262306a36Sopenharmony_ci		return assoc_array_walk_found_terminal_node;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (assoc_array_ptr_is_node(ptr)) {
22662306a36Sopenharmony_ci		/* There is a pointer to a node in the slot corresponding to
22762306a36Sopenharmony_ci		 * this index key segment, so we need to follow it.
22862306a36Sopenharmony_ci		 */
22962306a36Sopenharmony_ci		cursor = ptr;
23062306a36Sopenharmony_ci		level += ASSOC_ARRAY_LEVEL_STEP;
23162306a36Sopenharmony_ci		if ((level & ASSOC_ARRAY_KEY_CHUNK_MASK) != 0)
23262306a36Sopenharmony_ci			goto consider_node;
23362306a36Sopenharmony_ci		goto jumped;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* There is a shortcut in the slot corresponding to the index key
23762306a36Sopenharmony_ci	 * segment.  We follow the shortcut if its partial index key matches
23862306a36Sopenharmony_ci	 * this leaf's.  Otherwise we need to split the shortcut.
23962306a36Sopenharmony_ci	 */
24062306a36Sopenharmony_ci	cursor = ptr;
24162306a36Sopenharmony_cifollow_shortcut:
24262306a36Sopenharmony_ci	shortcut = assoc_array_ptr_to_shortcut(cursor);
24362306a36Sopenharmony_ci	pr_devel("shortcut to %d\n", shortcut->skip_to_level);
24462306a36Sopenharmony_ci	sc_level = level + ASSOC_ARRAY_LEVEL_STEP;
24562306a36Sopenharmony_ci	BUG_ON(sc_level > shortcut->skip_to_level);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	do {
24862306a36Sopenharmony_ci		/* Check the leaf against the shortcut's index key a word at a
24962306a36Sopenharmony_ci		 * time, trimming the final word (the shortcut stores the index
25062306a36Sopenharmony_ci		 * key completely from the root to the shortcut's target).
25162306a36Sopenharmony_ci		 */
25262306a36Sopenharmony_ci		if ((sc_level & ASSOC_ARRAY_KEY_CHUNK_MASK) == 0)
25362306a36Sopenharmony_ci			segments = ops->get_key_chunk(index_key, sc_level);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
25662306a36Sopenharmony_ci		dissimilarity = segments ^ sc_segments;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) {
25962306a36Sopenharmony_ci			/* Trim segments that are beyond the shortcut */
26062306a36Sopenharmony_ci			int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
26162306a36Sopenharmony_ci			dissimilarity &= ~(ULONG_MAX << shift);
26262306a36Sopenharmony_ci			next_sc_level = shortcut->skip_to_level;
26362306a36Sopenharmony_ci		} else {
26462306a36Sopenharmony_ci			next_sc_level = sc_level + ASSOC_ARRAY_KEY_CHUNK_SIZE;
26562306a36Sopenharmony_ci			next_sc_level = round_down(next_sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		if (dissimilarity != 0) {
26962306a36Sopenharmony_ci			/* This shortcut points elsewhere */
27062306a36Sopenharmony_ci			result->wrong_shortcut.shortcut = shortcut;
27162306a36Sopenharmony_ci			result->wrong_shortcut.level = level;
27262306a36Sopenharmony_ci			result->wrong_shortcut.sc_level = sc_level;
27362306a36Sopenharmony_ci			result->wrong_shortcut.sc_segments = sc_segments;
27462306a36Sopenharmony_ci			result->wrong_shortcut.dissimilarity = dissimilarity;
27562306a36Sopenharmony_ci			return assoc_array_walk_found_wrong_shortcut;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		sc_level = next_sc_level;
27962306a36Sopenharmony_ci	} while (sc_level < shortcut->skip_to_level);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* The shortcut matches the leaf's index to this point. */
28262306a36Sopenharmony_ci	cursor = READ_ONCE(shortcut->next_node); /* Address dependency. */
28362306a36Sopenharmony_ci	if (((level ^ sc_level) & ~ASSOC_ARRAY_KEY_CHUNK_MASK) != 0) {
28462306a36Sopenharmony_ci		level = sc_level;
28562306a36Sopenharmony_ci		goto jumped;
28662306a36Sopenharmony_ci	} else {
28762306a36Sopenharmony_ci		level = sc_level;
28862306a36Sopenharmony_ci		goto consider_node;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci/**
29362306a36Sopenharmony_ci * assoc_array_find - Find an object by index key
29462306a36Sopenharmony_ci * @array: The associative array to search.
29562306a36Sopenharmony_ci * @ops: The operations to use.
29662306a36Sopenharmony_ci * @index_key: The key to the object.
29762306a36Sopenharmony_ci *
29862306a36Sopenharmony_ci * Find an object in an associative array by walking through the internal tree
29962306a36Sopenharmony_ci * to the node that should contain the object and then searching the leaves
30062306a36Sopenharmony_ci * there.  NULL is returned if the requested object was not found in the array.
30162306a36Sopenharmony_ci *
30262306a36Sopenharmony_ci * The caller must hold the RCU read lock or better.
30362306a36Sopenharmony_ci */
30462306a36Sopenharmony_civoid *assoc_array_find(const struct assoc_array *array,
30562306a36Sopenharmony_ci		       const struct assoc_array_ops *ops,
30662306a36Sopenharmony_ci		       const void *index_key)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct assoc_array_walk_result result;
30962306a36Sopenharmony_ci	const struct assoc_array_node *node;
31062306a36Sopenharmony_ci	const struct assoc_array_ptr *ptr;
31162306a36Sopenharmony_ci	const void *leaf;
31262306a36Sopenharmony_ci	int slot;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (assoc_array_walk(array, ops, index_key, &result) !=
31562306a36Sopenharmony_ci	    assoc_array_walk_found_terminal_node)
31662306a36Sopenharmony_ci		return NULL;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	node = result.terminal_node.node;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* If the target key is available to us, it's has to be pointed to by
32162306a36Sopenharmony_ci	 * the terminal node.
32262306a36Sopenharmony_ci	 */
32362306a36Sopenharmony_ci	for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
32462306a36Sopenharmony_ci		ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
32562306a36Sopenharmony_ci		if (ptr && assoc_array_ptr_is_leaf(ptr)) {
32662306a36Sopenharmony_ci			/* We need a barrier between the read of the pointer
32762306a36Sopenharmony_ci			 * and dereferencing the pointer - but only if we are
32862306a36Sopenharmony_ci			 * actually going to dereference it.
32962306a36Sopenharmony_ci			 */
33062306a36Sopenharmony_ci			leaf = assoc_array_ptr_to_leaf(ptr);
33162306a36Sopenharmony_ci			if (ops->compare_object(leaf, index_key))
33262306a36Sopenharmony_ci				return (void *)leaf;
33362306a36Sopenharmony_ci		}
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	return NULL;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/*
34062306a36Sopenharmony_ci * Destructively iterate over an associative array.  The caller must prevent
34162306a36Sopenharmony_ci * other simultaneous accesses.
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_cistatic void assoc_array_destroy_subtree(struct assoc_array_ptr *root,
34462306a36Sopenharmony_ci					const struct assoc_array_ops *ops)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct assoc_array_shortcut *shortcut;
34762306a36Sopenharmony_ci	struct assoc_array_node *node;
34862306a36Sopenharmony_ci	struct assoc_array_ptr *cursor, *parent = NULL;
34962306a36Sopenharmony_ci	int slot = -1;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	pr_devel("-->%s()\n", __func__);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	cursor = root;
35462306a36Sopenharmony_ci	if (!cursor) {
35562306a36Sopenharmony_ci		pr_devel("empty\n");
35662306a36Sopenharmony_ci		return;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cimove_to_meta:
36062306a36Sopenharmony_ci	if (assoc_array_ptr_is_shortcut(cursor)) {
36162306a36Sopenharmony_ci		/* Descend through a shortcut */
36262306a36Sopenharmony_ci		pr_devel("[%d] shortcut\n", slot);
36362306a36Sopenharmony_ci		BUG_ON(!assoc_array_ptr_is_shortcut(cursor));
36462306a36Sopenharmony_ci		shortcut = assoc_array_ptr_to_shortcut(cursor);
36562306a36Sopenharmony_ci		BUG_ON(shortcut->back_pointer != parent);
36662306a36Sopenharmony_ci		BUG_ON(slot != -1 && shortcut->parent_slot != slot);
36762306a36Sopenharmony_ci		parent = cursor;
36862306a36Sopenharmony_ci		cursor = shortcut->next_node;
36962306a36Sopenharmony_ci		slot = -1;
37062306a36Sopenharmony_ci		BUG_ON(!assoc_array_ptr_is_node(cursor));
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	pr_devel("[%d] node\n", slot);
37462306a36Sopenharmony_ci	node = assoc_array_ptr_to_node(cursor);
37562306a36Sopenharmony_ci	BUG_ON(node->back_pointer != parent);
37662306a36Sopenharmony_ci	BUG_ON(slot != -1 && node->parent_slot != slot);
37762306a36Sopenharmony_ci	slot = 0;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cicontinue_node:
38062306a36Sopenharmony_ci	pr_devel("Node %p [back=%p]\n", node, node->back_pointer);
38162306a36Sopenharmony_ci	for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
38262306a36Sopenharmony_ci		struct assoc_array_ptr *ptr = node->slots[slot];
38362306a36Sopenharmony_ci		if (!ptr)
38462306a36Sopenharmony_ci			continue;
38562306a36Sopenharmony_ci		if (assoc_array_ptr_is_meta(ptr)) {
38662306a36Sopenharmony_ci			parent = cursor;
38762306a36Sopenharmony_ci			cursor = ptr;
38862306a36Sopenharmony_ci			goto move_to_meta;
38962306a36Sopenharmony_ci		}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci		if (ops) {
39262306a36Sopenharmony_ci			pr_devel("[%d] free leaf\n", slot);
39362306a36Sopenharmony_ci			ops->free_object(assoc_array_ptr_to_leaf(ptr));
39462306a36Sopenharmony_ci		}
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	parent = node->back_pointer;
39862306a36Sopenharmony_ci	slot = node->parent_slot;
39962306a36Sopenharmony_ci	pr_devel("free node\n");
40062306a36Sopenharmony_ci	kfree(node);
40162306a36Sopenharmony_ci	if (!parent)
40262306a36Sopenharmony_ci		return; /* Done */
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/* Move back up to the parent (may need to free a shortcut on
40562306a36Sopenharmony_ci	 * the way up) */
40662306a36Sopenharmony_ci	if (assoc_array_ptr_is_shortcut(parent)) {
40762306a36Sopenharmony_ci		shortcut = assoc_array_ptr_to_shortcut(parent);
40862306a36Sopenharmony_ci		BUG_ON(shortcut->next_node != cursor);
40962306a36Sopenharmony_ci		cursor = parent;
41062306a36Sopenharmony_ci		parent = shortcut->back_pointer;
41162306a36Sopenharmony_ci		slot = shortcut->parent_slot;
41262306a36Sopenharmony_ci		pr_devel("free shortcut\n");
41362306a36Sopenharmony_ci		kfree(shortcut);
41462306a36Sopenharmony_ci		if (!parent)
41562306a36Sopenharmony_ci			return;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		BUG_ON(!assoc_array_ptr_is_node(parent));
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* Ascend to next slot in parent node */
42162306a36Sopenharmony_ci	pr_devel("ascend to %p[%d]\n", parent, slot);
42262306a36Sopenharmony_ci	cursor = parent;
42362306a36Sopenharmony_ci	node = assoc_array_ptr_to_node(cursor);
42462306a36Sopenharmony_ci	slot++;
42562306a36Sopenharmony_ci	goto continue_node;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci/**
42962306a36Sopenharmony_ci * assoc_array_destroy - Destroy an associative array
43062306a36Sopenharmony_ci * @array: The array to destroy.
43162306a36Sopenharmony_ci * @ops: The operations to use.
43262306a36Sopenharmony_ci *
43362306a36Sopenharmony_ci * Discard all metadata and free all objects in an associative array.  The
43462306a36Sopenharmony_ci * array will be empty and ready to use again upon completion.  This function
43562306a36Sopenharmony_ci * cannot fail.
43662306a36Sopenharmony_ci *
43762306a36Sopenharmony_ci * The caller must prevent all other accesses whilst this takes place as no
43862306a36Sopenharmony_ci * attempt is made to adjust pointers gracefully to permit RCU readlock-holding
43962306a36Sopenharmony_ci * accesses to continue.  On the other hand, no memory allocation is required.
44062306a36Sopenharmony_ci */
44162306a36Sopenharmony_civoid assoc_array_destroy(struct assoc_array *array,
44262306a36Sopenharmony_ci			 const struct assoc_array_ops *ops)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	assoc_array_destroy_subtree(array->root, ops);
44562306a36Sopenharmony_ci	array->root = NULL;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci/*
44962306a36Sopenharmony_ci * Handle insertion into an empty tree.
45062306a36Sopenharmony_ci */
45162306a36Sopenharmony_cistatic bool assoc_array_insert_in_empty_tree(struct assoc_array_edit *edit)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct assoc_array_node *new_n0;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	pr_devel("-->%s()\n", __func__);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
45862306a36Sopenharmony_ci	if (!new_n0)
45962306a36Sopenharmony_ci		return false;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
46262306a36Sopenharmony_ci	edit->leaf_p = &new_n0->slots[0];
46362306a36Sopenharmony_ci	edit->adjust_count_on = new_n0;
46462306a36Sopenharmony_ci	edit->set[0].ptr = &edit->array->root;
46562306a36Sopenharmony_ci	edit->set[0].to = assoc_array_node_to_ptr(new_n0);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	pr_devel("<--%s() = ok [no root]\n", __func__);
46862306a36Sopenharmony_ci	return true;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci/*
47262306a36Sopenharmony_ci * Handle insertion into a terminal node.
47362306a36Sopenharmony_ci */
47462306a36Sopenharmony_cistatic bool assoc_array_insert_into_terminal_node(struct assoc_array_edit *edit,
47562306a36Sopenharmony_ci						  const struct assoc_array_ops *ops,
47662306a36Sopenharmony_ci						  const void *index_key,
47762306a36Sopenharmony_ci						  struct assoc_array_walk_result *result)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct assoc_array_shortcut *shortcut, *new_s0;
48062306a36Sopenharmony_ci	struct assoc_array_node *node, *new_n0, *new_n1, *side;
48162306a36Sopenharmony_ci	struct assoc_array_ptr *ptr;
48262306a36Sopenharmony_ci	unsigned long dissimilarity, base_seg, blank;
48362306a36Sopenharmony_ci	size_t keylen;
48462306a36Sopenharmony_ci	bool have_meta;
48562306a36Sopenharmony_ci	int level, diff;
48662306a36Sopenharmony_ci	int slot, next_slot, free_slot, i, j;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	node	= result->terminal_node.node;
48962306a36Sopenharmony_ci	level	= result->terminal_node.level;
49062306a36Sopenharmony_ci	edit->segment_cache[ASSOC_ARRAY_FAN_OUT] = result->terminal_node.slot;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	pr_devel("-->%s()\n", __func__);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/* We arrived at a node which doesn't have an onward node or shortcut
49562306a36Sopenharmony_ci	 * pointer that we have to follow.  This means that (a) the leaf we
49662306a36Sopenharmony_ci	 * want must go here (either by insertion or replacement) or (b) we
49762306a36Sopenharmony_ci	 * need to split this node and insert in one of the fragments.
49862306a36Sopenharmony_ci	 */
49962306a36Sopenharmony_ci	free_slot = -1;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	/* Firstly, we have to check the leaves in this node to see if there's
50262306a36Sopenharmony_ci	 * a matching one we should replace in place.
50362306a36Sopenharmony_ci	 */
50462306a36Sopenharmony_ci	for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
50562306a36Sopenharmony_ci		ptr = node->slots[i];
50662306a36Sopenharmony_ci		if (!ptr) {
50762306a36Sopenharmony_ci			free_slot = i;
50862306a36Sopenharmony_ci			continue;
50962306a36Sopenharmony_ci		}
51062306a36Sopenharmony_ci		if (assoc_array_ptr_is_leaf(ptr) &&
51162306a36Sopenharmony_ci		    ops->compare_object(assoc_array_ptr_to_leaf(ptr),
51262306a36Sopenharmony_ci					index_key)) {
51362306a36Sopenharmony_ci			pr_devel("replace in slot %d\n", i);
51462306a36Sopenharmony_ci			edit->leaf_p = &node->slots[i];
51562306a36Sopenharmony_ci			edit->dead_leaf = node->slots[i];
51662306a36Sopenharmony_ci			pr_devel("<--%s() = ok [replace]\n", __func__);
51762306a36Sopenharmony_ci			return true;
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/* If there is a free slot in this node then we can just insert the
52262306a36Sopenharmony_ci	 * leaf here.
52362306a36Sopenharmony_ci	 */
52462306a36Sopenharmony_ci	if (free_slot >= 0) {
52562306a36Sopenharmony_ci		pr_devel("insert in free slot %d\n", free_slot);
52662306a36Sopenharmony_ci		edit->leaf_p = &node->slots[free_slot];
52762306a36Sopenharmony_ci		edit->adjust_count_on = node;
52862306a36Sopenharmony_ci		pr_devel("<--%s() = ok [insert]\n", __func__);
52962306a36Sopenharmony_ci		return true;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* The node has no spare slots - so we're either going to have to split
53362306a36Sopenharmony_ci	 * it or insert another node before it.
53462306a36Sopenharmony_ci	 *
53562306a36Sopenharmony_ci	 * Whatever, we're going to need at least two new nodes - so allocate
53662306a36Sopenharmony_ci	 * those now.  We may also need a new shortcut, but we deal with that
53762306a36Sopenharmony_ci	 * when we need it.
53862306a36Sopenharmony_ci	 */
53962306a36Sopenharmony_ci	new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
54062306a36Sopenharmony_ci	if (!new_n0)
54162306a36Sopenharmony_ci		return false;
54262306a36Sopenharmony_ci	edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
54362306a36Sopenharmony_ci	new_n1 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
54462306a36Sopenharmony_ci	if (!new_n1)
54562306a36Sopenharmony_ci		return false;
54662306a36Sopenharmony_ci	edit->new_meta[1] = assoc_array_node_to_ptr(new_n1);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* We need to find out how similar the leaves are. */
54962306a36Sopenharmony_ci	pr_devel("no spare slots\n");
55062306a36Sopenharmony_ci	have_meta = false;
55162306a36Sopenharmony_ci	for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
55262306a36Sopenharmony_ci		ptr = node->slots[i];
55362306a36Sopenharmony_ci		if (assoc_array_ptr_is_meta(ptr)) {
55462306a36Sopenharmony_ci			edit->segment_cache[i] = 0xff;
55562306a36Sopenharmony_ci			have_meta = true;
55662306a36Sopenharmony_ci			continue;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci		base_seg = ops->get_object_key_chunk(
55962306a36Sopenharmony_ci			assoc_array_ptr_to_leaf(ptr), level);
56062306a36Sopenharmony_ci		base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
56162306a36Sopenharmony_ci		edit->segment_cache[i] = base_seg & ASSOC_ARRAY_FAN_MASK;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (have_meta) {
56562306a36Sopenharmony_ci		pr_devel("have meta\n");
56662306a36Sopenharmony_ci		goto split_node;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/* The node contains only leaves */
57062306a36Sopenharmony_ci	dissimilarity = 0;
57162306a36Sopenharmony_ci	base_seg = edit->segment_cache[0];
57262306a36Sopenharmony_ci	for (i = 1; i < ASSOC_ARRAY_FAN_OUT; i++)
57362306a36Sopenharmony_ci		dissimilarity |= edit->segment_cache[i] ^ base_seg;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	pr_devel("only leaves; dissimilarity=%lx\n", dissimilarity);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if ((dissimilarity & ASSOC_ARRAY_FAN_MASK) == 0) {
57862306a36Sopenharmony_ci		/* The old leaves all cluster in the same slot.  We will need
57962306a36Sopenharmony_ci		 * to insert a shortcut if the new node wants to cluster with them.
58062306a36Sopenharmony_ci		 */
58162306a36Sopenharmony_ci		if ((edit->segment_cache[ASSOC_ARRAY_FAN_OUT] ^ base_seg) == 0)
58262306a36Sopenharmony_ci			goto all_leaves_cluster_together;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		/* Otherwise all the old leaves cluster in the same slot, but
58562306a36Sopenharmony_ci		 * the new leaf wants to go into a different slot - so we
58662306a36Sopenharmony_ci		 * create a new node (n0) to hold the new leaf and a pointer to
58762306a36Sopenharmony_ci		 * a new node (n1) holding all the old leaves.
58862306a36Sopenharmony_ci		 *
58962306a36Sopenharmony_ci		 * This can be done by falling through to the node splitting
59062306a36Sopenharmony_ci		 * path.
59162306a36Sopenharmony_ci		 */
59262306a36Sopenharmony_ci		pr_devel("present leaves cluster but not new leaf\n");
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cisplit_node:
59662306a36Sopenharmony_ci	pr_devel("split node\n");
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	/* We need to split the current node.  The node must contain anything
59962306a36Sopenharmony_ci	 * from a single leaf (in the one leaf case, this leaf will cluster
60062306a36Sopenharmony_ci	 * with the new leaf) and the rest meta-pointers, to all leaves, some
60162306a36Sopenharmony_ci	 * of which may cluster.
60262306a36Sopenharmony_ci	 *
60362306a36Sopenharmony_ci	 * It won't contain the case in which all the current leaves plus the
60462306a36Sopenharmony_ci	 * new leaves want to cluster in the same slot.
60562306a36Sopenharmony_ci	 *
60662306a36Sopenharmony_ci	 * We need to expel at least two leaves out of a set consisting of the
60762306a36Sopenharmony_ci	 * leaves in the node and the new leaf.  The current meta pointers can
60862306a36Sopenharmony_ci	 * just be copied as they shouldn't cluster with any of the leaves.
60962306a36Sopenharmony_ci	 *
61062306a36Sopenharmony_ci	 * We need a new node (n0) to replace the current one and a new node to
61162306a36Sopenharmony_ci	 * take the expelled nodes (n1).
61262306a36Sopenharmony_ci	 */
61362306a36Sopenharmony_ci	edit->set[0].to = assoc_array_node_to_ptr(new_n0);
61462306a36Sopenharmony_ci	new_n0->back_pointer = node->back_pointer;
61562306a36Sopenharmony_ci	new_n0->parent_slot = node->parent_slot;
61662306a36Sopenharmony_ci	new_n1->back_pointer = assoc_array_node_to_ptr(new_n0);
61762306a36Sopenharmony_ci	new_n1->parent_slot = -1; /* Need to calculate this */
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cido_split_node:
62062306a36Sopenharmony_ci	pr_devel("do_split_node\n");
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch;
62362306a36Sopenharmony_ci	new_n1->nr_leaves_on_branch = 0;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	/* Begin by finding two matching leaves.  There have to be at least two
62662306a36Sopenharmony_ci	 * that match - even if there are meta pointers - because any leaf that
62762306a36Sopenharmony_ci	 * would match a slot with a meta pointer in it must be somewhere
62862306a36Sopenharmony_ci	 * behind that meta pointer and cannot be here.  Further, given N
62962306a36Sopenharmony_ci	 * remaining leaf slots, we now have N+1 leaves to go in them.
63062306a36Sopenharmony_ci	 */
63162306a36Sopenharmony_ci	for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
63262306a36Sopenharmony_ci		slot = edit->segment_cache[i];
63362306a36Sopenharmony_ci		if (slot != 0xff)
63462306a36Sopenharmony_ci			for (j = i + 1; j < ASSOC_ARRAY_FAN_OUT + 1; j++)
63562306a36Sopenharmony_ci				if (edit->segment_cache[j] == slot)
63662306a36Sopenharmony_ci					goto found_slot_for_multiple_occupancy;
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_cifound_slot_for_multiple_occupancy:
63962306a36Sopenharmony_ci	pr_devel("same slot: %x %x [%02x]\n", i, j, slot);
64062306a36Sopenharmony_ci	BUG_ON(i >= ASSOC_ARRAY_FAN_OUT);
64162306a36Sopenharmony_ci	BUG_ON(j >= ASSOC_ARRAY_FAN_OUT + 1);
64262306a36Sopenharmony_ci	BUG_ON(slot >= ASSOC_ARRAY_FAN_OUT);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	new_n1->parent_slot = slot;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	/* Metadata pointers cannot change slot */
64762306a36Sopenharmony_ci	for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++)
64862306a36Sopenharmony_ci		if (assoc_array_ptr_is_meta(node->slots[i]))
64962306a36Sopenharmony_ci			new_n0->slots[i] = node->slots[i];
65062306a36Sopenharmony_ci		else
65162306a36Sopenharmony_ci			new_n0->slots[i] = NULL;
65262306a36Sopenharmony_ci	BUG_ON(new_n0->slots[slot] != NULL);
65362306a36Sopenharmony_ci	new_n0->slots[slot] = assoc_array_node_to_ptr(new_n1);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	/* Filter the leaf pointers between the new nodes */
65662306a36Sopenharmony_ci	free_slot = -1;
65762306a36Sopenharmony_ci	next_slot = 0;
65862306a36Sopenharmony_ci	for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
65962306a36Sopenharmony_ci		if (assoc_array_ptr_is_meta(node->slots[i]))
66062306a36Sopenharmony_ci			continue;
66162306a36Sopenharmony_ci		if (edit->segment_cache[i] == slot) {
66262306a36Sopenharmony_ci			new_n1->slots[next_slot++] = node->slots[i];
66362306a36Sopenharmony_ci			new_n1->nr_leaves_on_branch++;
66462306a36Sopenharmony_ci		} else {
66562306a36Sopenharmony_ci			do {
66662306a36Sopenharmony_ci				free_slot++;
66762306a36Sopenharmony_ci			} while (new_n0->slots[free_slot] != NULL);
66862306a36Sopenharmony_ci			new_n0->slots[free_slot] = node->slots[i];
66962306a36Sopenharmony_ci		}
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	pr_devel("filtered: f=%x n=%x\n", free_slot, next_slot);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	if (edit->segment_cache[ASSOC_ARRAY_FAN_OUT] != slot) {
67562306a36Sopenharmony_ci		do {
67662306a36Sopenharmony_ci			free_slot++;
67762306a36Sopenharmony_ci		} while (new_n0->slots[free_slot] != NULL);
67862306a36Sopenharmony_ci		edit->leaf_p = &new_n0->slots[free_slot];
67962306a36Sopenharmony_ci		edit->adjust_count_on = new_n0;
68062306a36Sopenharmony_ci	} else {
68162306a36Sopenharmony_ci		edit->leaf_p = &new_n1->slots[next_slot++];
68262306a36Sopenharmony_ci		edit->adjust_count_on = new_n1;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	BUG_ON(next_slot <= 1);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	edit->set_backpointers_to = assoc_array_node_to_ptr(new_n0);
68862306a36Sopenharmony_ci	for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
68962306a36Sopenharmony_ci		if (edit->segment_cache[i] == 0xff) {
69062306a36Sopenharmony_ci			ptr = node->slots[i];
69162306a36Sopenharmony_ci			BUG_ON(assoc_array_ptr_is_leaf(ptr));
69262306a36Sopenharmony_ci			if (assoc_array_ptr_is_node(ptr)) {
69362306a36Sopenharmony_ci				side = assoc_array_ptr_to_node(ptr);
69462306a36Sopenharmony_ci				edit->set_backpointers[i] = &side->back_pointer;
69562306a36Sopenharmony_ci			} else {
69662306a36Sopenharmony_ci				shortcut = assoc_array_ptr_to_shortcut(ptr);
69762306a36Sopenharmony_ci				edit->set_backpointers[i] = &shortcut->back_pointer;
69862306a36Sopenharmony_ci			}
69962306a36Sopenharmony_ci		}
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	ptr = node->back_pointer;
70362306a36Sopenharmony_ci	if (!ptr)
70462306a36Sopenharmony_ci		edit->set[0].ptr = &edit->array->root;
70562306a36Sopenharmony_ci	else if (assoc_array_ptr_is_node(ptr))
70662306a36Sopenharmony_ci		edit->set[0].ptr = &assoc_array_ptr_to_node(ptr)->slots[node->parent_slot];
70762306a36Sopenharmony_ci	else
70862306a36Sopenharmony_ci		edit->set[0].ptr = &assoc_array_ptr_to_shortcut(ptr)->next_node;
70962306a36Sopenharmony_ci	edit->excised_meta[0] = assoc_array_node_to_ptr(node);
71062306a36Sopenharmony_ci	pr_devel("<--%s() = ok [split node]\n", __func__);
71162306a36Sopenharmony_ci	return true;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ciall_leaves_cluster_together:
71462306a36Sopenharmony_ci	/* All the leaves, new and old, want to cluster together in this node
71562306a36Sopenharmony_ci	 * in the same slot, so we have to replace this node with a shortcut to
71662306a36Sopenharmony_ci	 * skip over the identical parts of the key and then place a pair of
71762306a36Sopenharmony_ci	 * nodes, one inside the other, at the end of the shortcut and
71862306a36Sopenharmony_ci	 * distribute the keys between them.
71962306a36Sopenharmony_ci	 *
72062306a36Sopenharmony_ci	 * Firstly we need to work out where the leaves start diverging as a
72162306a36Sopenharmony_ci	 * bit position into their keys so that we know how big the shortcut
72262306a36Sopenharmony_ci	 * needs to be.
72362306a36Sopenharmony_ci	 *
72462306a36Sopenharmony_ci	 * We only need to make a single pass of N of the N+1 leaves because if
72562306a36Sopenharmony_ci	 * any keys differ between themselves at bit X then at least one of
72662306a36Sopenharmony_ci	 * them must also differ with the base key at bit X or before.
72762306a36Sopenharmony_ci	 */
72862306a36Sopenharmony_ci	pr_devel("all leaves cluster together\n");
72962306a36Sopenharmony_ci	diff = INT_MAX;
73062306a36Sopenharmony_ci	for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
73162306a36Sopenharmony_ci		int x = ops->diff_objects(assoc_array_ptr_to_leaf(node->slots[i]),
73262306a36Sopenharmony_ci					  index_key);
73362306a36Sopenharmony_ci		if (x < diff) {
73462306a36Sopenharmony_ci			BUG_ON(x < 0);
73562306a36Sopenharmony_ci			diff = x;
73662306a36Sopenharmony_ci		}
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci	BUG_ON(diff == INT_MAX);
73962306a36Sopenharmony_ci	BUG_ON(diff < level + ASSOC_ARRAY_LEVEL_STEP);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE);
74262306a36Sopenharmony_ci	keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	new_s0 = kzalloc(struct_size(new_s0, index_key, keylen), GFP_KERNEL);
74562306a36Sopenharmony_ci	if (!new_s0)
74662306a36Sopenharmony_ci		return false;
74762306a36Sopenharmony_ci	edit->new_meta[2] = assoc_array_shortcut_to_ptr(new_s0);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	edit->set[0].to = assoc_array_shortcut_to_ptr(new_s0);
75062306a36Sopenharmony_ci	new_s0->back_pointer = node->back_pointer;
75162306a36Sopenharmony_ci	new_s0->parent_slot = node->parent_slot;
75262306a36Sopenharmony_ci	new_s0->next_node = assoc_array_node_to_ptr(new_n0);
75362306a36Sopenharmony_ci	new_n0->back_pointer = assoc_array_shortcut_to_ptr(new_s0);
75462306a36Sopenharmony_ci	new_n0->parent_slot = 0;
75562306a36Sopenharmony_ci	new_n1->back_pointer = assoc_array_node_to_ptr(new_n0);
75662306a36Sopenharmony_ci	new_n1->parent_slot = -1; /* Need to calculate this */
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	new_s0->skip_to_level = level = diff & ~ASSOC_ARRAY_LEVEL_STEP_MASK;
75962306a36Sopenharmony_ci	pr_devel("skip_to_level = %d [diff %d]\n", level, diff);
76062306a36Sopenharmony_ci	BUG_ON(level <= 0);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	for (i = 0; i < keylen; i++)
76362306a36Sopenharmony_ci		new_s0->index_key[i] =
76462306a36Sopenharmony_ci			ops->get_key_chunk(index_key, i * ASSOC_ARRAY_KEY_CHUNK_SIZE);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (level & ASSOC_ARRAY_KEY_CHUNK_MASK) {
76762306a36Sopenharmony_ci		blank = ULONG_MAX << (level & ASSOC_ARRAY_KEY_CHUNK_MASK);
76862306a36Sopenharmony_ci		pr_devel("blank off [%zu] %d: %lx\n", keylen - 1, level, blank);
76962306a36Sopenharmony_ci		new_s0->index_key[keylen - 1] &= ~blank;
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	/* This now reduces to a node splitting exercise for which we'll need
77362306a36Sopenharmony_ci	 * to regenerate the disparity table.
77462306a36Sopenharmony_ci	 */
77562306a36Sopenharmony_ci	for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
77662306a36Sopenharmony_ci		ptr = node->slots[i];
77762306a36Sopenharmony_ci		base_seg = ops->get_object_key_chunk(assoc_array_ptr_to_leaf(ptr),
77862306a36Sopenharmony_ci						     level);
77962306a36Sopenharmony_ci		base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
78062306a36Sopenharmony_ci		edit->segment_cache[i] = base_seg & ASSOC_ARRAY_FAN_MASK;
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	base_seg = ops->get_key_chunk(index_key, level);
78462306a36Sopenharmony_ci	base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
78562306a36Sopenharmony_ci	edit->segment_cache[ASSOC_ARRAY_FAN_OUT] = base_seg & ASSOC_ARRAY_FAN_MASK;
78662306a36Sopenharmony_ci	goto do_split_node;
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci/*
79062306a36Sopenharmony_ci * Handle insertion into the middle of a shortcut.
79162306a36Sopenharmony_ci */
79262306a36Sopenharmony_cistatic bool assoc_array_insert_mid_shortcut(struct assoc_array_edit *edit,
79362306a36Sopenharmony_ci					    const struct assoc_array_ops *ops,
79462306a36Sopenharmony_ci					    struct assoc_array_walk_result *result)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	struct assoc_array_shortcut *shortcut, *new_s0, *new_s1;
79762306a36Sopenharmony_ci	struct assoc_array_node *node, *new_n0, *side;
79862306a36Sopenharmony_ci	unsigned long sc_segments, dissimilarity, blank;
79962306a36Sopenharmony_ci	size_t keylen;
80062306a36Sopenharmony_ci	int level, sc_level, diff;
80162306a36Sopenharmony_ci	int sc_slot;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	shortcut	= result->wrong_shortcut.shortcut;
80462306a36Sopenharmony_ci	level		= result->wrong_shortcut.level;
80562306a36Sopenharmony_ci	sc_level	= result->wrong_shortcut.sc_level;
80662306a36Sopenharmony_ci	sc_segments	= result->wrong_shortcut.sc_segments;
80762306a36Sopenharmony_ci	dissimilarity	= result->wrong_shortcut.dissimilarity;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	pr_devel("-->%s(ix=%d dis=%lx scix=%d)\n",
81062306a36Sopenharmony_ci		 __func__, level, dissimilarity, sc_level);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	/* We need to split a shortcut and insert a node between the two
81362306a36Sopenharmony_ci	 * pieces.  Zero-length pieces will be dispensed with entirely.
81462306a36Sopenharmony_ci	 *
81562306a36Sopenharmony_ci	 * First of all, we need to find out in which level the first
81662306a36Sopenharmony_ci	 * difference was.
81762306a36Sopenharmony_ci	 */
81862306a36Sopenharmony_ci	diff = __ffs(dissimilarity);
81962306a36Sopenharmony_ci	diff &= ~ASSOC_ARRAY_LEVEL_STEP_MASK;
82062306a36Sopenharmony_ci	diff += sc_level & ~ASSOC_ARRAY_KEY_CHUNK_MASK;
82162306a36Sopenharmony_ci	pr_devel("diff=%d\n", diff);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	if (!shortcut->back_pointer) {
82462306a36Sopenharmony_ci		edit->set[0].ptr = &edit->array->root;
82562306a36Sopenharmony_ci	} else if (assoc_array_ptr_is_node(shortcut->back_pointer)) {
82662306a36Sopenharmony_ci		node = assoc_array_ptr_to_node(shortcut->back_pointer);
82762306a36Sopenharmony_ci		edit->set[0].ptr = &node->slots[shortcut->parent_slot];
82862306a36Sopenharmony_ci	} else {
82962306a36Sopenharmony_ci		BUG();
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	edit->excised_meta[0] = assoc_array_shortcut_to_ptr(shortcut);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	/* Create a new node now since we're going to need it anyway */
83562306a36Sopenharmony_ci	new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
83662306a36Sopenharmony_ci	if (!new_n0)
83762306a36Sopenharmony_ci		return false;
83862306a36Sopenharmony_ci	edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
83962306a36Sopenharmony_ci	edit->adjust_count_on = new_n0;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	/* Insert a new shortcut before the new node if this segment isn't of
84262306a36Sopenharmony_ci	 * zero length - otherwise we just connect the new node directly to the
84362306a36Sopenharmony_ci	 * parent.
84462306a36Sopenharmony_ci	 */
84562306a36Sopenharmony_ci	level += ASSOC_ARRAY_LEVEL_STEP;
84662306a36Sopenharmony_ci	if (diff > level) {
84762306a36Sopenharmony_ci		pr_devel("pre-shortcut %d...%d\n", level, diff);
84862306a36Sopenharmony_ci		keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE);
84962306a36Sopenharmony_ci		keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci		new_s0 = kzalloc(struct_size(new_s0, index_key, keylen),
85262306a36Sopenharmony_ci				 GFP_KERNEL);
85362306a36Sopenharmony_ci		if (!new_s0)
85462306a36Sopenharmony_ci			return false;
85562306a36Sopenharmony_ci		edit->new_meta[1] = assoc_array_shortcut_to_ptr(new_s0);
85662306a36Sopenharmony_ci		edit->set[0].to = assoc_array_shortcut_to_ptr(new_s0);
85762306a36Sopenharmony_ci		new_s0->back_pointer = shortcut->back_pointer;
85862306a36Sopenharmony_ci		new_s0->parent_slot = shortcut->parent_slot;
85962306a36Sopenharmony_ci		new_s0->next_node = assoc_array_node_to_ptr(new_n0);
86062306a36Sopenharmony_ci		new_s0->skip_to_level = diff;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		new_n0->back_pointer = assoc_array_shortcut_to_ptr(new_s0);
86362306a36Sopenharmony_ci		new_n0->parent_slot = 0;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci		memcpy(new_s0->index_key, shortcut->index_key,
86662306a36Sopenharmony_ci		       flex_array_size(new_s0, index_key, keylen));
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		blank = ULONG_MAX << (diff & ASSOC_ARRAY_KEY_CHUNK_MASK);
86962306a36Sopenharmony_ci		pr_devel("blank off [%zu] %d: %lx\n", keylen - 1, diff, blank);
87062306a36Sopenharmony_ci		new_s0->index_key[keylen - 1] &= ~blank;
87162306a36Sopenharmony_ci	} else {
87262306a36Sopenharmony_ci		pr_devel("no pre-shortcut\n");
87362306a36Sopenharmony_ci		edit->set[0].to = assoc_array_node_to_ptr(new_n0);
87462306a36Sopenharmony_ci		new_n0->back_pointer = shortcut->back_pointer;
87562306a36Sopenharmony_ci		new_n0->parent_slot = shortcut->parent_slot;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	side = assoc_array_ptr_to_node(shortcut->next_node);
87962306a36Sopenharmony_ci	new_n0->nr_leaves_on_branch = side->nr_leaves_on_branch;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	/* We need to know which slot in the new node is going to take a
88262306a36Sopenharmony_ci	 * metadata pointer.
88362306a36Sopenharmony_ci	 */
88462306a36Sopenharmony_ci	sc_slot = sc_segments >> (diff & ASSOC_ARRAY_KEY_CHUNK_MASK);
88562306a36Sopenharmony_ci	sc_slot &= ASSOC_ARRAY_FAN_MASK;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	pr_devel("new slot %lx >> %d -> %d\n",
88862306a36Sopenharmony_ci		 sc_segments, diff & ASSOC_ARRAY_KEY_CHUNK_MASK, sc_slot);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	/* Determine whether we need to follow the new node with a replacement
89162306a36Sopenharmony_ci	 * for the current shortcut.  We could in theory reuse the current
89262306a36Sopenharmony_ci	 * shortcut if its parent slot number doesn't change - but that's a
89362306a36Sopenharmony_ci	 * 1-in-16 chance so not worth expending the code upon.
89462306a36Sopenharmony_ci	 */
89562306a36Sopenharmony_ci	level = diff + ASSOC_ARRAY_LEVEL_STEP;
89662306a36Sopenharmony_ci	if (level < shortcut->skip_to_level) {
89762306a36Sopenharmony_ci		pr_devel("post-shortcut %d...%d\n", level, shortcut->skip_to_level);
89862306a36Sopenharmony_ci		keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
89962306a36Sopenharmony_ci		keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci		new_s1 = kzalloc(struct_size(new_s1, index_key, keylen),
90262306a36Sopenharmony_ci				 GFP_KERNEL);
90362306a36Sopenharmony_ci		if (!new_s1)
90462306a36Sopenharmony_ci			return false;
90562306a36Sopenharmony_ci		edit->new_meta[2] = assoc_array_shortcut_to_ptr(new_s1);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		new_s1->back_pointer = assoc_array_node_to_ptr(new_n0);
90862306a36Sopenharmony_ci		new_s1->parent_slot = sc_slot;
90962306a36Sopenharmony_ci		new_s1->next_node = shortcut->next_node;
91062306a36Sopenharmony_ci		new_s1->skip_to_level = shortcut->skip_to_level;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci		new_n0->slots[sc_slot] = assoc_array_shortcut_to_ptr(new_s1);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci		memcpy(new_s1->index_key, shortcut->index_key,
91562306a36Sopenharmony_ci		       flex_array_size(new_s1, index_key, keylen));
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci		edit->set[1].ptr = &side->back_pointer;
91862306a36Sopenharmony_ci		edit->set[1].to = assoc_array_shortcut_to_ptr(new_s1);
91962306a36Sopenharmony_ci	} else {
92062306a36Sopenharmony_ci		pr_devel("no post-shortcut\n");
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		/* We don't have to replace the pointed-to node as long as we
92362306a36Sopenharmony_ci		 * use memory barriers to make sure the parent slot number is
92462306a36Sopenharmony_ci		 * changed before the back pointer (the parent slot number is
92562306a36Sopenharmony_ci		 * irrelevant to the old parent shortcut).
92662306a36Sopenharmony_ci		 */
92762306a36Sopenharmony_ci		new_n0->slots[sc_slot] = shortcut->next_node;
92862306a36Sopenharmony_ci		edit->set_parent_slot[0].p = &side->parent_slot;
92962306a36Sopenharmony_ci		edit->set_parent_slot[0].to = sc_slot;
93062306a36Sopenharmony_ci		edit->set[1].ptr = &side->back_pointer;
93162306a36Sopenharmony_ci		edit->set[1].to = assoc_array_node_to_ptr(new_n0);
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	/* Install the new leaf in a spare slot in the new node. */
93562306a36Sopenharmony_ci	if (sc_slot == 0)
93662306a36Sopenharmony_ci		edit->leaf_p = &new_n0->slots[1];
93762306a36Sopenharmony_ci	else
93862306a36Sopenharmony_ci		edit->leaf_p = &new_n0->slots[0];
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	pr_devel("<--%s() = ok [split shortcut]\n", __func__);
94162306a36Sopenharmony_ci	return edit;
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci/**
94562306a36Sopenharmony_ci * assoc_array_insert - Script insertion of an object into an associative array
94662306a36Sopenharmony_ci * @array: The array to insert into.
94762306a36Sopenharmony_ci * @ops: The operations to use.
94862306a36Sopenharmony_ci * @index_key: The key to insert at.
94962306a36Sopenharmony_ci * @object: The object to insert.
95062306a36Sopenharmony_ci *
95162306a36Sopenharmony_ci * Precalculate and preallocate a script for the insertion or replacement of an
95262306a36Sopenharmony_ci * object in an associative array.  This results in an edit script that can
95362306a36Sopenharmony_ci * either be applied or cancelled.
95462306a36Sopenharmony_ci *
95562306a36Sopenharmony_ci * The function returns a pointer to an edit script or -ENOMEM.
95662306a36Sopenharmony_ci *
95762306a36Sopenharmony_ci * The caller should lock against other modifications and must continue to hold
95862306a36Sopenharmony_ci * the lock until assoc_array_apply_edit() has been called.
95962306a36Sopenharmony_ci *
96062306a36Sopenharmony_ci * Accesses to the tree may take place concurrently with this function,
96162306a36Sopenharmony_ci * provided they hold the RCU read lock.
96262306a36Sopenharmony_ci */
96362306a36Sopenharmony_cistruct assoc_array_edit *assoc_array_insert(struct assoc_array *array,
96462306a36Sopenharmony_ci					    const struct assoc_array_ops *ops,
96562306a36Sopenharmony_ci					    const void *index_key,
96662306a36Sopenharmony_ci					    void *object)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	struct assoc_array_walk_result result;
96962306a36Sopenharmony_ci	struct assoc_array_edit *edit;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	pr_devel("-->%s()\n", __func__);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	/* The leaf pointer we're given must not have the bottom bit set as we
97462306a36Sopenharmony_ci	 * use those for type-marking the pointer.  NULL pointers are also not
97562306a36Sopenharmony_ci	 * allowed as they indicate an empty slot but we have to allow them
97662306a36Sopenharmony_ci	 * here as they can be updated later.
97762306a36Sopenharmony_ci	 */
97862306a36Sopenharmony_ci	BUG_ON(assoc_array_ptr_is_meta(object));
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL);
98162306a36Sopenharmony_ci	if (!edit)
98262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
98362306a36Sopenharmony_ci	edit->array = array;
98462306a36Sopenharmony_ci	edit->ops = ops;
98562306a36Sopenharmony_ci	edit->leaf = assoc_array_leaf_to_ptr(object);
98662306a36Sopenharmony_ci	edit->adjust_count_by = 1;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	switch (assoc_array_walk(array, ops, index_key, &result)) {
98962306a36Sopenharmony_ci	case assoc_array_walk_tree_empty:
99062306a36Sopenharmony_ci		/* Allocate a root node if there isn't one yet */
99162306a36Sopenharmony_ci		if (!assoc_array_insert_in_empty_tree(edit))
99262306a36Sopenharmony_ci			goto enomem;
99362306a36Sopenharmony_ci		return edit;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	case assoc_array_walk_found_terminal_node:
99662306a36Sopenharmony_ci		/* We found a node that doesn't have a node/shortcut pointer in
99762306a36Sopenharmony_ci		 * the slot corresponding to the index key that we have to
99862306a36Sopenharmony_ci		 * follow.
99962306a36Sopenharmony_ci		 */
100062306a36Sopenharmony_ci		if (!assoc_array_insert_into_terminal_node(edit, ops, index_key,
100162306a36Sopenharmony_ci							   &result))
100262306a36Sopenharmony_ci			goto enomem;
100362306a36Sopenharmony_ci		return edit;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	case assoc_array_walk_found_wrong_shortcut:
100662306a36Sopenharmony_ci		/* We found a shortcut that didn't match our key in a slot we
100762306a36Sopenharmony_ci		 * needed to follow.
100862306a36Sopenharmony_ci		 */
100962306a36Sopenharmony_ci		if (!assoc_array_insert_mid_shortcut(edit, ops, &result))
101062306a36Sopenharmony_ci			goto enomem;
101162306a36Sopenharmony_ci		return edit;
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_cienomem:
101562306a36Sopenharmony_ci	/* Clean up after an out of memory error */
101662306a36Sopenharmony_ci	pr_devel("enomem\n");
101762306a36Sopenharmony_ci	assoc_array_cancel_edit(edit);
101862306a36Sopenharmony_ci	return ERR_PTR(-ENOMEM);
101962306a36Sopenharmony_ci}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci/**
102262306a36Sopenharmony_ci * assoc_array_insert_set_object - Set the new object pointer in an edit script
102362306a36Sopenharmony_ci * @edit: The edit script to modify.
102462306a36Sopenharmony_ci * @object: The object pointer to set.
102562306a36Sopenharmony_ci *
102662306a36Sopenharmony_ci * Change the object to be inserted in an edit script.  The object pointed to
102762306a36Sopenharmony_ci * by the old object is not freed.  This must be done prior to applying the
102862306a36Sopenharmony_ci * script.
102962306a36Sopenharmony_ci */
103062306a36Sopenharmony_civoid assoc_array_insert_set_object(struct assoc_array_edit *edit, void *object)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	BUG_ON(!object);
103362306a36Sopenharmony_ci	edit->leaf = assoc_array_leaf_to_ptr(object);
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_cistruct assoc_array_delete_collapse_context {
103762306a36Sopenharmony_ci	struct assoc_array_node	*node;
103862306a36Sopenharmony_ci	const void		*skip_leaf;
103962306a36Sopenharmony_ci	int			slot;
104062306a36Sopenharmony_ci};
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci/*
104362306a36Sopenharmony_ci * Subtree collapse to node iterator.
104462306a36Sopenharmony_ci */
104562306a36Sopenharmony_cistatic int assoc_array_delete_collapse_iterator(const void *leaf,
104662306a36Sopenharmony_ci						void *iterator_data)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	struct assoc_array_delete_collapse_context *collapse = iterator_data;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	if (leaf == collapse->skip_leaf)
105162306a36Sopenharmony_ci		return 0;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	BUG_ON(collapse->slot >= ASSOC_ARRAY_FAN_OUT);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	collapse->node->slots[collapse->slot++] = assoc_array_leaf_to_ptr(leaf);
105662306a36Sopenharmony_ci	return 0;
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci/**
106062306a36Sopenharmony_ci * assoc_array_delete - Script deletion of an object from an associative array
106162306a36Sopenharmony_ci * @array: The array to search.
106262306a36Sopenharmony_ci * @ops: The operations to use.
106362306a36Sopenharmony_ci * @index_key: The key to the object.
106462306a36Sopenharmony_ci *
106562306a36Sopenharmony_ci * Precalculate and preallocate a script for the deletion of an object from an
106662306a36Sopenharmony_ci * associative array.  This results in an edit script that can either be
106762306a36Sopenharmony_ci * applied or cancelled.
106862306a36Sopenharmony_ci *
106962306a36Sopenharmony_ci * The function returns a pointer to an edit script if the object was found,
107062306a36Sopenharmony_ci * NULL if the object was not found or -ENOMEM.
107162306a36Sopenharmony_ci *
107262306a36Sopenharmony_ci * The caller should lock against other modifications and must continue to hold
107362306a36Sopenharmony_ci * the lock until assoc_array_apply_edit() has been called.
107462306a36Sopenharmony_ci *
107562306a36Sopenharmony_ci * Accesses to the tree may take place concurrently with this function,
107662306a36Sopenharmony_ci * provided they hold the RCU read lock.
107762306a36Sopenharmony_ci */
107862306a36Sopenharmony_cistruct assoc_array_edit *assoc_array_delete(struct assoc_array *array,
107962306a36Sopenharmony_ci					    const struct assoc_array_ops *ops,
108062306a36Sopenharmony_ci					    const void *index_key)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	struct assoc_array_delete_collapse_context collapse;
108362306a36Sopenharmony_ci	struct assoc_array_walk_result result;
108462306a36Sopenharmony_ci	struct assoc_array_node *node, *new_n0;
108562306a36Sopenharmony_ci	struct assoc_array_edit *edit;
108662306a36Sopenharmony_ci	struct assoc_array_ptr *ptr;
108762306a36Sopenharmony_ci	bool has_meta;
108862306a36Sopenharmony_ci	int slot, i;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	pr_devel("-->%s()\n", __func__);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL);
109362306a36Sopenharmony_ci	if (!edit)
109462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
109562306a36Sopenharmony_ci	edit->array = array;
109662306a36Sopenharmony_ci	edit->ops = ops;
109762306a36Sopenharmony_ci	edit->adjust_count_by = -1;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	switch (assoc_array_walk(array, ops, index_key, &result)) {
110062306a36Sopenharmony_ci	case assoc_array_walk_found_terminal_node:
110162306a36Sopenharmony_ci		/* We found a node that should contain the leaf we've been
110262306a36Sopenharmony_ci		 * asked to remove - *if* it's in the tree.
110362306a36Sopenharmony_ci		 */
110462306a36Sopenharmony_ci		pr_devel("terminal_node\n");
110562306a36Sopenharmony_ci		node = result.terminal_node.node;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci		for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
110862306a36Sopenharmony_ci			ptr = node->slots[slot];
110962306a36Sopenharmony_ci			if (ptr &&
111062306a36Sopenharmony_ci			    assoc_array_ptr_is_leaf(ptr) &&
111162306a36Sopenharmony_ci			    ops->compare_object(assoc_array_ptr_to_leaf(ptr),
111262306a36Sopenharmony_ci						index_key))
111362306a36Sopenharmony_ci				goto found_leaf;
111462306a36Sopenharmony_ci		}
111562306a36Sopenharmony_ci		fallthrough;
111662306a36Sopenharmony_ci	case assoc_array_walk_tree_empty:
111762306a36Sopenharmony_ci	case assoc_array_walk_found_wrong_shortcut:
111862306a36Sopenharmony_ci	default:
111962306a36Sopenharmony_ci		assoc_array_cancel_edit(edit);
112062306a36Sopenharmony_ci		pr_devel("not found\n");
112162306a36Sopenharmony_ci		return NULL;
112262306a36Sopenharmony_ci	}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_cifound_leaf:
112562306a36Sopenharmony_ci	BUG_ON(array->nr_leaves_on_tree <= 0);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	/* In the simplest form of deletion we just clear the slot and release
112862306a36Sopenharmony_ci	 * the leaf after a suitable interval.
112962306a36Sopenharmony_ci	 */
113062306a36Sopenharmony_ci	edit->dead_leaf = node->slots[slot];
113162306a36Sopenharmony_ci	edit->set[0].ptr = &node->slots[slot];
113262306a36Sopenharmony_ci	edit->set[0].to = NULL;
113362306a36Sopenharmony_ci	edit->adjust_count_on = node;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	/* If that concludes erasure of the last leaf, then delete the entire
113662306a36Sopenharmony_ci	 * internal array.
113762306a36Sopenharmony_ci	 */
113862306a36Sopenharmony_ci	if (array->nr_leaves_on_tree == 1) {
113962306a36Sopenharmony_ci		edit->set[1].ptr = &array->root;
114062306a36Sopenharmony_ci		edit->set[1].to = NULL;
114162306a36Sopenharmony_ci		edit->adjust_count_on = NULL;
114262306a36Sopenharmony_ci		edit->excised_subtree = array->root;
114362306a36Sopenharmony_ci		pr_devel("all gone\n");
114462306a36Sopenharmony_ci		return edit;
114562306a36Sopenharmony_ci	}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	/* However, we'd also like to clear up some metadata blocks if we
114862306a36Sopenharmony_ci	 * possibly can.
114962306a36Sopenharmony_ci	 *
115062306a36Sopenharmony_ci	 * We go for a simple algorithm of: if this node has FAN_OUT or fewer
115162306a36Sopenharmony_ci	 * leaves in it, then attempt to collapse it - and attempt to
115262306a36Sopenharmony_ci	 * recursively collapse up the tree.
115362306a36Sopenharmony_ci	 *
115462306a36Sopenharmony_ci	 * We could also try and collapse in partially filled subtrees to take
115562306a36Sopenharmony_ci	 * up space in this node.
115662306a36Sopenharmony_ci	 */
115762306a36Sopenharmony_ci	if (node->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT + 1) {
115862306a36Sopenharmony_ci		struct assoc_array_node *parent, *grandparent;
115962306a36Sopenharmony_ci		struct assoc_array_ptr *ptr;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci		/* First of all, we need to know if this node has metadata so
116262306a36Sopenharmony_ci		 * that we don't try collapsing if all the leaves are already
116362306a36Sopenharmony_ci		 * here.
116462306a36Sopenharmony_ci		 */
116562306a36Sopenharmony_ci		has_meta = false;
116662306a36Sopenharmony_ci		for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
116762306a36Sopenharmony_ci			ptr = node->slots[i];
116862306a36Sopenharmony_ci			if (assoc_array_ptr_is_meta(ptr)) {
116962306a36Sopenharmony_ci				has_meta = true;
117062306a36Sopenharmony_ci				break;
117162306a36Sopenharmony_ci			}
117262306a36Sopenharmony_ci		}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci		pr_devel("leaves: %ld [m=%d]\n",
117562306a36Sopenharmony_ci			 node->nr_leaves_on_branch - 1, has_meta);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci		/* Look further up the tree to see if we can collapse this node
117862306a36Sopenharmony_ci		 * into a more proximal node too.
117962306a36Sopenharmony_ci		 */
118062306a36Sopenharmony_ci		parent = node;
118162306a36Sopenharmony_ci	collapse_up:
118262306a36Sopenharmony_ci		pr_devel("collapse subtree: %ld\n", parent->nr_leaves_on_branch);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci		ptr = parent->back_pointer;
118562306a36Sopenharmony_ci		if (!ptr)
118662306a36Sopenharmony_ci			goto do_collapse;
118762306a36Sopenharmony_ci		if (assoc_array_ptr_is_shortcut(ptr)) {
118862306a36Sopenharmony_ci			struct assoc_array_shortcut *s = assoc_array_ptr_to_shortcut(ptr);
118962306a36Sopenharmony_ci			ptr = s->back_pointer;
119062306a36Sopenharmony_ci			if (!ptr)
119162306a36Sopenharmony_ci				goto do_collapse;
119262306a36Sopenharmony_ci		}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci		grandparent = assoc_array_ptr_to_node(ptr);
119562306a36Sopenharmony_ci		if (grandparent->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT + 1) {
119662306a36Sopenharmony_ci			parent = grandparent;
119762306a36Sopenharmony_ci			goto collapse_up;
119862306a36Sopenharmony_ci		}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	do_collapse:
120162306a36Sopenharmony_ci		/* There's no point collapsing if the original node has no meta
120262306a36Sopenharmony_ci		 * pointers to discard and if we didn't merge into one of that
120362306a36Sopenharmony_ci		 * node's ancestry.
120462306a36Sopenharmony_ci		 */
120562306a36Sopenharmony_ci		if (has_meta || parent != node) {
120662306a36Sopenharmony_ci			node = parent;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci			/* Create a new node to collapse into */
120962306a36Sopenharmony_ci			new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
121062306a36Sopenharmony_ci			if (!new_n0)
121162306a36Sopenharmony_ci				goto enomem;
121262306a36Sopenharmony_ci			edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci			new_n0->back_pointer = node->back_pointer;
121562306a36Sopenharmony_ci			new_n0->parent_slot = node->parent_slot;
121662306a36Sopenharmony_ci			new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch;
121762306a36Sopenharmony_ci			edit->adjust_count_on = new_n0;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci			collapse.node = new_n0;
122062306a36Sopenharmony_ci			collapse.skip_leaf = assoc_array_ptr_to_leaf(edit->dead_leaf);
122162306a36Sopenharmony_ci			collapse.slot = 0;
122262306a36Sopenharmony_ci			assoc_array_subtree_iterate(assoc_array_node_to_ptr(node),
122362306a36Sopenharmony_ci						    node->back_pointer,
122462306a36Sopenharmony_ci						    assoc_array_delete_collapse_iterator,
122562306a36Sopenharmony_ci						    &collapse);
122662306a36Sopenharmony_ci			pr_devel("collapsed %d,%lu\n", collapse.slot, new_n0->nr_leaves_on_branch);
122762306a36Sopenharmony_ci			BUG_ON(collapse.slot != new_n0->nr_leaves_on_branch - 1);
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci			if (!node->back_pointer) {
123062306a36Sopenharmony_ci				edit->set[1].ptr = &array->root;
123162306a36Sopenharmony_ci			} else if (assoc_array_ptr_is_leaf(node->back_pointer)) {
123262306a36Sopenharmony_ci				BUG();
123362306a36Sopenharmony_ci			} else if (assoc_array_ptr_is_node(node->back_pointer)) {
123462306a36Sopenharmony_ci				struct assoc_array_node *p =
123562306a36Sopenharmony_ci					assoc_array_ptr_to_node(node->back_pointer);
123662306a36Sopenharmony_ci				edit->set[1].ptr = &p->slots[node->parent_slot];
123762306a36Sopenharmony_ci			} else if (assoc_array_ptr_is_shortcut(node->back_pointer)) {
123862306a36Sopenharmony_ci				struct assoc_array_shortcut *s =
123962306a36Sopenharmony_ci					assoc_array_ptr_to_shortcut(node->back_pointer);
124062306a36Sopenharmony_ci				edit->set[1].ptr = &s->next_node;
124162306a36Sopenharmony_ci			}
124262306a36Sopenharmony_ci			edit->set[1].to = assoc_array_node_to_ptr(new_n0);
124362306a36Sopenharmony_ci			edit->excised_subtree = assoc_array_node_to_ptr(node);
124462306a36Sopenharmony_ci		}
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	return edit;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_cienomem:
125062306a36Sopenharmony_ci	/* Clean up after an out of memory error */
125162306a36Sopenharmony_ci	pr_devel("enomem\n");
125262306a36Sopenharmony_ci	assoc_array_cancel_edit(edit);
125362306a36Sopenharmony_ci	return ERR_PTR(-ENOMEM);
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci/**
125762306a36Sopenharmony_ci * assoc_array_clear - Script deletion of all objects from an associative array
125862306a36Sopenharmony_ci * @array: The array to clear.
125962306a36Sopenharmony_ci * @ops: The operations to use.
126062306a36Sopenharmony_ci *
126162306a36Sopenharmony_ci * Precalculate and preallocate a script for the deletion of all the objects
126262306a36Sopenharmony_ci * from an associative array.  This results in an edit script that can either
126362306a36Sopenharmony_ci * be applied or cancelled.
126462306a36Sopenharmony_ci *
126562306a36Sopenharmony_ci * The function returns a pointer to an edit script if there are objects to be
126662306a36Sopenharmony_ci * deleted, NULL if there are no objects in the array or -ENOMEM.
126762306a36Sopenharmony_ci *
126862306a36Sopenharmony_ci * The caller should lock against other modifications and must continue to hold
126962306a36Sopenharmony_ci * the lock until assoc_array_apply_edit() has been called.
127062306a36Sopenharmony_ci *
127162306a36Sopenharmony_ci * Accesses to the tree may take place concurrently with this function,
127262306a36Sopenharmony_ci * provided they hold the RCU read lock.
127362306a36Sopenharmony_ci */
127462306a36Sopenharmony_cistruct assoc_array_edit *assoc_array_clear(struct assoc_array *array,
127562306a36Sopenharmony_ci					   const struct assoc_array_ops *ops)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	struct assoc_array_edit *edit;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	pr_devel("-->%s()\n", __func__);
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	if (!array->root)
128262306a36Sopenharmony_ci		return NULL;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL);
128562306a36Sopenharmony_ci	if (!edit)
128662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
128762306a36Sopenharmony_ci	edit->array = array;
128862306a36Sopenharmony_ci	edit->ops = ops;
128962306a36Sopenharmony_ci	edit->set[1].ptr = &array->root;
129062306a36Sopenharmony_ci	edit->set[1].to = NULL;
129162306a36Sopenharmony_ci	edit->excised_subtree = array->root;
129262306a36Sopenharmony_ci	edit->ops_for_excised_subtree = ops;
129362306a36Sopenharmony_ci	pr_devel("all gone\n");
129462306a36Sopenharmony_ci	return edit;
129562306a36Sopenharmony_ci}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci/*
129862306a36Sopenharmony_ci * Handle the deferred destruction after an applied edit.
129962306a36Sopenharmony_ci */
130062306a36Sopenharmony_cistatic void assoc_array_rcu_cleanup(struct rcu_head *head)
130162306a36Sopenharmony_ci{
130262306a36Sopenharmony_ci	struct assoc_array_edit *edit =
130362306a36Sopenharmony_ci		container_of(head, struct assoc_array_edit, rcu);
130462306a36Sopenharmony_ci	int i;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	pr_devel("-->%s()\n", __func__);
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	if (edit->dead_leaf)
130962306a36Sopenharmony_ci		edit->ops->free_object(assoc_array_ptr_to_leaf(edit->dead_leaf));
131062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(edit->excised_meta); i++)
131162306a36Sopenharmony_ci		if (edit->excised_meta[i])
131262306a36Sopenharmony_ci			kfree(assoc_array_ptr_to_node(edit->excised_meta[i]));
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	if (edit->excised_subtree) {
131562306a36Sopenharmony_ci		BUG_ON(assoc_array_ptr_is_leaf(edit->excised_subtree));
131662306a36Sopenharmony_ci		if (assoc_array_ptr_is_node(edit->excised_subtree)) {
131762306a36Sopenharmony_ci			struct assoc_array_node *n =
131862306a36Sopenharmony_ci				assoc_array_ptr_to_node(edit->excised_subtree);
131962306a36Sopenharmony_ci			n->back_pointer = NULL;
132062306a36Sopenharmony_ci		} else {
132162306a36Sopenharmony_ci			struct assoc_array_shortcut *s =
132262306a36Sopenharmony_ci				assoc_array_ptr_to_shortcut(edit->excised_subtree);
132362306a36Sopenharmony_ci			s->back_pointer = NULL;
132462306a36Sopenharmony_ci		}
132562306a36Sopenharmony_ci		assoc_array_destroy_subtree(edit->excised_subtree,
132662306a36Sopenharmony_ci					    edit->ops_for_excised_subtree);
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	kfree(edit);
133062306a36Sopenharmony_ci}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci/**
133362306a36Sopenharmony_ci * assoc_array_apply_edit - Apply an edit script to an associative array
133462306a36Sopenharmony_ci * @edit: The script to apply.
133562306a36Sopenharmony_ci *
133662306a36Sopenharmony_ci * Apply an edit script to an associative array to effect an insertion,
133762306a36Sopenharmony_ci * deletion or clearance.  As the edit script includes preallocated memory,
133862306a36Sopenharmony_ci * this is guaranteed not to fail.
133962306a36Sopenharmony_ci *
134062306a36Sopenharmony_ci * The edit script, dead objects and dead metadata will be scheduled for
134162306a36Sopenharmony_ci * destruction after an RCU grace period to permit those doing read-only
134262306a36Sopenharmony_ci * accesses on the array to continue to do so under the RCU read lock whilst
134362306a36Sopenharmony_ci * the edit is taking place.
134462306a36Sopenharmony_ci */
134562306a36Sopenharmony_civoid assoc_array_apply_edit(struct assoc_array_edit *edit)
134662306a36Sopenharmony_ci{
134762306a36Sopenharmony_ci	struct assoc_array_shortcut *shortcut;
134862306a36Sopenharmony_ci	struct assoc_array_node *node;
134962306a36Sopenharmony_ci	struct assoc_array_ptr *ptr;
135062306a36Sopenharmony_ci	int i;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	pr_devel("-->%s()\n", __func__);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	smp_wmb();
135562306a36Sopenharmony_ci	if (edit->leaf_p)
135662306a36Sopenharmony_ci		*edit->leaf_p = edit->leaf;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	smp_wmb();
135962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(edit->set_parent_slot); i++)
136062306a36Sopenharmony_ci		if (edit->set_parent_slot[i].p)
136162306a36Sopenharmony_ci			*edit->set_parent_slot[i].p = edit->set_parent_slot[i].to;
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	smp_wmb();
136462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(edit->set_backpointers); i++)
136562306a36Sopenharmony_ci		if (edit->set_backpointers[i])
136662306a36Sopenharmony_ci			*edit->set_backpointers[i] = edit->set_backpointers_to;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	smp_wmb();
136962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(edit->set); i++)
137062306a36Sopenharmony_ci		if (edit->set[i].ptr)
137162306a36Sopenharmony_ci			*edit->set[i].ptr = edit->set[i].to;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (edit->array->root == NULL) {
137462306a36Sopenharmony_ci		edit->array->nr_leaves_on_tree = 0;
137562306a36Sopenharmony_ci	} else if (edit->adjust_count_on) {
137662306a36Sopenharmony_ci		node = edit->adjust_count_on;
137762306a36Sopenharmony_ci		for (;;) {
137862306a36Sopenharmony_ci			node->nr_leaves_on_branch += edit->adjust_count_by;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci			ptr = node->back_pointer;
138162306a36Sopenharmony_ci			if (!ptr)
138262306a36Sopenharmony_ci				break;
138362306a36Sopenharmony_ci			if (assoc_array_ptr_is_shortcut(ptr)) {
138462306a36Sopenharmony_ci				shortcut = assoc_array_ptr_to_shortcut(ptr);
138562306a36Sopenharmony_ci				ptr = shortcut->back_pointer;
138662306a36Sopenharmony_ci				if (!ptr)
138762306a36Sopenharmony_ci					break;
138862306a36Sopenharmony_ci			}
138962306a36Sopenharmony_ci			BUG_ON(!assoc_array_ptr_is_node(ptr));
139062306a36Sopenharmony_ci			node = assoc_array_ptr_to_node(ptr);
139162306a36Sopenharmony_ci		}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci		edit->array->nr_leaves_on_tree += edit->adjust_count_by;
139462306a36Sopenharmony_ci	}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	call_rcu(&edit->rcu, assoc_array_rcu_cleanup);
139762306a36Sopenharmony_ci}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci/**
140062306a36Sopenharmony_ci * assoc_array_cancel_edit - Discard an edit script.
140162306a36Sopenharmony_ci * @edit: The script to discard.
140262306a36Sopenharmony_ci *
140362306a36Sopenharmony_ci * Free an edit script and all the preallocated data it holds without making
140462306a36Sopenharmony_ci * any changes to the associative array it was intended for.
140562306a36Sopenharmony_ci *
140662306a36Sopenharmony_ci * NOTE!  In the case of an insertion script, this does _not_ release the leaf
140762306a36Sopenharmony_ci * that was to be inserted.  That is left to the caller.
140862306a36Sopenharmony_ci */
140962306a36Sopenharmony_civoid assoc_array_cancel_edit(struct assoc_array_edit *edit)
141062306a36Sopenharmony_ci{
141162306a36Sopenharmony_ci	struct assoc_array_ptr *ptr;
141262306a36Sopenharmony_ci	int i;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	pr_devel("-->%s()\n", __func__);
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	/* Clean up after an out of memory error */
141762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(edit->new_meta); i++) {
141862306a36Sopenharmony_ci		ptr = edit->new_meta[i];
141962306a36Sopenharmony_ci		if (ptr) {
142062306a36Sopenharmony_ci			if (assoc_array_ptr_is_node(ptr))
142162306a36Sopenharmony_ci				kfree(assoc_array_ptr_to_node(ptr));
142262306a36Sopenharmony_ci			else
142362306a36Sopenharmony_ci				kfree(assoc_array_ptr_to_shortcut(ptr));
142462306a36Sopenharmony_ci		}
142562306a36Sopenharmony_ci	}
142662306a36Sopenharmony_ci	kfree(edit);
142762306a36Sopenharmony_ci}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci/**
143062306a36Sopenharmony_ci * assoc_array_gc - Garbage collect an associative array.
143162306a36Sopenharmony_ci * @array: The array to clean.
143262306a36Sopenharmony_ci * @ops: The operations to use.
143362306a36Sopenharmony_ci * @iterator: A callback function to pass judgement on each object.
143462306a36Sopenharmony_ci * @iterator_data: Private data for the callback function.
143562306a36Sopenharmony_ci *
143662306a36Sopenharmony_ci * Collect garbage from an associative array and pack down the internal tree to
143762306a36Sopenharmony_ci * save memory.
143862306a36Sopenharmony_ci *
143962306a36Sopenharmony_ci * The iterator function is asked to pass judgement upon each object in the
144062306a36Sopenharmony_ci * array.  If it returns false, the object is discard and if it returns true,
144162306a36Sopenharmony_ci * the object is kept.  If it returns true, it must increment the object's
144262306a36Sopenharmony_ci * usage count (or whatever it needs to do to retain it) before returning.
144362306a36Sopenharmony_ci *
144462306a36Sopenharmony_ci * This function returns 0 if successful or -ENOMEM if out of memory.  In the
144562306a36Sopenharmony_ci * latter case, the array is not changed.
144662306a36Sopenharmony_ci *
144762306a36Sopenharmony_ci * The caller should lock against other modifications and must continue to hold
144862306a36Sopenharmony_ci * the lock until assoc_array_apply_edit() has been called.
144962306a36Sopenharmony_ci *
145062306a36Sopenharmony_ci * Accesses to the tree may take place concurrently with this function,
145162306a36Sopenharmony_ci * provided they hold the RCU read lock.
145262306a36Sopenharmony_ci */
145362306a36Sopenharmony_ciint assoc_array_gc(struct assoc_array *array,
145462306a36Sopenharmony_ci		   const struct assoc_array_ops *ops,
145562306a36Sopenharmony_ci		   bool (*iterator)(void *object, void *iterator_data),
145662306a36Sopenharmony_ci		   void *iterator_data)
145762306a36Sopenharmony_ci{
145862306a36Sopenharmony_ci	struct assoc_array_shortcut *shortcut, *new_s;
145962306a36Sopenharmony_ci	struct assoc_array_node *node, *new_n;
146062306a36Sopenharmony_ci	struct assoc_array_edit *edit;
146162306a36Sopenharmony_ci	struct assoc_array_ptr *cursor, *ptr;
146262306a36Sopenharmony_ci	struct assoc_array_ptr *new_root, *new_parent, **new_ptr_pp;
146362306a36Sopenharmony_ci	unsigned long nr_leaves_on_tree;
146462306a36Sopenharmony_ci	bool retained;
146562306a36Sopenharmony_ci	int keylen, slot, nr_free, next_slot, i;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	pr_devel("-->%s()\n", __func__);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	if (!array->root)
147062306a36Sopenharmony_ci		return 0;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL);
147362306a36Sopenharmony_ci	if (!edit)
147462306a36Sopenharmony_ci		return -ENOMEM;
147562306a36Sopenharmony_ci	edit->array = array;
147662306a36Sopenharmony_ci	edit->ops = ops;
147762306a36Sopenharmony_ci	edit->ops_for_excised_subtree = ops;
147862306a36Sopenharmony_ci	edit->set[0].ptr = &array->root;
147962306a36Sopenharmony_ci	edit->excised_subtree = array->root;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	new_root = new_parent = NULL;
148262306a36Sopenharmony_ci	new_ptr_pp = &new_root;
148362306a36Sopenharmony_ci	cursor = array->root;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cidescend:
148662306a36Sopenharmony_ci	/* If this point is a shortcut, then we need to duplicate it and
148762306a36Sopenharmony_ci	 * advance the target cursor.
148862306a36Sopenharmony_ci	 */
148962306a36Sopenharmony_ci	if (assoc_array_ptr_is_shortcut(cursor)) {
149062306a36Sopenharmony_ci		shortcut = assoc_array_ptr_to_shortcut(cursor);
149162306a36Sopenharmony_ci		keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
149262306a36Sopenharmony_ci		keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
149362306a36Sopenharmony_ci		new_s = kmalloc(struct_size(new_s, index_key, keylen),
149462306a36Sopenharmony_ci				GFP_KERNEL);
149562306a36Sopenharmony_ci		if (!new_s)
149662306a36Sopenharmony_ci			goto enomem;
149762306a36Sopenharmony_ci		pr_devel("dup shortcut %p -> %p\n", shortcut, new_s);
149862306a36Sopenharmony_ci		memcpy(new_s, shortcut, struct_size(new_s, index_key, keylen));
149962306a36Sopenharmony_ci		new_s->back_pointer = new_parent;
150062306a36Sopenharmony_ci		new_s->parent_slot = shortcut->parent_slot;
150162306a36Sopenharmony_ci		*new_ptr_pp = new_parent = assoc_array_shortcut_to_ptr(new_s);
150262306a36Sopenharmony_ci		new_ptr_pp = &new_s->next_node;
150362306a36Sopenharmony_ci		cursor = shortcut->next_node;
150462306a36Sopenharmony_ci	}
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	/* Duplicate the node at this position */
150762306a36Sopenharmony_ci	node = assoc_array_ptr_to_node(cursor);
150862306a36Sopenharmony_ci	new_n = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
150962306a36Sopenharmony_ci	if (!new_n)
151062306a36Sopenharmony_ci		goto enomem;
151162306a36Sopenharmony_ci	pr_devel("dup node %p -> %p\n", node, new_n);
151262306a36Sopenharmony_ci	new_n->back_pointer = new_parent;
151362306a36Sopenharmony_ci	new_n->parent_slot = node->parent_slot;
151462306a36Sopenharmony_ci	*new_ptr_pp = new_parent = assoc_array_node_to_ptr(new_n);
151562306a36Sopenharmony_ci	new_ptr_pp = NULL;
151662306a36Sopenharmony_ci	slot = 0;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_cicontinue_node:
151962306a36Sopenharmony_ci	/* Filter across any leaves and gc any subtrees */
152062306a36Sopenharmony_ci	for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
152162306a36Sopenharmony_ci		ptr = node->slots[slot];
152262306a36Sopenharmony_ci		if (!ptr)
152362306a36Sopenharmony_ci			continue;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci		if (assoc_array_ptr_is_leaf(ptr)) {
152662306a36Sopenharmony_ci			if (iterator(assoc_array_ptr_to_leaf(ptr),
152762306a36Sopenharmony_ci				     iterator_data))
152862306a36Sopenharmony_ci				/* The iterator will have done any reference
152962306a36Sopenharmony_ci				 * counting on the object for us.
153062306a36Sopenharmony_ci				 */
153162306a36Sopenharmony_ci				new_n->slots[slot] = ptr;
153262306a36Sopenharmony_ci			continue;
153362306a36Sopenharmony_ci		}
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci		new_ptr_pp = &new_n->slots[slot];
153662306a36Sopenharmony_ci		cursor = ptr;
153762306a36Sopenharmony_ci		goto descend;
153862306a36Sopenharmony_ci	}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ciretry_compress:
154162306a36Sopenharmony_ci	pr_devel("-- compress node %p --\n", new_n);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	/* Count up the number of empty slots in this node and work out the
154462306a36Sopenharmony_ci	 * subtree leaf count.
154562306a36Sopenharmony_ci	 */
154662306a36Sopenharmony_ci	new_n->nr_leaves_on_branch = 0;
154762306a36Sopenharmony_ci	nr_free = 0;
154862306a36Sopenharmony_ci	for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
154962306a36Sopenharmony_ci		ptr = new_n->slots[slot];
155062306a36Sopenharmony_ci		if (!ptr)
155162306a36Sopenharmony_ci			nr_free++;
155262306a36Sopenharmony_ci		else if (assoc_array_ptr_is_leaf(ptr))
155362306a36Sopenharmony_ci			new_n->nr_leaves_on_branch++;
155462306a36Sopenharmony_ci	}
155562306a36Sopenharmony_ci	pr_devel("free=%d, leaves=%lu\n", nr_free, new_n->nr_leaves_on_branch);
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	/* See what we can fold in */
155862306a36Sopenharmony_ci	retained = false;
155962306a36Sopenharmony_ci	next_slot = 0;
156062306a36Sopenharmony_ci	for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
156162306a36Sopenharmony_ci		struct assoc_array_shortcut *s;
156262306a36Sopenharmony_ci		struct assoc_array_node *child;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci		ptr = new_n->slots[slot];
156562306a36Sopenharmony_ci		if (!ptr || assoc_array_ptr_is_leaf(ptr))
156662306a36Sopenharmony_ci			continue;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci		s = NULL;
156962306a36Sopenharmony_ci		if (assoc_array_ptr_is_shortcut(ptr)) {
157062306a36Sopenharmony_ci			s = assoc_array_ptr_to_shortcut(ptr);
157162306a36Sopenharmony_ci			ptr = s->next_node;
157262306a36Sopenharmony_ci		}
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci		child = assoc_array_ptr_to_node(ptr);
157562306a36Sopenharmony_ci		new_n->nr_leaves_on_branch += child->nr_leaves_on_branch;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci		if (child->nr_leaves_on_branch <= nr_free + 1) {
157862306a36Sopenharmony_ci			/* Fold the child node into this one */
157962306a36Sopenharmony_ci			pr_devel("[%d] fold node %lu/%d [nx %d]\n",
158062306a36Sopenharmony_ci				 slot, child->nr_leaves_on_branch, nr_free + 1,
158162306a36Sopenharmony_ci				 next_slot);
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci			/* We would already have reaped an intervening shortcut
158462306a36Sopenharmony_ci			 * on the way back up the tree.
158562306a36Sopenharmony_ci			 */
158662306a36Sopenharmony_ci			BUG_ON(s);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci			new_n->slots[slot] = NULL;
158962306a36Sopenharmony_ci			nr_free++;
159062306a36Sopenharmony_ci			if (slot < next_slot)
159162306a36Sopenharmony_ci				next_slot = slot;
159262306a36Sopenharmony_ci			for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
159362306a36Sopenharmony_ci				struct assoc_array_ptr *p = child->slots[i];
159462306a36Sopenharmony_ci				if (!p)
159562306a36Sopenharmony_ci					continue;
159662306a36Sopenharmony_ci				BUG_ON(assoc_array_ptr_is_meta(p));
159762306a36Sopenharmony_ci				while (new_n->slots[next_slot])
159862306a36Sopenharmony_ci					next_slot++;
159962306a36Sopenharmony_ci				BUG_ON(next_slot >= ASSOC_ARRAY_FAN_OUT);
160062306a36Sopenharmony_ci				new_n->slots[next_slot++] = p;
160162306a36Sopenharmony_ci				nr_free--;
160262306a36Sopenharmony_ci			}
160362306a36Sopenharmony_ci			kfree(child);
160462306a36Sopenharmony_ci		} else {
160562306a36Sopenharmony_ci			pr_devel("[%d] retain node %lu/%d [nx %d]\n",
160662306a36Sopenharmony_ci				 slot, child->nr_leaves_on_branch, nr_free + 1,
160762306a36Sopenharmony_ci				 next_slot);
160862306a36Sopenharmony_ci			retained = true;
160962306a36Sopenharmony_ci		}
161062306a36Sopenharmony_ci	}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	if (retained && new_n->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT) {
161362306a36Sopenharmony_ci		pr_devel("internal nodes remain despite enough space, retrying\n");
161462306a36Sopenharmony_ci		goto retry_compress;
161562306a36Sopenharmony_ci	}
161662306a36Sopenharmony_ci	pr_devel("after: %lu\n", new_n->nr_leaves_on_branch);
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	nr_leaves_on_tree = new_n->nr_leaves_on_branch;
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	/* Excise this node if it is singly occupied by a shortcut */
162162306a36Sopenharmony_ci	if (nr_free == ASSOC_ARRAY_FAN_OUT - 1) {
162262306a36Sopenharmony_ci		for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++)
162362306a36Sopenharmony_ci			if ((ptr = new_n->slots[slot]))
162462306a36Sopenharmony_ci				break;
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci		if (assoc_array_ptr_is_meta(ptr) &&
162762306a36Sopenharmony_ci		    assoc_array_ptr_is_shortcut(ptr)) {
162862306a36Sopenharmony_ci			pr_devel("excise node %p with 1 shortcut\n", new_n);
162962306a36Sopenharmony_ci			new_s = assoc_array_ptr_to_shortcut(ptr);
163062306a36Sopenharmony_ci			new_parent = new_n->back_pointer;
163162306a36Sopenharmony_ci			slot = new_n->parent_slot;
163262306a36Sopenharmony_ci			kfree(new_n);
163362306a36Sopenharmony_ci			if (!new_parent) {
163462306a36Sopenharmony_ci				new_s->back_pointer = NULL;
163562306a36Sopenharmony_ci				new_s->parent_slot = 0;
163662306a36Sopenharmony_ci				new_root = ptr;
163762306a36Sopenharmony_ci				goto gc_complete;
163862306a36Sopenharmony_ci			}
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci			if (assoc_array_ptr_is_shortcut(new_parent)) {
164162306a36Sopenharmony_ci				/* We can discard any preceding shortcut also */
164262306a36Sopenharmony_ci				struct assoc_array_shortcut *s =
164362306a36Sopenharmony_ci					assoc_array_ptr_to_shortcut(new_parent);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci				pr_devel("excise preceding shortcut\n");
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci				new_parent = new_s->back_pointer = s->back_pointer;
164862306a36Sopenharmony_ci				slot = new_s->parent_slot = s->parent_slot;
164962306a36Sopenharmony_ci				kfree(s);
165062306a36Sopenharmony_ci				if (!new_parent) {
165162306a36Sopenharmony_ci					new_s->back_pointer = NULL;
165262306a36Sopenharmony_ci					new_s->parent_slot = 0;
165362306a36Sopenharmony_ci					new_root = ptr;
165462306a36Sopenharmony_ci					goto gc_complete;
165562306a36Sopenharmony_ci				}
165662306a36Sopenharmony_ci			}
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci			new_s->back_pointer = new_parent;
165962306a36Sopenharmony_ci			new_s->parent_slot = slot;
166062306a36Sopenharmony_ci			new_n = assoc_array_ptr_to_node(new_parent);
166162306a36Sopenharmony_ci			new_n->slots[slot] = ptr;
166262306a36Sopenharmony_ci			goto ascend_old_tree;
166362306a36Sopenharmony_ci		}
166462306a36Sopenharmony_ci	}
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	/* Excise any shortcuts we might encounter that point to nodes that
166762306a36Sopenharmony_ci	 * only contain leaves.
166862306a36Sopenharmony_ci	 */
166962306a36Sopenharmony_ci	ptr = new_n->back_pointer;
167062306a36Sopenharmony_ci	if (!ptr)
167162306a36Sopenharmony_ci		goto gc_complete;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	if (assoc_array_ptr_is_shortcut(ptr)) {
167462306a36Sopenharmony_ci		new_s = assoc_array_ptr_to_shortcut(ptr);
167562306a36Sopenharmony_ci		new_parent = new_s->back_pointer;
167662306a36Sopenharmony_ci		slot = new_s->parent_slot;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci		if (new_n->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT) {
167962306a36Sopenharmony_ci			struct assoc_array_node *n;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci			pr_devel("excise shortcut\n");
168262306a36Sopenharmony_ci			new_n->back_pointer = new_parent;
168362306a36Sopenharmony_ci			new_n->parent_slot = slot;
168462306a36Sopenharmony_ci			kfree(new_s);
168562306a36Sopenharmony_ci			if (!new_parent) {
168662306a36Sopenharmony_ci				new_root = assoc_array_node_to_ptr(new_n);
168762306a36Sopenharmony_ci				goto gc_complete;
168862306a36Sopenharmony_ci			}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci			n = assoc_array_ptr_to_node(new_parent);
169162306a36Sopenharmony_ci			n->slots[slot] = assoc_array_node_to_ptr(new_n);
169262306a36Sopenharmony_ci		}
169362306a36Sopenharmony_ci	} else {
169462306a36Sopenharmony_ci		new_parent = ptr;
169562306a36Sopenharmony_ci	}
169662306a36Sopenharmony_ci	new_n = assoc_array_ptr_to_node(new_parent);
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ciascend_old_tree:
169962306a36Sopenharmony_ci	ptr = node->back_pointer;
170062306a36Sopenharmony_ci	if (assoc_array_ptr_is_shortcut(ptr)) {
170162306a36Sopenharmony_ci		shortcut = assoc_array_ptr_to_shortcut(ptr);
170262306a36Sopenharmony_ci		slot = shortcut->parent_slot;
170362306a36Sopenharmony_ci		cursor = shortcut->back_pointer;
170462306a36Sopenharmony_ci		if (!cursor)
170562306a36Sopenharmony_ci			goto gc_complete;
170662306a36Sopenharmony_ci	} else {
170762306a36Sopenharmony_ci		slot = node->parent_slot;
170862306a36Sopenharmony_ci		cursor = ptr;
170962306a36Sopenharmony_ci	}
171062306a36Sopenharmony_ci	BUG_ON(!cursor);
171162306a36Sopenharmony_ci	node = assoc_array_ptr_to_node(cursor);
171262306a36Sopenharmony_ci	slot++;
171362306a36Sopenharmony_ci	goto continue_node;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_cigc_complete:
171662306a36Sopenharmony_ci	edit->set[0].to = new_root;
171762306a36Sopenharmony_ci	assoc_array_apply_edit(edit);
171862306a36Sopenharmony_ci	array->nr_leaves_on_tree = nr_leaves_on_tree;
171962306a36Sopenharmony_ci	return 0;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_cienomem:
172262306a36Sopenharmony_ci	pr_devel("enomem\n");
172362306a36Sopenharmony_ci	assoc_array_destroy_subtree(new_root, edit->ops);
172462306a36Sopenharmony_ci	kfree(edit);
172562306a36Sopenharmony_ci	return -ENOMEM;
172662306a36Sopenharmony_ci}
1727