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