b4d0d230ccfb5 (Thomas Gleixner 2019-05-20 19:08:01 +0200 1) // SPDX-License-Identifier: GPL-2.0-or-later
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 2) /* Generic associative array implementation.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 3) *
48c40c26fc010 (Alexander Kuleshov 2017-08-23 00:39:13 +0600 4) * See Documentation/core-api/assoc_array.rst for information.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 5) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 6) * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 7) * Written by David Howells (dhowells@redhat.com)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 8) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 9) //#define DEBUG
990428b8ead31 (Pranith Kumar 2014-12-30 00:46:21 -0500 10) #include <linux/rcupdate.h>
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 11) #include <linux/slab.h>
b2a4df200d570 (David Howells 2013-09-24 10:35:18 +0100 12) #include <linux/err.h>
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 13) #include <linux/assoc_array_priv.h>
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 14)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 15) /*
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 16) * Iterate over an associative array. The caller must hold the RCU read lock
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 17) * or better.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 18) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 19) static int assoc_array_subtree_iterate(const struct assoc_array_ptr *root,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 20) const struct assoc_array_ptr *stop,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 21) int (*iterator)(const void *leaf,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 22) void *iterator_data),
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 23) void *iterator_data)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 24) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 25) const struct assoc_array_shortcut *shortcut;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 26) const struct assoc_array_node *node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 27) const struct assoc_array_ptr *cursor, *ptr, *parent;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 28) unsigned long has_meta;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 29) int slot, ret;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 30)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 31) cursor = root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 32)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 33) begin_node:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 34) if (assoc_array_ptr_is_shortcut(cursor)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 35) /* Descend through a shortcut */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 36) shortcut = assoc_array_ptr_to_shortcut(cursor);
516df050615e4 (Paul E. McKenney 2017-10-09 11:39:57 -0700 37) cursor = READ_ONCE(shortcut->next_node); /* Address dependency. */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 38) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 39)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 40) node = assoc_array_ptr_to_node(cursor);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 41) slot = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 42)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 43) /* We perform two passes of each node.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 44) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 45) * The first pass does all the leaves in this node. This means we
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 46) * don't miss any leaves if the node is split up by insertion whilst
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 47) * we're iterating over the branches rooted here (we may, however, see
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 48) * some leaves twice).
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 49) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 50) has_meta = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 51) for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
516df050615e4 (Paul E. McKenney 2017-10-09 11:39:57 -0700 52) ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 53) has_meta |= (unsigned long)ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 54) if (ptr && assoc_array_ptr_is_leaf(ptr)) {
516df050615e4 (Paul E. McKenney 2017-10-09 11:39:57 -0700 55) /* We need a barrier between the read of the pointer,
516df050615e4 (Paul E. McKenney 2017-10-09 11:39:57 -0700 56) * which is supplied by the above READ_ONCE().
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 57) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 58) /* Invoke the callback */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 59) ret = iterator(assoc_array_ptr_to_leaf(ptr),
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 60) iterator_data);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 61) if (ret)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 62) return ret;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 63) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 64) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 65)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 66) /* The second pass attends to all the metadata pointers. If we follow
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 67) * one of these we may find that we don't come back here, but rather go
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 68) * back to a replacement node with the leaves in a different layout.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 69) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 70) * We are guaranteed to make progress, however, as the slot number for
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 71) * a particular portion of the key space cannot change - and we
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 72) * continue at the back pointer + 1.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 73) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 74) if (!(has_meta & ASSOC_ARRAY_PTR_META_TYPE))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 75) goto finished_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 76) slot = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 77)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 78) continue_node:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 79) node = assoc_array_ptr_to_node(cursor);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 80) for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
516df050615e4 (Paul E. McKenney 2017-10-09 11:39:57 -0700 81) ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 82) if (assoc_array_ptr_is_meta(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 83) cursor = ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 84) goto begin_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 85) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 86) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 87)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 88) finished_node:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 89) /* Move up to the parent (may need to skip back over a shortcut) */
516df050615e4 (Paul E. McKenney 2017-10-09 11:39:57 -0700 90) parent = READ_ONCE(node->back_pointer); /* Address dependency. */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 91) slot = node->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 92) if (parent == stop)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 93) return 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 94)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 95) if (assoc_array_ptr_is_shortcut(parent)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 96) shortcut = assoc_array_ptr_to_shortcut(parent);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 97) cursor = parent;
516df050615e4 (Paul E. McKenney 2017-10-09 11:39:57 -0700 98) parent = READ_ONCE(shortcut->back_pointer); /* Address dependency. */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 99) slot = shortcut->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 100) if (parent == stop)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 101) return 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 102) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 103)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 104) /* Ascend to next slot in parent node */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 105) cursor = parent;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 106) slot++;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 107) goto continue_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 108) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 109)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 110) /**
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 111) * assoc_array_iterate - Pass all objects in the array to a callback
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 112) * @array: The array to iterate over.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 113) * @iterator: The callback function.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 114) * @iterator_data: Private data for the callback function.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 115) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 116) * Iterate over all the objects in an associative array. Each one will be
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 117) * presented to the iterator function.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 118) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 119) * If the array is being modified concurrently with the iteration then it is
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 120) * possible that some objects in the array will be passed to the iterator
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 121) * callback more than once - though every object should be passed at least
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 122) * once. If this is undesirable then the caller must lock against modification
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 123) * for the duration of this function.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 124) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 125) * The function will return 0 if no objects were in the array or else it will
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 126) * return the result of the last iterator function called. Iteration stops
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 127) * immediately if any call to the iteration function results in a non-zero
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 128) * return.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 129) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 130) * The caller should hold the RCU read lock or better if concurrent
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 131) * modification is possible.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 132) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 133) int assoc_array_iterate(const struct assoc_array *array,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 134) int (*iterator)(const void *object,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 135) void *iterator_data),
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 136) void *iterator_data)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 137) {
516df050615e4 (Paul E. McKenney 2017-10-09 11:39:57 -0700 138) struct assoc_array_ptr *root = READ_ONCE(array->root); /* Address dependency. */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 139)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 140) if (!root)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 141) return 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 142) return assoc_array_subtree_iterate(root, NULL, iterator, iterator_data);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 143) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 144)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 145) enum assoc_array_walk_status {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 146) assoc_array_walk_tree_empty,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 147) assoc_array_walk_found_terminal_node,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 148) assoc_array_walk_found_wrong_shortcut,
30b02c4b2a1eb (Stephen Hemminger 2014-01-23 13:24:09 +0000 149) };
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 150)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 151) struct assoc_array_walk_result {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 152) struct {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 153) struct assoc_array_node *node; /* Node in which leaf might be found */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 154) int level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 155) int slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 156) } terminal_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 157) struct {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 158) struct assoc_array_shortcut *shortcut;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 159) int level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 160) int sc_level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 161) unsigned long sc_segments;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 162) unsigned long dissimilarity;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 163) } wrong_shortcut;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 164) };
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 165)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 166) /*
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 167) * Navigate through the internal tree looking for the closest node to the key.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 168) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 169) static enum assoc_array_walk_status
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 170) assoc_array_walk(const struct assoc_array *array,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 171) const struct assoc_array_ops *ops,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 172) const void *index_key,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 173) struct assoc_array_walk_result *result)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 174) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 175) struct assoc_array_shortcut *shortcut;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 176) struct assoc_array_node *node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 177) struct assoc_array_ptr *cursor, *ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 178) unsigned long sc_segments, dissimilarity;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 179) unsigned long segments;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 180) int level, sc_level, next_sc_level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 181) int slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 182)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 183) pr_devel("-->%s()\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 184)
516df050615e4 (Paul E. McKenney 2017-10-09 11:39:57 -0700 185) cursor = READ_ONCE(array->root); /* Address dependency. */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 186) if (!cursor)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 187) return assoc_array_walk_tree_empty;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 188)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 189) level = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 190)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 191) /* Use segments from the key for the new leaf to navigate through the
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 192) * internal tree, skipping through nodes and shortcuts that are on
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 193) * route to the destination. Eventually we'll come to a slot that is
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 194) * either empty or contains a leaf at which point we've found a node in
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 195) * which the leaf we're looking for might be found or into which it
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 196) * should be inserted.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 197) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 198) jumped:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 199) segments = ops->get_key_chunk(index_key, level);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 200) pr_devel("segments[%d]: %lx\n", level, segments);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 201)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 202) if (assoc_array_ptr_is_shortcut(cursor))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 203) goto follow_shortcut;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 204)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 205) consider_node:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 206) node = assoc_array_ptr_to_node(cursor);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 207) slot = segments >> (level & ASSOC_ARRAY_KEY_CHUNK_MASK);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 208) slot &= ASSOC_ARRAY_FAN_MASK;
516df050615e4 (Paul E. McKenney 2017-10-09 11:39:57 -0700 209) ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 210)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 211) pr_devel("consider slot %x [ix=%d type=%lu]\n",
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 212) slot, level, (unsigned long)ptr & 3);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 213)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 214) if (!assoc_array_ptr_is_meta(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 215) /* The node doesn't have a node/shortcut pointer in the slot
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 216) * corresponding to the index key that we have to follow.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 217) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 218) result->terminal_node.node = node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 219) result->terminal_node.level = level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 220) result->terminal_node.slot = slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 221) pr_devel("<--%s() = terminal_node\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 222) return assoc_array_walk_found_terminal_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 223) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 224)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 225) if (assoc_array_ptr_is_node(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 226) /* There is a pointer to a node in the slot corresponding to
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 227) * this index key segment, so we need to follow it.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 228) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 229) cursor = ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 230) level += ASSOC_ARRAY_LEVEL_STEP;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 231) if ((level & ASSOC_ARRAY_KEY_CHUNK_MASK) != 0)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 232) goto consider_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 233) goto jumped;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 234) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 235)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 236) /* There is a shortcut in the slot corresponding to the index key
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 237) * segment. We follow the shortcut if its partial index key matches
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 238) * this leaf's. Otherwise we need to split the shortcut.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 239) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 240) cursor = ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 241) follow_shortcut:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 242) shortcut = assoc_array_ptr_to_shortcut(cursor);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 243) pr_devel("shortcut to %d\n", shortcut->skip_to_level);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 244) sc_level = level + ASSOC_ARRAY_LEVEL_STEP;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 245) BUG_ON(sc_level > shortcut->skip_to_level);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 246)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 247) do {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 248) /* Check the leaf against the shortcut's index key a word at a
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 249) * time, trimming the final word (the shortcut stores the index
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 250) * key completely from the root to the shortcut's target).
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 251) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 252) if ((sc_level & ASSOC_ARRAY_KEY_CHUNK_MASK) == 0)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 253) segments = ops->get_key_chunk(index_key, sc_level);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 254)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 255) sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 256) dissimilarity = segments ^ sc_segments;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 257)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 258) if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 259) /* Trim segments that are beyond the shortcut */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 260) int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 261) dissimilarity &= ~(ULONG_MAX << shift);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 262) next_sc_level = shortcut->skip_to_level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 263) } else {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 264) next_sc_level = sc_level + ASSOC_ARRAY_KEY_CHUNK_SIZE;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 265) next_sc_level = round_down(next_sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 266) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 267)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 268) if (dissimilarity != 0) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 269) /* This shortcut points elsewhere */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 270) result->wrong_shortcut.shortcut = shortcut;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 271) result->wrong_shortcut.level = level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 272) result->wrong_shortcut.sc_level = sc_level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 273) result->wrong_shortcut.sc_segments = sc_segments;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 274) result->wrong_shortcut.dissimilarity = dissimilarity;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 275) return assoc_array_walk_found_wrong_shortcut;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 276) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 277)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 278) sc_level = next_sc_level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 279) } while (sc_level < shortcut->skip_to_level);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 280)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 281) /* The shortcut matches the leaf's index to this point. */
516df050615e4 (Paul E. McKenney 2017-10-09 11:39:57 -0700 282) cursor = READ_ONCE(shortcut->next_node); /* Address dependency. */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 283) if (((level ^ sc_level) & ~ASSOC_ARRAY_KEY_CHUNK_MASK) != 0) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 284) level = sc_level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 285) goto jumped;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 286) } else {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 287) level = sc_level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 288) goto consider_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 289) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 290) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 291)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 292) /**
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 293) * assoc_array_find - Find an object by index key
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 294) * @array: The associative array to search.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 295) * @ops: The operations to use.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 296) * @index_key: The key to the object.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 297) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 298) * Find an object in an associative array by walking through the internal tree
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 299) * to the node that should contain the object and then searching the leaves
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 300) * there. NULL is returned if the requested object was not found in the array.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 301) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 302) * The caller must hold the RCU read lock or better.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 303) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 304) void *assoc_array_find(const struct assoc_array *array,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 305) const struct assoc_array_ops *ops,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 306) const void *index_key)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 307) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 308) struct assoc_array_walk_result result;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 309) const struct assoc_array_node *node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 310) const struct assoc_array_ptr *ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 311) const void *leaf;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 312) int slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 313)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 314) if (assoc_array_walk(array, ops, index_key, &result) !=
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 315) assoc_array_walk_found_terminal_node)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 316) return NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 317)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 318) node = result.terminal_node.node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 319)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 320) /* If the target key is available to us, it's has to be pointed to by
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 321) * the terminal node.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 322) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 323) for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
516df050615e4 (Paul E. McKenney 2017-10-09 11:39:57 -0700 324) ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 325) if (ptr && assoc_array_ptr_is_leaf(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 326) /* We need a barrier between the read of the pointer
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 327) * and dereferencing the pointer - but only if we are
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 328) * actually going to dereference it.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 329) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 330) leaf = assoc_array_ptr_to_leaf(ptr);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 331) if (ops->compare_object(leaf, index_key))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 332) return (void *)leaf;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 333) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 334) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 335)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 336) return NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 337) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 338)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 339) /*
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 340) * Destructively iterate over an associative array. The caller must prevent
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 341) * other simultaneous accesses.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 342) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 343) static void assoc_array_destroy_subtree(struct assoc_array_ptr *root,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 344) const struct assoc_array_ops *ops)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 345) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 346) struct assoc_array_shortcut *shortcut;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 347) struct assoc_array_node *node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 348) struct assoc_array_ptr *cursor, *parent = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 349) int slot = -1;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 350)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 351) pr_devel("-->%s()\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 352)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 353) cursor = root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 354) if (!cursor) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 355) pr_devel("empty\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 356) return;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 357) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 358)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 359) move_to_meta:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 360) if (assoc_array_ptr_is_shortcut(cursor)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 361) /* Descend through a shortcut */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 362) pr_devel("[%d] shortcut\n", slot);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 363) BUG_ON(!assoc_array_ptr_is_shortcut(cursor));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 364) shortcut = assoc_array_ptr_to_shortcut(cursor);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 365) BUG_ON(shortcut->back_pointer != parent);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 366) BUG_ON(slot != -1 && shortcut->parent_slot != slot);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 367) parent = cursor;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 368) cursor = shortcut->next_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 369) slot = -1;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 370) BUG_ON(!assoc_array_ptr_is_node(cursor));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 371) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 372)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 373) pr_devel("[%d] node\n", slot);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 374) node = assoc_array_ptr_to_node(cursor);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 375) BUG_ON(node->back_pointer != parent);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 376) BUG_ON(slot != -1 && node->parent_slot != slot);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 377) slot = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 378)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 379) continue_node:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 380) pr_devel("Node %p [back=%p]\n", node, node->back_pointer);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 381) for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 382) struct assoc_array_ptr *ptr = node->slots[slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 383) if (!ptr)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 384) continue;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 385) if (assoc_array_ptr_is_meta(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 386) parent = cursor;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 387) cursor = ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 388) goto move_to_meta;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 389) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 390)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 391) if (ops) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 392) pr_devel("[%d] free leaf\n", slot);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 393) ops->free_object(assoc_array_ptr_to_leaf(ptr));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 394) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 395) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 396)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 397) parent = node->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 398) slot = node->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 399) pr_devel("free node\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 400) kfree(node);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 401) if (!parent)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 402) return; /* Done */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 403)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 404) /* Move back up to the parent (may need to free a shortcut on
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 405) * the way up) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 406) if (assoc_array_ptr_is_shortcut(parent)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 407) shortcut = assoc_array_ptr_to_shortcut(parent);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 408) BUG_ON(shortcut->next_node != cursor);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 409) cursor = parent;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 410) parent = shortcut->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 411) slot = shortcut->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 412) pr_devel("free shortcut\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 413) kfree(shortcut);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 414) if (!parent)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 415) return;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 416)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 417) BUG_ON(!assoc_array_ptr_is_node(parent));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 418) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 419)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 420) /* Ascend to next slot in parent node */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 421) pr_devel("ascend to %p[%d]\n", parent, slot);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 422) cursor = parent;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 423) node = assoc_array_ptr_to_node(cursor);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 424) slot++;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 425) goto continue_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 426) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 427)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 428) /**
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 429) * assoc_array_destroy - Destroy an associative array
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 430) * @array: The array to destroy.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 431) * @ops: The operations to use.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 432) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 433) * Discard all metadata and free all objects in an associative array. The
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 434) * array will be empty and ready to use again upon completion. This function
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 435) * cannot fail.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 436) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 437) * The caller must prevent all other accesses whilst this takes place as no
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 438) * attempt is made to adjust pointers gracefully to permit RCU readlock-holding
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 439) * accesses to continue. On the other hand, no memory allocation is required.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 440) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 441) void assoc_array_destroy(struct assoc_array *array,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 442) const struct assoc_array_ops *ops)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 443) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 444) assoc_array_destroy_subtree(array->root, ops);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 445) array->root = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 446) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 447)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 448) /*
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 449) * Handle insertion into an empty tree.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 450) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 451) static bool assoc_array_insert_in_empty_tree(struct assoc_array_edit *edit)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 452) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 453) struct assoc_array_node *new_n0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 454)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 455) pr_devel("-->%s()\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 456)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 457) new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 458) if (!new_n0)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 459) return false;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 460)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 461) edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 462) edit->leaf_p = &new_n0->slots[0];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 463) edit->adjust_count_on = new_n0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 464) edit->set[0].ptr = &edit->array->root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 465) edit->set[0].to = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 466)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 467) pr_devel("<--%s() = ok [no root]\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 468) return true;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 469) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 470)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 471) /*
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 472) * Handle insertion into a terminal node.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 473) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 474) static bool assoc_array_insert_into_terminal_node(struct assoc_array_edit *edit,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 475) const struct assoc_array_ops *ops,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 476) const void *index_key,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 477) struct assoc_array_walk_result *result)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 478) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 479) struct assoc_array_shortcut *shortcut, *new_s0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 480) struct assoc_array_node *node, *new_n0, *new_n1, *side;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 481) struct assoc_array_ptr *ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 482) unsigned long dissimilarity, base_seg, blank;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 483) size_t keylen;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 484) bool have_meta;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 485) int level, diff;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 486) int slot, next_slot, free_slot, i, j;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 487)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 488) node = result->terminal_node.node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 489) level = result->terminal_node.level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 490) edit->segment_cache[ASSOC_ARRAY_FAN_OUT] = result->terminal_node.slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 491)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 492) pr_devel("-->%s()\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 493)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 494) /* We arrived at a node which doesn't have an onward node or shortcut
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 495) * pointer that we have to follow. This means that (a) the leaf we
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 496) * want must go here (either by insertion or replacement) or (b) we
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 497) * need to split this node and insert in one of the fragments.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 498) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 499) free_slot = -1;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 500)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 501) /* Firstly, we have to check the leaves in this node to see if there's
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 502) * a matching one we should replace in place.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 503) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 504) for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 505) ptr = node->slots[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 506) if (!ptr) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 507) free_slot = i;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 508) continue;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 509) }
8d4a2ec1e0b41 (Jerome Marchand 2016-04-06 14:06:48 +0100 510) if (assoc_array_ptr_is_leaf(ptr) &&
8d4a2ec1e0b41 (Jerome Marchand 2016-04-06 14:06:48 +0100 511) ops->compare_object(assoc_array_ptr_to_leaf(ptr),
8d4a2ec1e0b41 (Jerome Marchand 2016-04-06 14:06:48 +0100 512) index_key)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 513) pr_devel("replace in slot %d\n", i);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 514) edit->leaf_p = &node->slots[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 515) edit->dead_leaf = node->slots[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 516) pr_devel("<--%s() = ok [replace]\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 517) return true;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 518) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 519) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 520)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 521) /* If there is a free slot in this node then we can just insert the
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 522) * leaf here.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 523) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 524) if (free_slot >= 0) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 525) pr_devel("insert in free slot %d\n", free_slot);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 526) edit->leaf_p = &node->slots[free_slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 527) edit->adjust_count_on = node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 528) pr_devel("<--%s() = ok [insert]\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 529) return true;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 530) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 531)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 532) /* The node has no spare slots - so we're either going to have to split
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 533) * it or insert another node before it.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 534) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 535) * Whatever, we're going to need at least two new nodes - so allocate
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 536) * those now. We may also need a new shortcut, but we deal with that
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 537) * when we need it.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 538) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 539) new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 540) if (!new_n0)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 541) return false;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 542) edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 543) new_n1 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 544) if (!new_n1)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 545) return false;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 546) edit->new_meta[1] = assoc_array_node_to_ptr(new_n1);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 547)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 548) /* We need to find out how similar the leaves are. */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 549) pr_devel("no spare slots\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 550) have_meta = false;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 551) for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 552) ptr = node->slots[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 553) if (assoc_array_ptr_is_meta(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 554) edit->segment_cache[i] = 0xff;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 555) have_meta = true;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 556) continue;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 557) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 558) base_seg = ops->get_object_key_chunk(
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 559) assoc_array_ptr_to_leaf(ptr), level);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 560) base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 561) edit->segment_cache[i] = base_seg & ASSOC_ARRAY_FAN_MASK;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 562) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 563)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 564) if (have_meta) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 565) pr_devel("have meta\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 566) goto split_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 567) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 568)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 569) /* The node contains only leaves */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 570) dissimilarity = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 571) base_seg = edit->segment_cache[0];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 572) for (i = 1; i < ASSOC_ARRAY_FAN_OUT; i++)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 573) dissimilarity |= edit->segment_cache[i] ^ base_seg;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 574)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 575) pr_devel("only leaves; dissimilarity=%lx\n", dissimilarity);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 576)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 577) if ((dissimilarity & ASSOC_ARRAY_FAN_MASK) == 0) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 578) /* The old leaves all cluster in the same slot. We will need
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 579) * to insert a shortcut if the new node wants to cluster with them.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 580) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 581) if ((edit->segment_cache[ASSOC_ARRAY_FAN_OUT] ^ base_seg) == 0)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 582) goto all_leaves_cluster_together;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 583)
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 584) /* Otherwise all the old leaves cluster in the same slot, but
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 585) * the new leaf wants to go into a different slot - so we
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 586) * create a new node (n0) to hold the new leaf and a pointer to
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 587) * a new node (n1) holding all the old leaves.
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 588) *
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 589) * This can be done by falling through to the node splitting
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 590) * path.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 591) */
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 592) pr_devel("present leaves cluster but not new leaf\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 593) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 594)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 595) split_node:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 596) pr_devel("split node\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 597)
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 598) /* We need to split the current node. The node must contain anything
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 599) * from a single leaf (in the one leaf case, this leaf will cluster
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 600) * with the new leaf) and the rest meta-pointers, to all leaves, some
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 601) * of which may cluster.
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 602) *
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 603) * It won't contain the case in which all the current leaves plus the
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 604) * new leaves want to cluster in the same slot.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 605) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 606) * We need to expel at least two leaves out of a set consisting of the
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 607) * leaves in the node and the new leaf. The current meta pointers can
ea6789980fdaa (David Howells 2017-10-11 23:32:27 +0100 608) * just be copied as they shouldn't cluster with any of the leaves.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 609) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 610) * We need a new node (n0) to replace the current one and a new node to
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 611) * take the expelled nodes (n1).
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 612) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 613) edit->set[0].to = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 614) new_n0->back_pointer = node->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 615) new_n0->parent_slot = node->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 616) new_n1->back_pointer = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 617) new_n1->parent_slot = -1; /* Need to calculate this */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 618)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 619) do_split_node:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 620) pr_devel("do_split_node\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 621)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 622) new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 623) new_n1->nr_leaves_on_branch = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 624)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 625) /* Begin by finding two matching leaves. There have to be at least two
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 626) * that match - even if there are meta pointers - because any leaf that
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 627) * would match a slot with a meta pointer in it must be somewhere
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 628) * behind that meta pointer and cannot be here. Further, given N
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 629) * remaining leaf slots, we now have N+1 leaves to go in them.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 630) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 631) for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 632) slot = edit->segment_cache[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 633) if (slot != 0xff)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 634) for (j = i + 1; j < ASSOC_ARRAY_FAN_OUT + 1; j++)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 635) if (edit->segment_cache[j] == slot)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 636) goto found_slot_for_multiple_occupancy;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 637) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 638) found_slot_for_multiple_occupancy:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 639) pr_devel("same slot: %x %x [%02x]\n", i, j, slot);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 640) BUG_ON(i >= ASSOC_ARRAY_FAN_OUT);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 641) BUG_ON(j >= ASSOC_ARRAY_FAN_OUT + 1);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 642) BUG_ON(slot >= ASSOC_ARRAY_FAN_OUT);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 643)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 644) new_n1->parent_slot = slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 645)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 646) /* Metadata pointers cannot change slot */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 647) for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 648) if (assoc_array_ptr_is_meta(node->slots[i]))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 649) new_n0->slots[i] = node->slots[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 650) else
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 651) new_n0->slots[i] = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 652) BUG_ON(new_n0->slots[slot] != NULL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 653) new_n0->slots[slot] = assoc_array_node_to_ptr(new_n1);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 654)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 655) /* Filter the leaf pointers between the new nodes */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 656) free_slot = -1;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 657) next_slot = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 658) for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 659) if (assoc_array_ptr_is_meta(node->slots[i]))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 660) continue;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 661) if (edit->segment_cache[i] == slot) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 662) new_n1->slots[next_slot++] = node->slots[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 663) new_n1->nr_leaves_on_branch++;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 664) } else {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 665) do {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 666) free_slot++;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 667) } while (new_n0->slots[free_slot] != NULL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 668) new_n0->slots[free_slot] = node->slots[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 669) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 670) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 671)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 672) pr_devel("filtered: f=%x n=%x\n", free_slot, next_slot);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 673)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 674) if (edit->segment_cache[ASSOC_ARRAY_FAN_OUT] != slot) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 675) do {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 676) free_slot++;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 677) } while (new_n0->slots[free_slot] != NULL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 678) edit->leaf_p = &new_n0->slots[free_slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 679) edit->adjust_count_on = new_n0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 680) } else {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 681) edit->leaf_p = &new_n1->slots[next_slot++];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 682) edit->adjust_count_on = new_n1;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 683) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 684)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 685) BUG_ON(next_slot <= 1);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 686)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 687) edit->set_backpointers_to = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 688) for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 689) if (edit->segment_cache[i] == 0xff) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 690) ptr = node->slots[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 691) BUG_ON(assoc_array_ptr_is_leaf(ptr));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 692) if (assoc_array_ptr_is_node(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 693) side = assoc_array_ptr_to_node(ptr);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 694) edit->set_backpointers[i] = &side->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 695) } else {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 696) shortcut = assoc_array_ptr_to_shortcut(ptr);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 697) edit->set_backpointers[i] = &shortcut->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 698) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 699) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 700) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 701)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 702) ptr = node->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 703) if (!ptr)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 704) edit->set[0].ptr = &edit->array->root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 705) else if (assoc_array_ptr_is_node(ptr))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 706) edit->set[0].ptr = &assoc_array_ptr_to_node(ptr)->slots[node->parent_slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 707) else
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 708) edit->set[0].ptr = &assoc_array_ptr_to_shortcut(ptr)->next_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 709) edit->excised_meta[0] = assoc_array_node_to_ptr(node);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 710) pr_devel("<--%s() = ok [split node]\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 711) return true;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 712)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 713) all_leaves_cluster_together:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 714) /* All the leaves, new and old, want to cluster together in this node
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 715) * in the same slot, so we have to replace this node with a shortcut to
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 716) * skip over the identical parts of the key and then place a pair of
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 717) * nodes, one inside the other, at the end of the shortcut and
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 718) * distribute the keys between them.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 719) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 720) * Firstly we need to work out where the leaves start diverging as a
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 721) * bit position into their keys so that we know how big the shortcut
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 722) * needs to be.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 723) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 724) * We only need to make a single pass of N of the N+1 leaves because if
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 725) * any keys differ between themselves at bit X then at least one of
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 726) * them must also differ with the base key at bit X or before.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 727) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 728) pr_devel("all leaves cluster together\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 729) diff = INT_MAX;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 730) for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
23fd78d764157 (David Howells 2013-12-02 11:24:18 +0000 731) int x = ops->diff_objects(assoc_array_ptr_to_leaf(node->slots[i]),
23fd78d764157 (David Howells 2013-12-02 11:24:18 +0000 732) index_key);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 733) if (x < diff) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 734) BUG_ON(x < 0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 735) diff = x;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 736) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 737) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 738) BUG_ON(diff == INT_MAX);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 739) BUG_ON(diff < level + ASSOC_ARRAY_LEVEL_STEP);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 740)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 741) keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 742) keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 743)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 744) new_s0 = kzalloc(sizeof(struct assoc_array_shortcut) +
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 745) keylen * sizeof(unsigned long), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 746) if (!new_s0)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 747) return false;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 748) edit->new_meta[2] = assoc_array_shortcut_to_ptr(new_s0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 749)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 750) edit->set[0].to = assoc_array_shortcut_to_ptr(new_s0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 751) new_s0->back_pointer = node->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 752) new_s0->parent_slot = node->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 753) new_s0->next_node = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 754) new_n0->back_pointer = assoc_array_shortcut_to_ptr(new_s0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 755) new_n0->parent_slot = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 756) new_n1->back_pointer = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 757) new_n1->parent_slot = -1; /* Need to calculate this */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 758)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 759) new_s0->skip_to_level = level = diff & ~ASSOC_ARRAY_LEVEL_STEP_MASK;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 760) pr_devel("skip_to_level = %d [diff %d]\n", level, diff);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 761) BUG_ON(level <= 0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 762)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 763) for (i = 0; i < keylen; i++)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 764) new_s0->index_key[i] =
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 765) ops->get_key_chunk(index_key, i * ASSOC_ARRAY_KEY_CHUNK_SIZE);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 766)
bb2ba2d75a2d6 (David Howells 2019-02-14 16:20:15 +0000 767) if (level & ASSOC_ARRAY_KEY_CHUNK_MASK) {
bb2ba2d75a2d6 (David Howells 2019-02-14 16:20:15 +0000 768) blank = ULONG_MAX << (level & ASSOC_ARRAY_KEY_CHUNK_MASK);
bb2ba2d75a2d6 (David Howells 2019-02-14 16:20:15 +0000 769) pr_devel("blank off [%zu] %d: %lx\n", keylen - 1, level, blank);
bb2ba2d75a2d6 (David Howells 2019-02-14 16:20:15 +0000 770) new_s0->index_key[keylen - 1] &= ~blank;
bb2ba2d75a2d6 (David Howells 2019-02-14 16:20:15 +0000 771) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 772)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 773) /* This now reduces to a node splitting exercise for which we'll need
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 774) * to regenerate the disparity table.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 775) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 776) for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 777) ptr = node->slots[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 778) base_seg = ops->get_object_key_chunk(assoc_array_ptr_to_leaf(ptr),
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 779) level);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 780) base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 781) edit->segment_cache[i] = base_seg & ASSOC_ARRAY_FAN_MASK;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 782) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 783)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 784) base_seg = ops->get_key_chunk(index_key, level);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 785) base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 786) edit->segment_cache[ASSOC_ARRAY_FAN_OUT] = base_seg & ASSOC_ARRAY_FAN_MASK;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 787) goto do_split_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 788) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 789)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 790) /*
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 791) * Handle insertion into the middle of a shortcut.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 792) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 793) static bool assoc_array_insert_mid_shortcut(struct assoc_array_edit *edit,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 794) const struct assoc_array_ops *ops,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 795) struct assoc_array_walk_result *result)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 796) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 797) struct assoc_array_shortcut *shortcut, *new_s0, *new_s1;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 798) struct assoc_array_node *node, *new_n0, *side;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 799) unsigned long sc_segments, dissimilarity, blank;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 800) size_t keylen;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 801) int level, sc_level, diff;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 802) int sc_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 803)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 804) shortcut = result->wrong_shortcut.shortcut;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 805) level = result->wrong_shortcut.level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 806) sc_level = result->wrong_shortcut.sc_level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 807) sc_segments = result->wrong_shortcut.sc_segments;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 808) dissimilarity = result->wrong_shortcut.dissimilarity;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 809)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 810) pr_devel("-->%s(ix=%d dis=%lx scix=%d)\n",
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 811) __func__, level, dissimilarity, sc_level);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 812)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 813) /* We need to split a shortcut and insert a node between the two
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 814) * pieces. Zero-length pieces will be dispensed with entirely.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 815) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 816) * First of all, we need to find out in which level the first
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 817) * difference was.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 818) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 819) diff = __ffs(dissimilarity);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 820) diff &= ~ASSOC_ARRAY_LEVEL_STEP_MASK;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 821) diff += sc_level & ~ASSOC_ARRAY_KEY_CHUNK_MASK;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 822) pr_devel("diff=%d\n", diff);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 823)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 824) if (!shortcut->back_pointer) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 825) edit->set[0].ptr = &edit->array->root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 826) } else if (assoc_array_ptr_is_node(shortcut->back_pointer)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 827) node = assoc_array_ptr_to_node(shortcut->back_pointer);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 828) edit->set[0].ptr = &node->slots[shortcut->parent_slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 829) } else {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 830) BUG();
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 831) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 832)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 833) edit->excised_meta[0] = assoc_array_shortcut_to_ptr(shortcut);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 834)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 835) /* Create a new node now since we're going to need it anyway */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 836) new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 837) if (!new_n0)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 838) return false;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 839) edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 840) edit->adjust_count_on = new_n0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 841)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 842) /* Insert a new shortcut before the new node if this segment isn't of
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 843) * zero length - otherwise we just connect the new node directly to the
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 844) * parent.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 845) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 846) level += ASSOC_ARRAY_LEVEL_STEP;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 847) if (diff > level) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 848) pr_devel("pre-shortcut %d...%d\n", level, diff);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 849) keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 850) keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 851)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 852) new_s0 = kzalloc(sizeof(struct assoc_array_shortcut) +
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 853) keylen * sizeof(unsigned long), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 854) if (!new_s0)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 855) return false;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 856) edit->new_meta[1] = assoc_array_shortcut_to_ptr(new_s0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 857) edit->set[0].to = assoc_array_shortcut_to_ptr(new_s0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 858) new_s0->back_pointer = shortcut->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 859) new_s0->parent_slot = shortcut->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 860) new_s0->next_node = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 861) new_s0->skip_to_level = diff;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 862)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 863) new_n0->back_pointer = assoc_array_shortcut_to_ptr(new_s0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 864) new_n0->parent_slot = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 865)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 866) memcpy(new_s0->index_key, shortcut->index_key,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 867) keylen * sizeof(unsigned long));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 868)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 869) blank = ULONG_MAX << (diff & ASSOC_ARRAY_KEY_CHUNK_MASK);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 870) pr_devel("blank off [%zu] %d: %lx\n", keylen - 1, diff, blank);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 871) new_s0->index_key[keylen - 1] &= ~blank;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 872) } else {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 873) pr_devel("no pre-shortcut\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 874) edit->set[0].to = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 875) new_n0->back_pointer = shortcut->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 876) new_n0->parent_slot = shortcut->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 877) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 878)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 879) side = assoc_array_ptr_to_node(shortcut->next_node);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 880) new_n0->nr_leaves_on_branch = side->nr_leaves_on_branch;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 881)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 882) /* We need to know which slot in the new node is going to take a
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 883) * metadata pointer.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 884) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 885) sc_slot = sc_segments >> (diff & ASSOC_ARRAY_KEY_CHUNK_MASK);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 886) sc_slot &= ASSOC_ARRAY_FAN_MASK;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 887)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 888) pr_devel("new slot %lx >> %d -> %d\n",
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 889) sc_segments, diff & ASSOC_ARRAY_KEY_CHUNK_MASK, sc_slot);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 890)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 891) /* Determine whether we need to follow the new node with a replacement
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 892) * for the current shortcut. We could in theory reuse the current
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 893) * shortcut if its parent slot number doesn't change - but that's a
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 894) * 1-in-16 chance so not worth expending the code upon.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 895) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 896) level = diff + ASSOC_ARRAY_LEVEL_STEP;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 897) if (level < shortcut->skip_to_level) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 898) pr_devel("post-shortcut %d...%d\n", level, shortcut->skip_to_level);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 899) keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 900) keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 901)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 902) new_s1 = kzalloc(sizeof(struct assoc_array_shortcut) +
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 903) keylen * sizeof(unsigned long), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 904) if (!new_s1)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 905) return false;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 906) edit->new_meta[2] = assoc_array_shortcut_to_ptr(new_s1);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 907)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 908) new_s1->back_pointer = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 909) new_s1->parent_slot = sc_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 910) new_s1->next_node = shortcut->next_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 911) new_s1->skip_to_level = shortcut->skip_to_level;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 912)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 913) new_n0->slots[sc_slot] = assoc_array_shortcut_to_ptr(new_s1);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 914)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 915) memcpy(new_s1->index_key, shortcut->index_key,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 916) keylen * sizeof(unsigned long));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 917)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 918) edit->set[1].ptr = &side->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 919) edit->set[1].to = assoc_array_shortcut_to_ptr(new_s1);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 920) } else {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 921) pr_devel("no post-shortcut\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 922)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 923) /* We don't have to replace the pointed-to node as long as we
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 924) * use memory barriers to make sure the parent slot number is
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 925) * changed before the back pointer (the parent slot number is
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 926) * irrelevant to the old parent shortcut).
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 927) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 928) new_n0->slots[sc_slot] = shortcut->next_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 929) edit->set_parent_slot[0].p = &side->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 930) edit->set_parent_slot[0].to = sc_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 931) edit->set[1].ptr = &side->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 932) edit->set[1].to = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 933) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 934)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 935) /* Install the new leaf in a spare slot in the new node. */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 936) if (sc_slot == 0)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 937) edit->leaf_p = &new_n0->slots[1];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 938) else
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 939) edit->leaf_p = &new_n0->slots[0];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 940)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 941) pr_devel("<--%s() = ok [split shortcut]\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 942) return edit;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 943) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 944)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 945) /**
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 946) * assoc_array_insert - Script insertion of an object into an associative array
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 947) * @array: The array to insert into.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 948) * @ops: The operations to use.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 949) * @index_key: The key to insert at.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 950) * @object: The object to insert.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 951) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 952) * Precalculate and preallocate a script for the insertion or replacement of an
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 953) * object in an associative array. This results in an edit script that can
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 954) * either be applied or cancelled.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 955) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 956) * The function returns a pointer to an edit script or -ENOMEM.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 957) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 958) * The caller should lock against other modifications and must continue to hold
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 959) * the lock until assoc_array_apply_edit() has been called.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 960) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 961) * Accesses to the tree may take place concurrently with this function,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 962) * provided they hold the RCU read lock.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 963) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 964) struct assoc_array_edit *assoc_array_insert(struct assoc_array *array,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 965) const struct assoc_array_ops *ops,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 966) const void *index_key,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 967) void *object)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 968) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 969) struct assoc_array_walk_result result;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 970) struct assoc_array_edit *edit;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 971)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 972) pr_devel("-->%s()\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 973)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 974) /* The leaf pointer we're given must not have the bottom bit set as we
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 975) * use those for type-marking the pointer. NULL pointers are also not
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 976) * allowed as they indicate an empty slot but we have to allow them
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 977) * here as they can be updated later.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 978) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 979) BUG_ON(assoc_array_ptr_is_meta(object));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 980)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 981) edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 982) if (!edit)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 983) return ERR_PTR(-ENOMEM);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 984) edit->array = array;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 985) edit->ops = ops;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 986) edit->leaf = assoc_array_leaf_to_ptr(object);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 987) edit->adjust_count_by = 1;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 988)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 989) switch (assoc_array_walk(array, ops, index_key, &result)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 990) case assoc_array_walk_tree_empty:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 991) /* Allocate a root node if there isn't one yet */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 992) if (!assoc_array_insert_in_empty_tree(edit))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 993) goto enomem;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 994) return edit;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 995)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 996) case assoc_array_walk_found_terminal_node:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 997) /* We found a node that doesn't have a node/shortcut pointer in
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 998) * the slot corresponding to the index key that we have to
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 999) * follow.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1000) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1001) if (!assoc_array_insert_into_terminal_node(edit, ops, index_key,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1002) &result))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1003) goto enomem;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1004) return edit;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1005)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1006) case assoc_array_walk_found_wrong_shortcut:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1007) /* We found a shortcut that didn't match our key in a slot we
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1008) * needed to follow.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1009) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1010) if (!assoc_array_insert_mid_shortcut(edit, ops, &result))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1011) goto enomem;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1012) return edit;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1013) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1014)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1015) enomem:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1016) /* Clean up after an out of memory error */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1017) pr_devel("enomem\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1018) assoc_array_cancel_edit(edit);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1019) return ERR_PTR(-ENOMEM);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1020) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1021)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1022) /**
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1023) * assoc_array_insert_set_object - Set the new object pointer in an edit script
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1024) * @edit: The edit script to modify.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1025) * @object: The object pointer to set.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1026) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1027) * Change the object to be inserted in an edit script. The object pointed to
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1028) * by the old object is not freed. This must be done prior to applying the
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1029) * script.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1030) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1031) void assoc_array_insert_set_object(struct assoc_array_edit *edit, void *object)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1032) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1033) BUG_ON(!object);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1034) edit->leaf = assoc_array_leaf_to_ptr(object);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1035) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1036)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1037) struct assoc_array_delete_collapse_context {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1038) struct assoc_array_node *node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1039) const void *skip_leaf;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1040) int slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1041) };
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1042)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1043) /*
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1044) * Subtree collapse to node iterator.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1045) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1046) static int assoc_array_delete_collapse_iterator(const void *leaf,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1047) void *iterator_data)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1048) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1049) struct assoc_array_delete_collapse_context *collapse = iterator_data;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1050)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1051) if (leaf == collapse->skip_leaf)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1052) return 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1053)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1054) BUG_ON(collapse->slot >= ASSOC_ARRAY_FAN_OUT);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1055)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1056) collapse->node->slots[collapse->slot++] = assoc_array_leaf_to_ptr(leaf);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1057) return 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1058) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1059)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1060) /**
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1061) * assoc_array_delete - Script deletion of an object from an associative array
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1062) * @array: The array to search.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1063) * @ops: The operations to use.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1064) * @index_key: The key to the object.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1065) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1066) * Precalculate and preallocate a script for the deletion of an object from an
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1067) * associative array. This results in an edit script that can either be
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1068) * applied or cancelled.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1069) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1070) * The function returns a pointer to an edit script if the object was found,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1071) * NULL if the object was not found or -ENOMEM.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1072) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1073) * The caller should lock against other modifications and must continue to hold
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1074) * the lock until assoc_array_apply_edit() has been called.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1075) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1076) * Accesses to the tree may take place concurrently with this function,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1077) * provided they hold the RCU read lock.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1078) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1079) struct assoc_array_edit *assoc_array_delete(struct assoc_array *array,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1080) const struct assoc_array_ops *ops,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1081) const void *index_key)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1082) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1083) struct assoc_array_delete_collapse_context collapse;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1084) struct assoc_array_walk_result result;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1085) struct assoc_array_node *node, *new_n0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1086) struct assoc_array_edit *edit;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1087) struct assoc_array_ptr *ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1088) bool has_meta;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1089) int slot, i;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1090)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1091) pr_devel("-->%s()\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1092)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1093) edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1094) if (!edit)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1095) return ERR_PTR(-ENOMEM);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1096) edit->array = array;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1097) edit->ops = ops;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1098) edit->adjust_count_by = -1;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1099)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1100) switch (assoc_array_walk(array, ops, index_key, &result)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1101) case assoc_array_walk_found_terminal_node:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1102) /* We found a node that should contain the leaf we've been
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1103) * asked to remove - *if* it's in the tree.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1104) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1105) pr_devel("terminal_node\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1106) node = result.terminal_node.node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1107)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1108) for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1109) ptr = node->slots[slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1110) if (ptr &&
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1111) assoc_array_ptr_is_leaf(ptr) &&
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1112) ops->compare_object(assoc_array_ptr_to_leaf(ptr),
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1113) index_key))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1114) goto found_leaf;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1115) }
4c1ca831adb10 (Nick Desaulniers 2020-11-15 20:35:31 -0800 1116) fallthrough;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1117) case assoc_array_walk_tree_empty:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1118) case assoc_array_walk_found_wrong_shortcut:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1119) default:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1120) assoc_array_cancel_edit(edit);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1121) pr_devel("not found\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1122) return NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1123) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1124)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1125) found_leaf:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1126) BUG_ON(array->nr_leaves_on_tree <= 0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1127)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1128) /* In the simplest form of deletion we just clear the slot and release
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1129) * the leaf after a suitable interval.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1130) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1131) edit->dead_leaf = node->slots[slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1132) edit->set[0].ptr = &node->slots[slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1133) edit->set[0].to = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1134) edit->adjust_count_on = node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1135)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1136) /* If that concludes erasure of the last leaf, then delete the entire
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1137) * internal array.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1138) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1139) if (array->nr_leaves_on_tree == 1) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1140) edit->set[1].ptr = &array->root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1141) edit->set[1].to = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1142) edit->adjust_count_on = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1143) edit->excised_subtree = array->root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1144) pr_devel("all gone\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1145) return edit;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1146) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1147)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1148) /* However, we'd also like to clear up some metadata blocks if we
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1149) * possibly can.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1150) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1151) * We go for a simple algorithm of: if this node has FAN_OUT or fewer
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1152) * leaves in it, then attempt to collapse it - and attempt to
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1153) * recursively collapse up the tree.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1154) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1155) * We could also try and collapse in partially filled subtrees to take
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1156) * up space in this node.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1157) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1158) if (node->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT + 1) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1159) struct assoc_array_node *parent, *grandparent;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1160) struct assoc_array_ptr *ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1161)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1162) /* First of all, we need to know if this node has metadata so
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1163) * that we don't try collapsing if all the leaves are already
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1164) * here.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1165) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1166) has_meta = false;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1167) for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1168) ptr = node->slots[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1169) if (assoc_array_ptr_is_meta(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1170) has_meta = true;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1171) break;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1172) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1173) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1174)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1175) pr_devel("leaves: %ld [m=%d]\n",
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1176) node->nr_leaves_on_branch - 1, has_meta);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1177)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1178) /* Look further up the tree to see if we can collapse this node
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1179) * into a more proximal node too.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1180) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1181) parent = node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1182) collapse_up:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1183) pr_devel("collapse subtree: %ld\n", parent->nr_leaves_on_branch);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1184)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1185) ptr = parent->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1186) if (!ptr)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1187) goto do_collapse;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1188) if (assoc_array_ptr_is_shortcut(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1189) struct assoc_array_shortcut *s = assoc_array_ptr_to_shortcut(ptr);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1190) ptr = s->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1191) if (!ptr)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1192) goto do_collapse;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1193) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1194)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1195) grandparent = assoc_array_ptr_to_node(ptr);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1196) if (grandparent->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT + 1) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1197) parent = grandparent;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1198) goto collapse_up;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1199) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1200)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1201) do_collapse:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1202) /* There's no point collapsing if the original node has no meta
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1203) * pointers to discard and if we didn't merge into one of that
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1204) * node's ancestry.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1205) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1206) if (has_meta || parent != node) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1207) node = parent;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1208)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1209) /* Create a new node to collapse into */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1210) new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1211) if (!new_n0)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1212) goto enomem;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1213) edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1214)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1215) new_n0->back_pointer = node->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1216) new_n0->parent_slot = node->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1217) new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1218) edit->adjust_count_on = new_n0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1219)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1220) collapse.node = new_n0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1221) collapse.skip_leaf = assoc_array_ptr_to_leaf(edit->dead_leaf);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1222) collapse.slot = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1223) assoc_array_subtree_iterate(assoc_array_node_to_ptr(node),
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1224) node->back_pointer,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1225) assoc_array_delete_collapse_iterator,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1226) &collapse);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1227) pr_devel("collapsed %d,%lu\n", collapse.slot, new_n0->nr_leaves_on_branch);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1228) BUG_ON(collapse.slot != new_n0->nr_leaves_on_branch - 1);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1229)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1230) if (!node->back_pointer) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1231) edit->set[1].ptr = &array->root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1232) } else if (assoc_array_ptr_is_leaf(node->back_pointer)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1233) BUG();
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1234) } else if (assoc_array_ptr_is_node(node->back_pointer)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1235) struct assoc_array_node *p =
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1236) assoc_array_ptr_to_node(node->back_pointer);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1237) edit->set[1].ptr = &p->slots[node->parent_slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1238) } else if (assoc_array_ptr_is_shortcut(node->back_pointer)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1239) struct assoc_array_shortcut *s =
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1240) assoc_array_ptr_to_shortcut(node->back_pointer);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1241) edit->set[1].ptr = &s->next_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1242) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1243) edit->set[1].to = assoc_array_node_to_ptr(new_n0);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1244) edit->excised_subtree = assoc_array_node_to_ptr(node);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1245) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1246) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1247)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1248) return edit;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1249)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1250) enomem:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1251) /* Clean up after an out of memory error */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1252) pr_devel("enomem\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1253) assoc_array_cancel_edit(edit);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1254) return ERR_PTR(-ENOMEM);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1255) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1256)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1257) /**
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1258) * assoc_array_clear - Script deletion of all objects from an associative array
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1259) * @array: The array to clear.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1260) * @ops: The operations to use.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1261) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1262) * Precalculate and preallocate a script for the deletion of all the objects
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1263) * from an associative array. This results in an edit script that can either
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1264) * be applied or cancelled.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1265) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1266) * The function returns a pointer to an edit script if there are objects to be
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1267) * deleted, NULL if there are no objects in the array or -ENOMEM.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1268) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1269) * The caller should lock against other modifications and must continue to hold
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1270) * the lock until assoc_array_apply_edit() has been called.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1271) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1272) * Accesses to the tree may take place concurrently with this function,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1273) * provided they hold the RCU read lock.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1274) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1275) struct assoc_array_edit *assoc_array_clear(struct assoc_array *array,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1276) const struct assoc_array_ops *ops)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1277) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1278) struct assoc_array_edit *edit;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1279)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1280) pr_devel("-->%s()\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1281)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1282) if (!array->root)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1283) return NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1284)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1285) edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1286) if (!edit)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1287) return ERR_PTR(-ENOMEM);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1288) edit->array = array;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1289) edit->ops = ops;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1290) edit->set[1].ptr = &array->root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1291) edit->set[1].to = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1292) edit->excised_subtree = array->root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1293) edit->ops_for_excised_subtree = ops;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1294) pr_devel("all gone\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1295) return edit;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1296) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1297)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1298) /*
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1299) * Handle the deferred destruction after an applied edit.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1300) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1301) static void assoc_array_rcu_cleanup(struct rcu_head *head)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1302) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1303) struct assoc_array_edit *edit =
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1304) container_of(head, struct assoc_array_edit, rcu);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1305) int i;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1306)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1307) pr_devel("-->%s()\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1308)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1309) if (edit->dead_leaf)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1310) edit->ops->free_object(assoc_array_ptr_to_leaf(edit->dead_leaf));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1311) for (i = 0; i < ARRAY_SIZE(edit->excised_meta); i++)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1312) if (edit->excised_meta[i])
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1313) kfree(assoc_array_ptr_to_node(edit->excised_meta[i]));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1314)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1315) if (edit->excised_subtree) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1316) BUG_ON(assoc_array_ptr_is_leaf(edit->excised_subtree));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1317) if (assoc_array_ptr_is_node(edit->excised_subtree)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1318) struct assoc_array_node *n =
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1319) assoc_array_ptr_to_node(edit->excised_subtree);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1320) n->back_pointer = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1321) } else {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1322) struct assoc_array_shortcut *s =
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1323) assoc_array_ptr_to_shortcut(edit->excised_subtree);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1324) s->back_pointer = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1325) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1326) assoc_array_destroy_subtree(edit->excised_subtree,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1327) edit->ops_for_excised_subtree);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1328) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1329)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1330) kfree(edit);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1331) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1332)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1333) /**
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1334) * assoc_array_apply_edit - Apply an edit script to an associative array
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1335) * @edit: The script to apply.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1336) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1337) * Apply an edit script to an associative array to effect an insertion,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1338) * deletion or clearance. As the edit script includes preallocated memory,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1339) * this is guaranteed not to fail.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1340) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1341) * The edit script, dead objects and dead metadata will be scheduled for
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1342) * destruction after an RCU grace period to permit those doing read-only
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1343) * accesses on the array to continue to do so under the RCU read lock whilst
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1344) * the edit is taking place.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1345) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1346) void assoc_array_apply_edit(struct assoc_array_edit *edit)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1347) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1348) struct assoc_array_shortcut *shortcut;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1349) struct assoc_array_node *node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1350) struct assoc_array_ptr *ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1351) int i;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1352)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1353) pr_devel("-->%s()\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1354)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1355) smp_wmb();
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1356) if (edit->leaf_p)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1357) *edit->leaf_p = edit->leaf;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1358)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1359) smp_wmb();
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1360) for (i = 0; i < ARRAY_SIZE(edit->set_parent_slot); i++)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1361) if (edit->set_parent_slot[i].p)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1362) *edit->set_parent_slot[i].p = edit->set_parent_slot[i].to;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1363)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1364) smp_wmb();
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1365) for (i = 0; i < ARRAY_SIZE(edit->set_backpointers); i++)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1366) if (edit->set_backpointers[i])
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1367) *edit->set_backpointers[i] = edit->set_backpointers_to;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1368)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1369) smp_wmb();
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1370) for (i = 0; i < ARRAY_SIZE(edit->set); i++)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1371) if (edit->set[i].ptr)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1372) *edit->set[i].ptr = edit->set[i].to;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1373)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1374) if (edit->array->root == NULL) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1375) edit->array->nr_leaves_on_tree = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1376) } else if (edit->adjust_count_on) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1377) node = edit->adjust_count_on;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1378) for (;;) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1379) node->nr_leaves_on_branch += edit->adjust_count_by;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1380)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1381) ptr = node->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1382) if (!ptr)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1383) break;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1384) if (assoc_array_ptr_is_shortcut(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1385) shortcut = assoc_array_ptr_to_shortcut(ptr);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1386) ptr = shortcut->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1387) if (!ptr)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1388) break;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1389) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1390) BUG_ON(!assoc_array_ptr_is_node(ptr));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1391) node = assoc_array_ptr_to_node(ptr);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1392) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1393)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1394) edit->array->nr_leaves_on_tree += edit->adjust_count_by;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1395) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1396)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1397) call_rcu(&edit->rcu, assoc_array_rcu_cleanup);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1398) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1399)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1400) /**
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1401) * assoc_array_cancel_edit - Discard an edit script.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1402) * @edit: The script to discard.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1403) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1404) * Free an edit script and all the preallocated data it holds without making
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1405) * any changes to the associative array it was intended for.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1406) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1407) * NOTE! In the case of an insertion script, this does _not_ release the leaf
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1408) * that was to be inserted. That is left to the caller.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1409) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1410) void assoc_array_cancel_edit(struct assoc_array_edit *edit)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1411) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1412) struct assoc_array_ptr *ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1413) int i;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1414)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1415) pr_devel("-->%s()\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1416)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1417) /* Clean up after an out of memory error */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1418) for (i = 0; i < ARRAY_SIZE(edit->new_meta); i++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1419) ptr = edit->new_meta[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1420) if (ptr) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1421) if (assoc_array_ptr_is_node(ptr))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1422) kfree(assoc_array_ptr_to_node(ptr));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1423) else
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1424) kfree(assoc_array_ptr_to_shortcut(ptr));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1425) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1426) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1427) kfree(edit);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1428) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1429)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1430) /**
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1431) * assoc_array_gc - Garbage collect an associative array.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1432) * @array: The array to clean.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1433) * @ops: The operations to use.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1434) * @iterator: A callback function to pass judgement on each object.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1435) * @iterator_data: Private data for the callback function.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1436) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1437) * Collect garbage from an associative array and pack down the internal tree to
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1438) * save memory.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1439) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1440) * The iterator function is asked to pass judgement upon each object in the
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1441) * array. If it returns false, the object is discard and if it returns true,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1442) * the object is kept. If it returns true, it must increment the object's
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1443) * usage count (or whatever it needs to do to retain it) before returning.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1444) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1445) * This function returns 0 if successful or -ENOMEM if out of memory. In the
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1446) * latter case, the array is not changed.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1447) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1448) * The caller should lock against other modifications and must continue to hold
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1449) * the lock until assoc_array_apply_edit() has been called.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1450) *
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1451) * Accesses to the tree may take place concurrently with this function,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1452) * provided they hold the RCU read lock.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1453) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1454) int assoc_array_gc(struct assoc_array *array,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1455) const struct assoc_array_ops *ops,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1456) bool (*iterator)(void *object, void *iterator_data),
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1457) void *iterator_data)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1458) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1459) struct assoc_array_shortcut *shortcut, *new_s;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1460) struct assoc_array_node *node, *new_n;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1461) struct assoc_array_edit *edit;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1462) struct assoc_array_ptr *cursor, *ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1463) struct assoc_array_ptr *new_root, *new_parent, **new_ptr_pp;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1464) unsigned long nr_leaves_on_tree;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1465) int keylen, slot, nr_free, next_slot, i;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1466)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1467) pr_devel("-->%s()\n", __func__);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1468)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1469) if (!array->root)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1470) return 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1471)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1472) edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1473) if (!edit)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1474) return -ENOMEM;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1475) edit->array = array;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1476) edit->ops = ops;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1477) edit->ops_for_excised_subtree = ops;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1478) edit->set[0].ptr = &array->root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1479) edit->excised_subtree = array->root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1480)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1481) new_root = new_parent = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1482) new_ptr_pp = &new_root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1483) cursor = array->root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1484)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1485) descend:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1486) /* If this point is a shortcut, then we need to duplicate it and
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1487) * advance the target cursor.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1488) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1489) if (assoc_array_ptr_is_shortcut(cursor)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1490) shortcut = assoc_array_ptr_to_shortcut(cursor);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1491) keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1492) keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1493) new_s = kmalloc(sizeof(struct assoc_array_shortcut) +
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1494) keylen * sizeof(unsigned long), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1495) if (!new_s)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1496) goto enomem;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1497) pr_devel("dup shortcut %p -> %p\n", shortcut, new_s);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1498) memcpy(new_s, shortcut, (sizeof(struct assoc_array_shortcut) +
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1499) keylen * sizeof(unsigned long)));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1500) new_s->back_pointer = new_parent;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1501) new_s->parent_slot = shortcut->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1502) *new_ptr_pp = new_parent = assoc_array_shortcut_to_ptr(new_s);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1503) new_ptr_pp = &new_s->next_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1504) cursor = shortcut->next_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1505) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1506)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1507) /* Duplicate the node at this position */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1508) node = assoc_array_ptr_to_node(cursor);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1509) new_n = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1510) if (!new_n)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1511) goto enomem;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1512) pr_devel("dup node %p -> %p\n", node, new_n);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1513) new_n->back_pointer = new_parent;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1514) new_n->parent_slot = node->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1515) *new_ptr_pp = new_parent = assoc_array_node_to_ptr(new_n);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1516) new_ptr_pp = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1517) slot = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1518)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1519) continue_node:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1520) /* Filter across any leaves and gc any subtrees */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1521) for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1522) ptr = node->slots[slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1523) if (!ptr)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1524) continue;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1525)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1526) if (assoc_array_ptr_is_leaf(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1527) if (iterator(assoc_array_ptr_to_leaf(ptr),
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1528) iterator_data))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1529) /* The iterator will have done any reference
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1530) * counting on the object for us.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1531) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1532) new_n->slots[slot] = ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1533) continue;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1534) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1535)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1536) new_ptr_pp = &new_n->slots[slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1537) cursor = ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1538) goto descend;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1539) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1540)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1541) pr_devel("-- compress node %p --\n", new_n);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1542)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1543) /* Count up the number of empty slots in this node and work out the
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1544) * subtree leaf count.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1545) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1546) new_n->nr_leaves_on_branch = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1547) nr_free = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1548) for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1549) ptr = new_n->slots[slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1550) if (!ptr)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1551) nr_free++;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1552) else if (assoc_array_ptr_is_leaf(ptr))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1553) new_n->nr_leaves_on_branch++;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1554) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1555) pr_devel("free=%d, leaves=%lu\n", nr_free, new_n->nr_leaves_on_branch);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1556)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1557) /* See what we can fold in */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1558) next_slot = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1559) for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1560) struct assoc_array_shortcut *s;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1561) struct assoc_array_node *child;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1562)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1563) ptr = new_n->slots[slot];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1564) if (!ptr || assoc_array_ptr_is_leaf(ptr))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1565) continue;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1566)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1567) s = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1568) if (assoc_array_ptr_is_shortcut(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1569) s = assoc_array_ptr_to_shortcut(ptr);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1570) ptr = s->next_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1571) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1572)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1573) child = assoc_array_ptr_to_node(ptr);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1574) new_n->nr_leaves_on_branch += child->nr_leaves_on_branch;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1575)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1576) if (child->nr_leaves_on_branch <= nr_free + 1) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1577) /* Fold the child node into this one */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1578) pr_devel("[%d] fold node %lu/%d [nx %d]\n",
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1579) slot, child->nr_leaves_on_branch, nr_free + 1,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1580) next_slot);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1581)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1582) /* We would already have reaped an intervening shortcut
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1583) * on the way back up the tree.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1584) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1585) BUG_ON(s);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1586)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1587) new_n->slots[slot] = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1588) nr_free++;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1589) if (slot < next_slot)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1590) next_slot = slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1591) for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1592) struct assoc_array_ptr *p = child->slots[i];
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1593) if (!p)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1594) continue;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1595) BUG_ON(assoc_array_ptr_is_meta(p));
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1596) while (new_n->slots[next_slot])
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1597) next_slot++;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1598) BUG_ON(next_slot >= ASSOC_ARRAY_FAN_OUT);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1599) new_n->slots[next_slot++] = p;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1600) nr_free--;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1601) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1602) kfree(child);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1603) } else {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1604) pr_devel("[%d] retain node %lu/%d [nx %d]\n",
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1605) slot, child->nr_leaves_on_branch, nr_free + 1,
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1606) next_slot);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1607) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1608) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1609)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1610) pr_devel("after: %lu\n", new_n->nr_leaves_on_branch);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1611)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1612) nr_leaves_on_tree = new_n->nr_leaves_on_branch;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1613)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1614) /* Excise this node if it is singly occupied by a shortcut */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1615) if (nr_free == ASSOC_ARRAY_FAN_OUT - 1) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1616) for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1617) if ((ptr = new_n->slots[slot]))
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1618) break;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1619)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1620) if (assoc_array_ptr_is_meta(ptr) &&
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1621) assoc_array_ptr_is_shortcut(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1622) pr_devel("excise node %p with 1 shortcut\n", new_n);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1623) new_s = assoc_array_ptr_to_shortcut(ptr);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1624) new_parent = new_n->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1625) slot = new_n->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1626) kfree(new_n);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1627) if (!new_parent) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1628) new_s->back_pointer = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1629) new_s->parent_slot = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1630) new_root = ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1631) goto gc_complete;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1632) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1633)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1634) if (assoc_array_ptr_is_shortcut(new_parent)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1635) /* We can discard any preceding shortcut also */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1636) struct assoc_array_shortcut *s =
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1637) assoc_array_ptr_to_shortcut(new_parent);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1638)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1639) pr_devel("excise preceding shortcut\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1640)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1641) new_parent = new_s->back_pointer = s->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1642) slot = new_s->parent_slot = s->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1643) kfree(s);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1644) if (!new_parent) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1645) new_s->back_pointer = NULL;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1646) new_s->parent_slot = 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1647) new_root = ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1648) goto gc_complete;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1649) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1650) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1651)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1652) new_s->back_pointer = new_parent;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1653) new_s->parent_slot = slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1654) new_n = assoc_array_ptr_to_node(new_parent);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1655) new_n->slots[slot] = ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1656) goto ascend_old_tree;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1657) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1658) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1659)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1660) /* Excise any shortcuts we might encounter that point to nodes that
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1661) * only contain leaves.
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1662) */
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1663) ptr = new_n->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1664) if (!ptr)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1665) goto gc_complete;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1666)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1667) if (assoc_array_ptr_is_shortcut(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1668) new_s = assoc_array_ptr_to_shortcut(ptr);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1669) new_parent = new_s->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1670) slot = new_s->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1671)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1672) if (new_n->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1673) struct assoc_array_node *n;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1674)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1675) pr_devel("excise shortcut\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1676) new_n->back_pointer = new_parent;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1677) new_n->parent_slot = slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1678) kfree(new_s);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1679) if (!new_parent) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1680) new_root = assoc_array_node_to_ptr(new_n);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1681) goto gc_complete;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1682) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1683)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1684) n = assoc_array_ptr_to_node(new_parent);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1685) n->slots[slot] = assoc_array_node_to_ptr(new_n);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1686) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1687) } else {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1688) new_parent = ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1689) }
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1690) new_n = assoc_array_ptr_to_node(new_parent);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1691)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1692) ascend_old_tree:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1693) ptr = node->back_pointer;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1694) if (assoc_array_ptr_is_shortcut(ptr)) {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1695) shortcut = assoc_array_ptr_to_shortcut(ptr);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1696) slot = shortcut->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1697) cursor = shortcut->back_pointer;
95389b08d93d5 (David Howells 2014-09-10 22:22:00 +0100 1698) if (!cursor)
95389b08d93d5 (David Howells 2014-09-10 22:22:00 +0100 1699) goto gc_complete;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1700) } else {
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1701) slot = node->parent_slot;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1702) cursor = ptr;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1703) }
95389b08d93d5 (David Howells 2014-09-10 22:22:00 +0100 1704) BUG_ON(!cursor);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1705) node = assoc_array_ptr_to_node(cursor);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1706) slot++;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1707) goto continue_node;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1708)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1709) gc_complete:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1710) edit->set[0].to = new_root;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1711) assoc_array_apply_edit(edit);
27419604f51a9 (David Howells 2014-09-02 13:52:20 +0100 1712) array->nr_leaves_on_tree = nr_leaves_on_tree;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1713) return 0;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1714)
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1715) enomem:
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1716) pr_devel("enomem\n");
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1717) assoc_array_destroy_subtree(new_root, edit->ops);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1718) kfree(edit);
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1719) return -ENOMEM;
3cb989501c268 (David Howells 2013-09-24 10:35:17 +0100 1720) }