13cb98950SDavid Howells /* Generic associative array implementation. 23cb98950SDavid Howells * 348c40c26SAlexander Kuleshov * See Documentation/core-api/assoc_array.rst for information. 43cb98950SDavid Howells * 53cb98950SDavid Howells * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. 63cb98950SDavid Howells * Written by David Howells (dhowells@redhat.com) 73cb98950SDavid Howells * 83cb98950SDavid Howells * This program is free software; you can redistribute it and/or 93cb98950SDavid Howells * modify it under the terms of the GNU General Public Licence 103cb98950SDavid Howells * as published by the Free Software Foundation; either version 113cb98950SDavid Howells * 2 of the Licence, or (at your option) any later version. 123cb98950SDavid Howells */ 133cb98950SDavid Howells //#define DEBUG 14990428b8SPranith Kumar #include <linux/rcupdate.h> 153cb98950SDavid Howells #include <linux/slab.h> 16b2a4df20SDavid Howells #include <linux/err.h> 173cb98950SDavid Howells #include <linux/assoc_array_priv.h> 183cb98950SDavid Howells 193cb98950SDavid Howells /* 203cb98950SDavid Howells * Iterate over an associative array. The caller must hold the RCU read lock 213cb98950SDavid Howells * or better. 223cb98950SDavid Howells */ 233cb98950SDavid Howells static int assoc_array_subtree_iterate(const struct assoc_array_ptr *root, 243cb98950SDavid Howells const struct assoc_array_ptr *stop, 253cb98950SDavid Howells int (*iterator)(const void *leaf, 263cb98950SDavid Howells void *iterator_data), 273cb98950SDavid Howells void *iterator_data) 283cb98950SDavid Howells { 293cb98950SDavid Howells const struct assoc_array_shortcut *shortcut; 303cb98950SDavid Howells const struct assoc_array_node *node; 313cb98950SDavid Howells const struct assoc_array_ptr *cursor, *ptr, *parent; 323cb98950SDavid Howells unsigned long has_meta; 333cb98950SDavid Howells int slot, ret; 343cb98950SDavid Howells 353cb98950SDavid Howells cursor = root; 363cb98950SDavid Howells 373cb98950SDavid Howells begin_node: 383cb98950SDavid Howells if (assoc_array_ptr_is_shortcut(cursor)) { 393cb98950SDavid Howells /* Descend through a shortcut */ 403cb98950SDavid Howells shortcut = assoc_array_ptr_to_shortcut(cursor); 413cb98950SDavid Howells smp_read_barrier_depends(); 423cb98950SDavid Howells cursor = ACCESS_ONCE(shortcut->next_node); 433cb98950SDavid Howells } 443cb98950SDavid Howells 453cb98950SDavid Howells node = assoc_array_ptr_to_node(cursor); 463cb98950SDavid Howells smp_read_barrier_depends(); 473cb98950SDavid Howells slot = 0; 483cb98950SDavid Howells 493cb98950SDavid Howells /* We perform two passes of each node. 503cb98950SDavid Howells * 513cb98950SDavid Howells * The first pass does all the leaves in this node. This means we 523cb98950SDavid Howells * don't miss any leaves if the node is split up by insertion whilst 533cb98950SDavid Howells * we're iterating over the branches rooted here (we may, however, see 543cb98950SDavid Howells * some leaves twice). 553cb98950SDavid Howells */ 563cb98950SDavid Howells has_meta = 0; 573cb98950SDavid Howells for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) { 583cb98950SDavid Howells ptr = ACCESS_ONCE(node->slots[slot]); 593cb98950SDavid Howells has_meta |= (unsigned long)ptr; 603cb98950SDavid Howells if (ptr && assoc_array_ptr_is_leaf(ptr)) { 613cb98950SDavid Howells /* We need a barrier between the read of the pointer 623cb98950SDavid Howells * and dereferencing the pointer - but only if we are 633cb98950SDavid Howells * actually going to dereference it. 643cb98950SDavid Howells */ 653cb98950SDavid Howells smp_read_barrier_depends(); 663cb98950SDavid Howells 673cb98950SDavid Howells /* Invoke the callback */ 683cb98950SDavid Howells ret = iterator(assoc_array_ptr_to_leaf(ptr), 693cb98950SDavid Howells iterator_data); 703cb98950SDavid Howells if (ret) 713cb98950SDavid Howells return ret; 723cb98950SDavid Howells } 733cb98950SDavid Howells } 743cb98950SDavid Howells 753cb98950SDavid Howells /* The second pass attends to all the metadata pointers. If we follow 763cb98950SDavid Howells * one of these we may find that we don't come back here, but rather go 773cb98950SDavid Howells * back to a replacement node with the leaves in a different layout. 783cb98950SDavid Howells * 793cb98950SDavid Howells * We are guaranteed to make progress, however, as the slot number for 803cb98950SDavid Howells * a particular portion of the key space cannot change - and we 813cb98950SDavid Howells * continue at the back pointer + 1. 823cb98950SDavid Howells */ 833cb98950SDavid Howells if (!(has_meta & ASSOC_ARRAY_PTR_META_TYPE)) 843cb98950SDavid Howells goto finished_node; 853cb98950SDavid Howells slot = 0; 863cb98950SDavid Howells 873cb98950SDavid Howells continue_node: 883cb98950SDavid Howells node = assoc_array_ptr_to_node(cursor); 893cb98950SDavid Howells smp_read_barrier_depends(); 903cb98950SDavid Howells 913cb98950SDavid Howells for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) { 923cb98950SDavid Howells ptr = ACCESS_ONCE(node->slots[slot]); 933cb98950SDavid Howells if (assoc_array_ptr_is_meta(ptr)) { 943cb98950SDavid Howells cursor = ptr; 953cb98950SDavid Howells goto begin_node; 963cb98950SDavid Howells } 973cb98950SDavid Howells } 983cb98950SDavid Howells 993cb98950SDavid Howells finished_node: 1003cb98950SDavid Howells /* Move up to the parent (may need to skip back over a shortcut) */ 1013cb98950SDavid Howells parent = ACCESS_ONCE(node->back_pointer); 1023cb98950SDavid Howells slot = node->parent_slot; 1033cb98950SDavid Howells if (parent == stop) 1043cb98950SDavid Howells return 0; 1053cb98950SDavid Howells 1063cb98950SDavid Howells if (assoc_array_ptr_is_shortcut(parent)) { 1073cb98950SDavid Howells shortcut = assoc_array_ptr_to_shortcut(parent); 1083cb98950SDavid Howells smp_read_barrier_depends(); 1093cb98950SDavid Howells cursor = parent; 1103cb98950SDavid Howells parent = ACCESS_ONCE(shortcut->back_pointer); 1113cb98950SDavid Howells slot = shortcut->parent_slot; 1123cb98950SDavid Howells if (parent == stop) 1133cb98950SDavid Howells return 0; 1143cb98950SDavid Howells } 1153cb98950SDavid Howells 1163cb98950SDavid Howells /* Ascend to next slot in parent node */ 1173cb98950SDavid Howells cursor = parent; 1183cb98950SDavid Howells slot++; 1193cb98950SDavid Howells goto continue_node; 1203cb98950SDavid Howells } 1213cb98950SDavid Howells 1223cb98950SDavid Howells /** 1233cb98950SDavid Howells * assoc_array_iterate - Pass all objects in the array to a callback 1243cb98950SDavid Howells * @array: The array to iterate over. 1253cb98950SDavid Howells * @iterator: The callback function. 1263cb98950SDavid Howells * @iterator_data: Private data for the callback function. 1273cb98950SDavid Howells * 1283cb98950SDavid Howells * Iterate over all the objects in an associative array. Each one will be 1293cb98950SDavid Howells * presented to the iterator function. 1303cb98950SDavid Howells * 1313cb98950SDavid Howells * If the array is being modified concurrently with the iteration then it is 1323cb98950SDavid Howells * possible that some objects in the array will be passed to the iterator 1333cb98950SDavid Howells * callback more than once - though every object should be passed at least 1343cb98950SDavid Howells * once. If this is undesirable then the caller must lock against modification 1353cb98950SDavid Howells * for the duration of this function. 1363cb98950SDavid Howells * 1373cb98950SDavid Howells * The function will return 0 if no objects were in the array or else it will 1383cb98950SDavid Howells * return the result of the last iterator function called. Iteration stops 1393cb98950SDavid Howells * immediately if any call to the iteration function results in a non-zero 1403cb98950SDavid Howells * return. 1413cb98950SDavid Howells * 1423cb98950SDavid Howells * The caller should hold the RCU read lock or better if concurrent 1433cb98950SDavid Howells * modification is possible. 1443cb98950SDavid Howells */ 1453cb98950SDavid Howells int assoc_array_iterate(const struct assoc_array *array, 1463cb98950SDavid Howells int (*iterator)(const void *object, 1473cb98950SDavid Howells void *iterator_data), 1483cb98950SDavid Howells void *iterator_data) 1493cb98950SDavid Howells { 1503cb98950SDavid Howells struct assoc_array_ptr *root = ACCESS_ONCE(array->root); 1513cb98950SDavid Howells 1523cb98950SDavid Howells if (!root) 1533cb98950SDavid Howells return 0; 1543cb98950SDavid Howells return assoc_array_subtree_iterate(root, NULL, iterator, iterator_data); 1553cb98950SDavid Howells } 1563cb98950SDavid Howells 1573cb98950SDavid Howells enum assoc_array_walk_status { 1583cb98950SDavid Howells assoc_array_walk_tree_empty, 1593cb98950SDavid Howells assoc_array_walk_found_terminal_node, 1603cb98950SDavid Howells assoc_array_walk_found_wrong_shortcut, 16130b02c4bSStephen Hemminger }; 1623cb98950SDavid Howells 1633cb98950SDavid Howells struct assoc_array_walk_result { 1643cb98950SDavid Howells struct { 1653cb98950SDavid Howells struct assoc_array_node *node; /* Node in which leaf might be found */ 1663cb98950SDavid Howells int level; 1673cb98950SDavid Howells int slot; 1683cb98950SDavid Howells } terminal_node; 1693cb98950SDavid Howells struct { 1703cb98950SDavid Howells struct assoc_array_shortcut *shortcut; 1713cb98950SDavid Howells int level; 1723cb98950SDavid Howells int sc_level; 1733cb98950SDavid Howells unsigned long sc_segments; 1743cb98950SDavid Howells unsigned long dissimilarity; 1753cb98950SDavid Howells } wrong_shortcut; 1763cb98950SDavid Howells }; 1773cb98950SDavid Howells 1783cb98950SDavid Howells /* 1793cb98950SDavid Howells * Navigate through the internal tree looking for the closest node to the key. 1803cb98950SDavid Howells */ 1813cb98950SDavid Howells static enum assoc_array_walk_status 1823cb98950SDavid Howells assoc_array_walk(const struct assoc_array *array, 1833cb98950SDavid Howells const struct assoc_array_ops *ops, 1843cb98950SDavid Howells const void *index_key, 1853cb98950SDavid Howells struct assoc_array_walk_result *result) 1863cb98950SDavid Howells { 1873cb98950SDavid Howells struct assoc_array_shortcut *shortcut; 1883cb98950SDavid Howells struct assoc_array_node *node; 1893cb98950SDavid Howells struct assoc_array_ptr *cursor, *ptr; 1903cb98950SDavid Howells unsigned long sc_segments, dissimilarity; 1913cb98950SDavid Howells unsigned long segments; 1923cb98950SDavid Howells int level, sc_level, next_sc_level; 1933cb98950SDavid Howells int slot; 1943cb98950SDavid Howells 1953cb98950SDavid Howells pr_devel("-->%s()\n", __func__); 1963cb98950SDavid Howells 1973cb98950SDavid Howells cursor = ACCESS_ONCE(array->root); 1983cb98950SDavid Howells if (!cursor) 1993cb98950SDavid Howells return assoc_array_walk_tree_empty; 2003cb98950SDavid Howells 2013cb98950SDavid Howells level = 0; 2023cb98950SDavid Howells 2033cb98950SDavid Howells /* Use segments from the key for the new leaf to navigate through the 2043cb98950SDavid Howells * internal tree, skipping through nodes and shortcuts that are on 2053cb98950SDavid Howells * route to the destination. Eventually we'll come to a slot that is 2063cb98950SDavid Howells * either empty or contains a leaf at which point we've found a node in 2073cb98950SDavid Howells * which the leaf we're looking for might be found or into which it 2083cb98950SDavid Howells * should be inserted. 2093cb98950SDavid Howells */ 2103cb98950SDavid Howells jumped: 2113cb98950SDavid Howells segments = ops->get_key_chunk(index_key, level); 2123cb98950SDavid Howells pr_devel("segments[%d]: %lx\n", level, segments); 2133cb98950SDavid Howells 2143cb98950SDavid Howells if (assoc_array_ptr_is_shortcut(cursor)) 2153cb98950SDavid Howells goto follow_shortcut; 2163cb98950SDavid Howells 2173cb98950SDavid Howells consider_node: 2183cb98950SDavid Howells node = assoc_array_ptr_to_node(cursor); 2193cb98950SDavid Howells smp_read_barrier_depends(); 2203cb98950SDavid Howells 2213cb98950SDavid Howells slot = segments >> (level & ASSOC_ARRAY_KEY_CHUNK_MASK); 2223cb98950SDavid Howells slot &= ASSOC_ARRAY_FAN_MASK; 2233cb98950SDavid Howells ptr = ACCESS_ONCE(node->slots[slot]); 2243cb98950SDavid Howells 2253cb98950SDavid Howells pr_devel("consider slot %x [ix=%d type=%lu]\n", 2263cb98950SDavid Howells slot, level, (unsigned long)ptr & 3); 2273cb98950SDavid Howells 2283cb98950SDavid Howells if (!assoc_array_ptr_is_meta(ptr)) { 2293cb98950SDavid Howells /* The node doesn't have a node/shortcut pointer in the slot 2303cb98950SDavid Howells * corresponding to the index key that we have to follow. 2313cb98950SDavid Howells */ 2323cb98950SDavid Howells result->terminal_node.node = node; 2333cb98950SDavid Howells result->terminal_node.level = level; 2343cb98950SDavid Howells result->terminal_node.slot = slot; 2353cb98950SDavid Howells pr_devel("<--%s() = terminal_node\n", __func__); 2363cb98950SDavid Howells return assoc_array_walk_found_terminal_node; 2373cb98950SDavid Howells } 2383cb98950SDavid Howells 2393cb98950SDavid Howells if (assoc_array_ptr_is_node(ptr)) { 2403cb98950SDavid Howells /* There is a pointer to a node in the slot corresponding to 2413cb98950SDavid Howells * this index key segment, so we need to follow it. 2423cb98950SDavid Howells */ 2433cb98950SDavid Howells cursor = ptr; 2443cb98950SDavid Howells level += ASSOC_ARRAY_LEVEL_STEP; 2453cb98950SDavid Howells if ((level & ASSOC_ARRAY_KEY_CHUNK_MASK) != 0) 2463cb98950SDavid Howells goto consider_node; 2473cb98950SDavid Howells goto jumped; 2483cb98950SDavid Howells } 2493cb98950SDavid Howells 2503cb98950SDavid Howells /* There is a shortcut in the slot corresponding to the index key 2513cb98950SDavid Howells * segment. We follow the shortcut if its partial index key matches 2523cb98950SDavid Howells * this leaf's. Otherwise we need to split the shortcut. 2533cb98950SDavid Howells */ 2543cb98950SDavid Howells cursor = ptr; 2553cb98950SDavid Howells follow_shortcut: 2563cb98950SDavid Howells shortcut = assoc_array_ptr_to_shortcut(cursor); 2573cb98950SDavid Howells smp_read_barrier_depends(); 2583cb98950SDavid Howells pr_devel("shortcut to %d\n", shortcut->skip_to_level); 2593cb98950SDavid Howells sc_level = level + ASSOC_ARRAY_LEVEL_STEP; 2603cb98950SDavid Howells BUG_ON(sc_level > shortcut->skip_to_level); 2613cb98950SDavid Howells 2623cb98950SDavid Howells do { 2633cb98950SDavid Howells /* Check the leaf against the shortcut's index key a word at a 2643cb98950SDavid Howells * time, trimming the final word (the shortcut stores the index 2653cb98950SDavid Howells * key completely from the root to the shortcut's target). 2663cb98950SDavid Howells */ 2673cb98950SDavid Howells if ((sc_level & ASSOC_ARRAY_KEY_CHUNK_MASK) == 0) 2683cb98950SDavid Howells segments = ops->get_key_chunk(index_key, sc_level); 2693cb98950SDavid Howells 2703cb98950SDavid Howells sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT]; 2713cb98950SDavid Howells dissimilarity = segments ^ sc_segments; 2723cb98950SDavid Howells 2733cb98950SDavid Howells if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) { 2743cb98950SDavid Howells /* Trim segments that are beyond the shortcut */ 2753cb98950SDavid Howells int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK; 2763cb98950SDavid Howells dissimilarity &= ~(ULONG_MAX << shift); 2773cb98950SDavid Howells next_sc_level = shortcut->skip_to_level; 2783cb98950SDavid Howells } else { 2793cb98950SDavid Howells next_sc_level = sc_level + ASSOC_ARRAY_KEY_CHUNK_SIZE; 2803cb98950SDavid Howells next_sc_level = round_down(next_sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE); 2813cb98950SDavid Howells } 2823cb98950SDavid Howells 2833cb98950SDavid Howells if (dissimilarity != 0) { 2843cb98950SDavid Howells /* This shortcut points elsewhere */ 2853cb98950SDavid Howells result->wrong_shortcut.shortcut = shortcut; 2863cb98950SDavid Howells result->wrong_shortcut.level = level; 2873cb98950SDavid Howells result->wrong_shortcut.sc_level = sc_level; 2883cb98950SDavid Howells result->wrong_shortcut.sc_segments = sc_segments; 2893cb98950SDavid Howells result->wrong_shortcut.dissimilarity = dissimilarity; 2903cb98950SDavid Howells return assoc_array_walk_found_wrong_shortcut; 2913cb98950SDavid Howells } 2923cb98950SDavid Howells 2933cb98950SDavid Howells sc_level = next_sc_level; 2943cb98950SDavid Howells } while (sc_level < shortcut->skip_to_level); 2953cb98950SDavid Howells 2963cb98950SDavid Howells /* The shortcut matches the leaf's index to this point. */ 2973cb98950SDavid Howells cursor = ACCESS_ONCE(shortcut->next_node); 2983cb98950SDavid Howells if (((level ^ sc_level) & ~ASSOC_ARRAY_KEY_CHUNK_MASK) != 0) { 2993cb98950SDavid Howells level = sc_level; 3003cb98950SDavid Howells goto jumped; 3013cb98950SDavid Howells } else { 3023cb98950SDavid Howells level = sc_level; 3033cb98950SDavid Howells goto consider_node; 3043cb98950SDavid Howells } 3053cb98950SDavid Howells } 3063cb98950SDavid Howells 3073cb98950SDavid Howells /** 3083cb98950SDavid Howells * assoc_array_find - Find an object by index key 3093cb98950SDavid Howells * @array: The associative array to search. 3103cb98950SDavid Howells * @ops: The operations to use. 3113cb98950SDavid Howells * @index_key: The key to the object. 3123cb98950SDavid Howells * 3133cb98950SDavid Howells * Find an object in an associative array by walking through the internal tree 3143cb98950SDavid Howells * to the node that should contain the object and then searching the leaves 3153cb98950SDavid Howells * there. NULL is returned if the requested object was not found in the array. 3163cb98950SDavid Howells * 3173cb98950SDavid Howells * The caller must hold the RCU read lock or better. 3183cb98950SDavid Howells */ 3193cb98950SDavid Howells void *assoc_array_find(const struct assoc_array *array, 3203cb98950SDavid Howells const struct assoc_array_ops *ops, 3213cb98950SDavid Howells const void *index_key) 3223cb98950SDavid Howells { 3233cb98950SDavid Howells struct assoc_array_walk_result result; 3243cb98950SDavid Howells const struct assoc_array_node *node; 3253cb98950SDavid Howells const struct assoc_array_ptr *ptr; 3263cb98950SDavid Howells const void *leaf; 3273cb98950SDavid Howells int slot; 3283cb98950SDavid Howells 3293cb98950SDavid Howells if (assoc_array_walk(array, ops, index_key, &result) != 3303cb98950SDavid Howells assoc_array_walk_found_terminal_node) 3313cb98950SDavid Howells return NULL; 3323cb98950SDavid Howells 3333cb98950SDavid Howells node = result.terminal_node.node; 3343cb98950SDavid Howells smp_read_barrier_depends(); 3353cb98950SDavid Howells 3363cb98950SDavid Howells /* If the target key is available to us, it's has to be pointed to by 3373cb98950SDavid Howells * the terminal node. 3383cb98950SDavid Howells */ 3393cb98950SDavid Howells for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) { 3403cb98950SDavid Howells ptr = ACCESS_ONCE(node->slots[slot]); 3413cb98950SDavid Howells if (ptr && assoc_array_ptr_is_leaf(ptr)) { 3423cb98950SDavid Howells /* We need a barrier between the read of the pointer 3433cb98950SDavid Howells * and dereferencing the pointer - but only if we are 3443cb98950SDavid Howells * actually going to dereference it. 3453cb98950SDavid Howells */ 3463cb98950SDavid Howells leaf = assoc_array_ptr_to_leaf(ptr); 3473cb98950SDavid Howells smp_read_barrier_depends(); 3483cb98950SDavid Howells if (ops->compare_object(leaf, index_key)) 3493cb98950SDavid Howells return (void *)leaf; 3503cb98950SDavid Howells } 3513cb98950SDavid Howells } 3523cb98950SDavid Howells 3533cb98950SDavid Howells return NULL; 3543cb98950SDavid Howells } 3553cb98950SDavid Howells 3563cb98950SDavid Howells /* 3573cb98950SDavid Howells * Destructively iterate over an associative array. The caller must prevent 3583cb98950SDavid Howells * other simultaneous accesses. 3593cb98950SDavid Howells */ 3603cb98950SDavid Howells static void assoc_array_destroy_subtree(struct assoc_array_ptr *root, 3613cb98950SDavid Howells const struct assoc_array_ops *ops) 3623cb98950SDavid Howells { 3633cb98950SDavid Howells struct assoc_array_shortcut *shortcut; 3643cb98950SDavid Howells struct assoc_array_node *node; 3653cb98950SDavid Howells struct assoc_array_ptr *cursor, *parent = NULL; 3663cb98950SDavid Howells int slot = -1; 3673cb98950SDavid Howells 3683cb98950SDavid Howells pr_devel("-->%s()\n", __func__); 3693cb98950SDavid Howells 3703cb98950SDavid Howells cursor = root; 3713cb98950SDavid Howells if (!cursor) { 3723cb98950SDavid Howells pr_devel("empty\n"); 3733cb98950SDavid Howells return; 3743cb98950SDavid Howells } 3753cb98950SDavid Howells 3763cb98950SDavid Howells move_to_meta: 3773cb98950SDavid Howells if (assoc_array_ptr_is_shortcut(cursor)) { 3783cb98950SDavid Howells /* Descend through a shortcut */ 3793cb98950SDavid Howells pr_devel("[%d] shortcut\n", slot); 3803cb98950SDavid Howells BUG_ON(!assoc_array_ptr_is_shortcut(cursor)); 3813cb98950SDavid Howells shortcut = assoc_array_ptr_to_shortcut(cursor); 3823cb98950SDavid Howells BUG_ON(shortcut->back_pointer != parent); 3833cb98950SDavid Howells BUG_ON(slot != -1 && shortcut->parent_slot != slot); 3843cb98950SDavid Howells parent = cursor; 3853cb98950SDavid Howells cursor = shortcut->next_node; 3863cb98950SDavid Howells slot = -1; 3873cb98950SDavid Howells BUG_ON(!assoc_array_ptr_is_node(cursor)); 3883cb98950SDavid Howells } 3893cb98950SDavid Howells 3903cb98950SDavid Howells pr_devel("[%d] node\n", slot); 3913cb98950SDavid Howells node = assoc_array_ptr_to_node(cursor); 3923cb98950SDavid Howells BUG_ON(node->back_pointer != parent); 3933cb98950SDavid Howells BUG_ON(slot != -1 && node->parent_slot != slot); 3943cb98950SDavid Howells slot = 0; 3953cb98950SDavid Howells 3963cb98950SDavid Howells continue_node: 3973cb98950SDavid Howells pr_devel("Node %p [back=%p]\n", node, node->back_pointer); 3983cb98950SDavid Howells for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) { 3993cb98950SDavid Howells struct assoc_array_ptr *ptr = node->slots[slot]; 4003cb98950SDavid Howells if (!ptr) 4013cb98950SDavid Howells continue; 4023cb98950SDavid Howells if (assoc_array_ptr_is_meta(ptr)) { 4033cb98950SDavid Howells parent = cursor; 4043cb98950SDavid Howells cursor = ptr; 4053cb98950SDavid Howells goto move_to_meta; 4063cb98950SDavid Howells } 4073cb98950SDavid Howells 4083cb98950SDavid Howells if (ops) { 4093cb98950SDavid Howells pr_devel("[%d] free leaf\n", slot); 4103cb98950SDavid Howells ops->free_object(assoc_array_ptr_to_leaf(ptr)); 4113cb98950SDavid Howells } 4123cb98950SDavid Howells } 4133cb98950SDavid Howells 4143cb98950SDavid Howells parent = node->back_pointer; 4153cb98950SDavid Howells slot = node->parent_slot; 4163cb98950SDavid Howells pr_devel("free node\n"); 4173cb98950SDavid Howells kfree(node); 4183cb98950SDavid Howells if (!parent) 4193cb98950SDavid Howells return; /* Done */ 4203cb98950SDavid Howells 4213cb98950SDavid Howells /* Move back up to the parent (may need to free a shortcut on 4223cb98950SDavid Howells * the way up) */ 4233cb98950SDavid Howells if (assoc_array_ptr_is_shortcut(parent)) { 4243cb98950SDavid Howells shortcut = assoc_array_ptr_to_shortcut(parent); 4253cb98950SDavid Howells BUG_ON(shortcut->next_node != cursor); 4263cb98950SDavid Howells cursor = parent; 4273cb98950SDavid Howells parent = shortcut->back_pointer; 4283cb98950SDavid Howells slot = shortcut->parent_slot; 4293cb98950SDavid Howells pr_devel("free shortcut\n"); 4303cb98950SDavid Howells kfree(shortcut); 4313cb98950SDavid Howells if (!parent) 4323cb98950SDavid Howells return; 4333cb98950SDavid Howells 4343cb98950SDavid Howells BUG_ON(!assoc_array_ptr_is_node(parent)); 4353cb98950SDavid Howells } 4363cb98950SDavid Howells 4373cb98950SDavid Howells /* Ascend to next slot in parent node */ 4383cb98950SDavid Howells pr_devel("ascend to %p[%d]\n", parent, slot); 4393cb98950SDavid Howells cursor = parent; 4403cb98950SDavid Howells node = assoc_array_ptr_to_node(cursor); 4413cb98950SDavid Howells slot++; 4423cb98950SDavid Howells goto continue_node; 4433cb98950SDavid Howells } 4443cb98950SDavid Howells 4453cb98950SDavid Howells /** 4463cb98950SDavid Howells * assoc_array_destroy - Destroy an associative array 4473cb98950SDavid Howells * @array: The array to destroy. 4483cb98950SDavid Howells * @ops: The operations to use. 4493cb98950SDavid Howells * 4503cb98950SDavid Howells * Discard all metadata and free all objects in an associative array. The 4513cb98950SDavid Howells * array will be empty and ready to use again upon completion. This function 4523cb98950SDavid Howells * cannot fail. 4533cb98950SDavid Howells * 4543cb98950SDavid Howells * The caller must prevent all other accesses whilst this takes place as no 4553cb98950SDavid Howells * attempt is made to adjust pointers gracefully to permit RCU readlock-holding 4563cb98950SDavid Howells * accesses to continue. On the other hand, no memory allocation is required. 4573cb98950SDavid Howells */ 4583cb98950SDavid Howells void assoc_array_destroy(struct assoc_array *array, 4593cb98950SDavid Howells const struct assoc_array_ops *ops) 4603cb98950SDavid Howells { 4613cb98950SDavid Howells assoc_array_destroy_subtree(array->root, ops); 4623cb98950SDavid Howells array->root = NULL; 4633cb98950SDavid Howells } 4643cb98950SDavid Howells 4653cb98950SDavid Howells /* 4663cb98950SDavid Howells * Handle insertion into an empty tree. 4673cb98950SDavid Howells */ 4683cb98950SDavid Howells static bool assoc_array_insert_in_empty_tree(struct assoc_array_edit *edit) 4693cb98950SDavid Howells { 4703cb98950SDavid Howells struct assoc_array_node *new_n0; 4713cb98950SDavid Howells 4723cb98950SDavid Howells pr_devel("-->%s()\n", __func__); 4733cb98950SDavid Howells 4743cb98950SDavid Howells new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL); 4753cb98950SDavid Howells if (!new_n0) 4763cb98950SDavid Howells return false; 4773cb98950SDavid Howells 4783cb98950SDavid Howells edit->new_meta[0] = assoc_array_node_to_ptr(new_n0); 4793cb98950SDavid Howells edit->leaf_p = &new_n0->slots[0]; 4803cb98950SDavid Howells edit->adjust_count_on = new_n0; 4813cb98950SDavid Howells edit->set[0].ptr = &edit->array->root; 4823cb98950SDavid Howells edit->set[0].to = assoc_array_node_to_ptr(new_n0); 4833cb98950SDavid Howells 4843cb98950SDavid Howells pr_devel("<--%s() = ok [no root]\n", __func__); 4853cb98950SDavid Howells return true; 4863cb98950SDavid Howells } 4873cb98950SDavid Howells 4883cb98950SDavid Howells /* 4893cb98950SDavid Howells * Handle insertion into a terminal node. 4903cb98950SDavid Howells */ 4913cb98950SDavid Howells static bool assoc_array_insert_into_terminal_node(struct assoc_array_edit *edit, 4923cb98950SDavid Howells const struct assoc_array_ops *ops, 4933cb98950SDavid Howells const void *index_key, 4943cb98950SDavid Howells struct assoc_array_walk_result *result) 4953cb98950SDavid Howells { 4963cb98950SDavid Howells struct assoc_array_shortcut *shortcut, *new_s0; 4973cb98950SDavid Howells struct assoc_array_node *node, *new_n0, *new_n1, *side; 4983cb98950SDavid Howells struct assoc_array_ptr *ptr; 4993cb98950SDavid Howells unsigned long dissimilarity, base_seg, blank; 5003cb98950SDavid Howells size_t keylen; 5013cb98950SDavid Howells bool have_meta; 5023cb98950SDavid Howells int level, diff; 5033cb98950SDavid Howells int slot, next_slot, free_slot, i, j; 5043cb98950SDavid Howells 5053cb98950SDavid Howells node = result->terminal_node.node; 5063cb98950SDavid Howells level = result->terminal_node.level; 5073cb98950SDavid Howells edit->segment_cache[ASSOC_ARRAY_FAN_OUT] = result->terminal_node.slot; 5083cb98950SDavid Howells 5093cb98950SDavid Howells pr_devel("-->%s()\n", __func__); 5103cb98950SDavid Howells 5113cb98950SDavid Howells /* We arrived at a node which doesn't have an onward node or shortcut 5123cb98950SDavid Howells * pointer that we have to follow. This means that (a) the leaf we 5133cb98950SDavid Howells * want must go here (either by insertion or replacement) or (b) we 5143cb98950SDavid Howells * need to split this node and insert in one of the fragments. 5153cb98950SDavid Howells */ 5163cb98950SDavid Howells free_slot = -1; 5173cb98950SDavid Howells 5183cb98950SDavid Howells /* Firstly, we have to check the leaves in this node to see if there's 5193cb98950SDavid Howells * a matching one we should replace in place. 5203cb98950SDavid Howells */ 5213cb98950SDavid Howells for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) { 5223cb98950SDavid Howells ptr = node->slots[i]; 5233cb98950SDavid Howells if (!ptr) { 5243cb98950SDavid Howells free_slot = i; 5253cb98950SDavid Howells continue; 5263cb98950SDavid Howells } 5278d4a2ec1SJerome Marchand if (assoc_array_ptr_is_leaf(ptr) && 5288d4a2ec1SJerome Marchand ops->compare_object(assoc_array_ptr_to_leaf(ptr), 5298d4a2ec1SJerome Marchand index_key)) { 5303cb98950SDavid Howells pr_devel("replace in slot %d\n", i); 5313cb98950SDavid Howells edit->leaf_p = &node->slots[i]; 5323cb98950SDavid Howells edit->dead_leaf = node->slots[i]; 5333cb98950SDavid Howells pr_devel("<--%s() = ok [replace]\n", __func__); 5343cb98950SDavid Howells return true; 5353cb98950SDavid Howells } 5363cb98950SDavid Howells } 5373cb98950SDavid Howells 5383cb98950SDavid Howells /* If there is a free slot in this node then we can just insert the 5393cb98950SDavid Howells * leaf here. 5403cb98950SDavid Howells */ 5413cb98950SDavid Howells if (free_slot >= 0) { 5423cb98950SDavid Howells pr_devel("insert in free slot %d\n", free_slot); 5433cb98950SDavid Howells edit->leaf_p = &node->slots[free_slot]; 5443cb98950SDavid Howells edit->adjust_count_on = node; 5453cb98950SDavid Howells pr_devel("<--%s() = ok [insert]\n", __func__); 5463cb98950SDavid Howells return true; 5473cb98950SDavid Howells } 5483cb98950SDavid Howells 5493cb98950SDavid Howells /* The node has no spare slots - so we're either going to have to split 5503cb98950SDavid Howells * it or insert another node before it. 5513cb98950SDavid Howells * 5523cb98950SDavid Howells * Whatever, we're going to need at least two new nodes - so allocate 5533cb98950SDavid Howells * those now. We may also need a new shortcut, but we deal with that 5543cb98950SDavid Howells * when we need it. 5553cb98950SDavid Howells */ 5563cb98950SDavid Howells new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL); 5573cb98950SDavid Howells if (!new_n0) 5583cb98950SDavid Howells return false; 5593cb98950SDavid Howells edit->new_meta[0] = assoc_array_node_to_ptr(new_n0); 5603cb98950SDavid Howells new_n1 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL); 5613cb98950SDavid Howells if (!new_n1) 5623cb98950SDavid Howells return false; 5633cb98950SDavid Howells edit->new_meta[1] = assoc_array_node_to_ptr(new_n1); 5643cb98950SDavid Howells 5653cb98950SDavid Howells /* We need to find out how similar the leaves are. */ 5663cb98950SDavid Howells pr_devel("no spare slots\n"); 5673cb98950SDavid Howells have_meta = false; 5683cb98950SDavid Howells for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) { 5693cb98950SDavid Howells ptr = node->slots[i]; 5703cb98950SDavid Howells if (assoc_array_ptr_is_meta(ptr)) { 5713cb98950SDavid Howells edit->segment_cache[i] = 0xff; 5723cb98950SDavid Howells have_meta = true; 5733cb98950SDavid Howells continue; 5743cb98950SDavid Howells } 5753cb98950SDavid Howells base_seg = ops->get_object_key_chunk( 5763cb98950SDavid Howells assoc_array_ptr_to_leaf(ptr), level); 5773cb98950SDavid Howells base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK; 5783cb98950SDavid Howells edit->segment_cache[i] = base_seg & ASSOC_ARRAY_FAN_MASK; 5793cb98950SDavid Howells } 5803cb98950SDavid Howells 5813cb98950SDavid Howells if (have_meta) { 5823cb98950SDavid Howells pr_devel("have meta\n"); 5833cb98950SDavid Howells goto split_node; 5843cb98950SDavid Howells } 5853cb98950SDavid Howells 5863cb98950SDavid Howells /* The node contains only leaves */ 5873cb98950SDavid Howells dissimilarity = 0; 5883cb98950SDavid Howells base_seg = edit->segment_cache[0]; 5893cb98950SDavid Howells for (i = 1; i < ASSOC_ARRAY_FAN_OUT; i++) 5903cb98950SDavid Howells dissimilarity |= edit->segment_cache[i] ^ base_seg; 5913cb98950SDavid Howells 5923cb98950SDavid Howells pr_devel("only leaves; dissimilarity=%lx\n", dissimilarity); 5933cb98950SDavid Howells 5943cb98950SDavid Howells if ((dissimilarity & ASSOC_ARRAY_FAN_MASK) == 0) { 5953cb98950SDavid Howells /* The old leaves all cluster in the same slot. We will need 5963cb98950SDavid Howells * to insert a shortcut if the new node wants to cluster with them. 5973cb98950SDavid Howells */ 5983cb98950SDavid Howells if ((edit->segment_cache[ASSOC_ARRAY_FAN_OUT] ^ base_seg) == 0) 5993cb98950SDavid Howells goto all_leaves_cluster_together; 6003cb98950SDavid Howells 601*ea678998SDavid Howells /* Otherwise all the old leaves cluster in the same slot, but 602*ea678998SDavid Howells * the new leaf wants to go into a different slot - so we 603*ea678998SDavid Howells * create a new node (n0) to hold the new leaf and a pointer to 604*ea678998SDavid Howells * a new node (n1) holding all the old leaves. 605*ea678998SDavid Howells * 606*ea678998SDavid Howells * This can be done by falling through to the node splitting 607*ea678998SDavid Howells * path. 6083cb98950SDavid Howells */ 609*ea678998SDavid Howells pr_devel("present leaves cluster but not new leaf\n"); 6103cb98950SDavid Howells } 6113cb98950SDavid Howells 6123cb98950SDavid Howells split_node: 6133cb98950SDavid Howells pr_devel("split node\n"); 6143cb98950SDavid Howells 615*ea678998SDavid Howells /* We need to split the current node. The node must contain anything 616*ea678998SDavid Howells * from a single leaf (in the one leaf case, this leaf will cluster 617*ea678998SDavid Howells * with the new leaf) and the rest meta-pointers, to all leaves, some 618*ea678998SDavid Howells * of which may cluster. 619*ea678998SDavid Howells * 620*ea678998SDavid Howells * It won't contain the case in which all the current leaves plus the 621*ea678998SDavid Howells * new leaves want to cluster in the same slot. 6223cb98950SDavid Howells * 6233cb98950SDavid Howells * We need to expel at least two leaves out of a set consisting of the 624*ea678998SDavid Howells * leaves in the node and the new leaf. The current meta pointers can 625*ea678998SDavid Howells * just be copied as they shouldn't cluster with any of the leaves. 6263cb98950SDavid Howells * 6273cb98950SDavid Howells * We need a new node (n0) to replace the current one and a new node to 6283cb98950SDavid Howells * take the expelled nodes (n1). 6293cb98950SDavid Howells */ 6303cb98950SDavid Howells edit->set[0].to = assoc_array_node_to_ptr(new_n0); 6313cb98950SDavid Howells new_n0->back_pointer = node->back_pointer; 6323cb98950SDavid Howells new_n0->parent_slot = node->parent_slot; 6333cb98950SDavid Howells new_n1->back_pointer = assoc_array_node_to_ptr(new_n0); 6343cb98950SDavid Howells new_n1->parent_slot = -1; /* Need to calculate this */ 6353cb98950SDavid Howells 6363cb98950SDavid Howells do_split_node: 6373cb98950SDavid Howells pr_devel("do_split_node\n"); 6383cb98950SDavid Howells 6393cb98950SDavid Howells new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch; 6403cb98950SDavid Howells new_n1->nr_leaves_on_branch = 0; 6413cb98950SDavid Howells 6423cb98950SDavid Howells /* Begin by finding two matching leaves. There have to be at least two 6433cb98950SDavid Howells * that match - even if there are meta pointers - because any leaf that 6443cb98950SDavid Howells * would match a slot with a meta pointer in it must be somewhere 6453cb98950SDavid Howells * behind that meta pointer and cannot be here. Further, given N 6463cb98950SDavid Howells * remaining leaf slots, we now have N+1 leaves to go in them. 6473cb98950SDavid Howells */ 6483cb98950SDavid Howells for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) { 6493cb98950SDavid Howells slot = edit->segment_cache[i]; 6503cb98950SDavid Howells if (slot != 0xff) 6513cb98950SDavid Howells for (j = i + 1; j < ASSOC_ARRAY_FAN_OUT + 1; j++) 6523cb98950SDavid Howells if (edit->segment_cache[j] == slot) 6533cb98950SDavid Howells goto found_slot_for_multiple_occupancy; 6543cb98950SDavid Howells } 6553cb98950SDavid Howells found_slot_for_multiple_occupancy: 6563cb98950SDavid Howells pr_devel("same slot: %x %x [%02x]\n", i, j, slot); 6573cb98950SDavid Howells BUG_ON(i >= ASSOC_ARRAY_FAN_OUT); 6583cb98950SDavid Howells BUG_ON(j >= ASSOC_ARRAY_FAN_OUT + 1); 6593cb98950SDavid Howells BUG_ON(slot >= ASSOC_ARRAY_FAN_OUT); 6603cb98950SDavid Howells 6613cb98950SDavid Howells new_n1->parent_slot = slot; 6623cb98950SDavid Howells 6633cb98950SDavid Howells /* Metadata pointers cannot change slot */ 6643cb98950SDavid Howells for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) 6653cb98950SDavid Howells if (assoc_array_ptr_is_meta(node->slots[i])) 6663cb98950SDavid Howells new_n0->slots[i] = node->slots[i]; 6673cb98950SDavid Howells else 6683cb98950SDavid Howells new_n0->slots[i] = NULL; 6693cb98950SDavid Howells BUG_ON(new_n0->slots[slot] != NULL); 6703cb98950SDavid Howells new_n0->slots[slot] = assoc_array_node_to_ptr(new_n1); 6713cb98950SDavid Howells 6723cb98950SDavid Howells /* Filter the leaf pointers between the new nodes */ 6733cb98950SDavid Howells free_slot = -1; 6743cb98950SDavid Howells next_slot = 0; 6753cb98950SDavid Howells for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) { 6763cb98950SDavid Howells if (assoc_array_ptr_is_meta(node->slots[i])) 6773cb98950SDavid Howells continue; 6783cb98950SDavid Howells if (edit->segment_cache[i] == slot) { 6793cb98950SDavid Howells new_n1->slots[next_slot++] = node->slots[i]; 6803cb98950SDavid Howells new_n1->nr_leaves_on_branch++; 6813cb98950SDavid Howells } else { 6823cb98950SDavid Howells do { 6833cb98950SDavid Howells free_slot++; 6843cb98950SDavid Howells } while (new_n0->slots[free_slot] != NULL); 6853cb98950SDavid Howells new_n0->slots[free_slot] = node->slots[i]; 6863cb98950SDavid Howells } 6873cb98950SDavid Howells } 6883cb98950SDavid Howells 6893cb98950SDavid Howells pr_devel("filtered: f=%x n=%x\n", free_slot, next_slot); 6903cb98950SDavid Howells 6913cb98950SDavid Howells if (edit->segment_cache[ASSOC_ARRAY_FAN_OUT] != slot) { 6923cb98950SDavid Howells do { 6933cb98950SDavid Howells free_slot++; 6943cb98950SDavid Howells } while (new_n0->slots[free_slot] != NULL); 6953cb98950SDavid Howells edit->leaf_p = &new_n0->slots[free_slot]; 6963cb98950SDavid Howells edit->adjust_count_on = new_n0; 6973cb98950SDavid Howells } else { 6983cb98950SDavid Howells edit->leaf_p = &new_n1->slots[next_slot++]; 6993cb98950SDavid Howells edit->adjust_count_on = new_n1; 7003cb98950SDavid Howells } 7013cb98950SDavid Howells 7023cb98950SDavid Howells BUG_ON(next_slot <= 1); 7033cb98950SDavid Howells 7043cb98950SDavid Howells edit->set_backpointers_to = assoc_array_node_to_ptr(new_n0); 7053cb98950SDavid Howells for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) { 7063cb98950SDavid Howells if (edit->segment_cache[i] == 0xff) { 7073cb98950SDavid Howells ptr = node->slots[i]; 7083cb98950SDavid Howells BUG_ON(assoc_array_ptr_is_leaf(ptr)); 7093cb98950SDavid Howells if (assoc_array_ptr_is_node(ptr)) { 7103cb98950SDavid Howells side = assoc_array_ptr_to_node(ptr); 7113cb98950SDavid Howells edit->set_backpointers[i] = &side->back_pointer; 7123cb98950SDavid Howells } else { 7133cb98950SDavid Howells shortcut = assoc_array_ptr_to_shortcut(ptr); 7143cb98950SDavid Howells edit->set_backpointers[i] = &shortcut->back_pointer; 7153cb98950SDavid Howells } 7163cb98950SDavid Howells } 7173cb98950SDavid Howells } 7183cb98950SDavid Howells 7193cb98950SDavid Howells ptr = node->back_pointer; 7203cb98950SDavid Howells if (!ptr) 7213cb98950SDavid Howells edit->set[0].ptr = &edit->array->root; 7223cb98950SDavid Howells else if (assoc_array_ptr_is_node(ptr)) 7233cb98950SDavid Howells edit->set[0].ptr = &assoc_array_ptr_to_node(ptr)->slots[node->parent_slot]; 7243cb98950SDavid Howells else 7253cb98950SDavid Howells edit->set[0].ptr = &assoc_array_ptr_to_shortcut(ptr)->next_node; 7263cb98950SDavid Howells edit->excised_meta[0] = assoc_array_node_to_ptr(node); 7273cb98950SDavid Howells pr_devel("<--%s() = ok [split node]\n", __func__); 7283cb98950SDavid Howells return true; 7293cb98950SDavid Howells 7303cb98950SDavid Howells all_leaves_cluster_together: 7313cb98950SDavid Howells /* All the leaves, new and old, want to cluster together in this node 7323cb98950SDavid Howells * in the same slot, so we have to replace this node with a shortcut to 7333cb98950SDavid Howells * skip over the identical parts of the key and then place a pair of 7343cb98950SDavid Howells * nodes, one inside the other, at the end of the shortcut and 7353cb98950SDavid Howells * distribute the keys between them. 7363cb98950SDavid Howells * 7373cb98950SDavid Howells * Firstly we need to work out where the leaves start diverging as a 7383cb98950SDavid Howells * bit position into their keys so that we know how big the shortcut 7393cb98950SDavid Howells * needs to be. 7403cb98950SDavid Howells * 7413cb98950SDavid Howells * We only need to make a single pass of N of the N+1 leaves because if 7423cb98950SDavid Howells * any keys differ between themselves at bit X then at least one of 7433cb98950SDavid Howells * them must also differ with the base key at bit X or before. 7443cb98950SDavid Howells */ 7453cb98950SDavid Howells pr_devel("all leaves cluster together\n"); 7463cb98950SDavid Howells diff = INT_MAX; 7473cb98950SDavid Howells for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) { 74823fd78d7SDavid Howells int x = ops->diff_objects(assoc_array_ptr_to_leaf(node->slots[i]), 74923fd78d7SDavid Howells index_key); 7503cb98950SDavid Howells if (x < diff) { 7513cb98950SDavid Howells BUG_ON(x < 0); 7523cb98950SDavid Howells diff = x; 7533cb98950SDavid Howells } 7543cb98950SDavid Howells } 7553cb98950SDavid Howells BUG_ON(diff == INT_MAX); 7563cb98950SDavid Howells BUG_ON(diff < level + ASSOC_ARRAY_LEVEL_STEP); 7573cb98950SDavid Howells 7583cb98950SDavid Howells keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE); 7593cb98950SDavid Howells keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT; 7603cb98950SDavid Howells 7613cb98950SDavid Howells new_s0 = kzalloc(sizeof(struct assoc_array_shortcut) + 7623cb98950SDavid Howells keylen * sizeof(unsigned long), GFP_KERNEL); 7633cb98950SDavid Howells if (!new_s0) 7643cb98950SDavid Howells return false; 7653cb98950SDavid Howells edit->new_meta[2] = assoc_array_shortcut_to_ptr(new_s0); 7663cb98950SDavid Howells 7673cb98950SDavid Howells edit->set[0].to = assoc_array_shortcut_to_ptr(new_s0); 7683cb98950SDavid Howells new_s0->back_pointer = node->back_pointer; 7693cb98950SDavid Howells new_s0->parent_slot = node->parent_slot; 7703cb98950SDavid Howells new_s0->next_node = assoc_array_node_to_ptr(new_n0); 7713cb98950SDavid Howells new_n0->back_pointer = assoc_array_shortcut_to_ptr(new_s0); 7723cb98950SDavid Howells new_n0->parent_slot = 0; 7733cb98950SDavid Howells new_n1->back_pointer = assoc_array_node_to_ptr(new_n0); 7743cb98950SDavid Howells new_n1->parent_slot = -1; /* Need to calculate this */ 7753cb98950SDavid Howells 7763cb98950SDavid Howells new_s0->skip_to_level = level = diff & ~ASSOC_ARRAY_LEVEL_STEP_MASK; 7773cb98950SDavid Howells pr_devel("skip_to_level = %d [diff %d]\n", level, diff); 7783cb98950SDavid Howells BUG_ON(level <= 0); 7793cb98950SDavid Howells 7803cb98950SDavid Howells for (i = 0; i < keylen; i++) 7813cb98950SDavid Howells new_s0->index_key[i] = 7823cb98950SDavid Howells ops->get_key_chunk(index_key, i * ASSOC_ARRAY_KEY_CHUNK_SIZE); 7833cb98950SDavid Howells 7843cb98950SDavid Howells blank = ULONG_MAX << (level & ASSOC_ARRAY_KEY_CHUNK_MASK); 7853cb98950SDavid Howells pr_devel("blank off [%zu] %d: %lx\n", keylen - 1, level, blank); 7863cb98950SDavid Howells new_s0->index_key[keylen - 1] &= ~blank; 7873cb98950SDavid Howells 7883cb98950SDavid Howells /* This now reduces to a node splitting exercise for which we'll need 7893cb98950SDavid Howells * to regenerate the disparity table. 7903cb98950SDavid Howells */ 7913cb98950SDavid Howells for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) { 7923cb98950SDavid Howells ptr = node->slots[i]; 7933cb98950SDavid Howells base_seg = ops->get_object_key_chunk(assoc_array_ptr_to_leaf(ptr), 7943cb98950SDavid Howells level); 7953cb98950SDavid Howells base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK; 7963cb98950SDavid Howells edit->segment_cache[i] = base_seg & ASSOC_ARRAY_FAN_MASK; 7973cb98950SDavid Howells } 7983cb98950SDavid Howells 7993cb98950SDavid Howells base_seg = ops->get_key_chunk(index_key, level); 8003cb98950SDavid Howells base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK; 8013cb98950SDavid Howells edit->segment_cache[ASSOC_ARRAY_FAN_OUT] = base_seg & ASSOC_ARRAY_FAN_MASK; 8023cb98950SDavid Howells goto do_split_node; 8033cb98950SDavid Howells } 8043cb98950SDavid Howells 8053cb98950SDavid Howells /* 8063cb98950SDavid Howells * Handle insertion into the middle of a shortcut. 8073cb98950SDavid Howells */ 8083cb98950SDavid Howells static bool assoc_array_insert_mid_shortcut(struct assoc_array_edit *edit, 8093cb98950SDavid Howells const struct assoc_array_ops *ops, 8103cb98950SDavid Howells struct assoc_array_walk_result *result) 8113cb98950SDavid Howells { 8123cb98950SDavid Howells struct assoc_array_shortcut *shortcut, *new_s0, *new_s1; 8133cb98950SDavid Howells struct assoc_array_node *node, *new_n0, *side; 8143cb98950SDavid Howells unsigned long sc_segments, dissimilarity, blank; 8153cb98950SDavid Howells size_t keylen; 8163cb98950SDavid Howells int level, sc_level, diff; 8173cb98950SDavid Howells int sc_slot; 8183cb98950SDavid Howells 8193cb98950SDavid Howells shortcut = result->wrong_shortcut.shortcut; 8203cb98950SDavid Howells level = result->wrong_shortcut.level; 8213cb98950SDavid Howells sc_level = result->wrong_shortcut.sc_level; 8223cb98950SDavid Howells sc_segments = result->wrong_shortcut.sc_segments; 8233cb98950SDavid Howells dissimilarity = result->wrong_shortcut.dissimilarity; 8243cb98950SDavid Howells 8253cb98950SDavid Howells pr_devel("-->%s(ix=%d dis=%lx scix=%d)\n", 8263cb98950SDavid Howells __func__, level, dissimilarity, sc_level); 8273cb98950SDavid Howells 8283cb98950SDavid Howells /* We need to split a shortcut and insert a node between the two 8293cb98950SDavid Howells * pieces. Zero-length pieces will be dispensed with entirely. 8303cb98950SDavid Howells * 8313cb98950SDavid Howells * First of all, we need to find out in which level the first 8323cb98950SDavid Howells * difference was. 8333cb98950SDavid Howells */ 8343cb98950SDavid Howells diff = __ffs(dissimilarity); 8353cb98950SDavid Howells diff &= ~ASSOC_ARRAY_LEVEL_STEP_MASK; 8363cb98950SDavid Howells diff += sc_level & ~ASSOC_ARRAY_KEY_CHUNK_MASK; 8373cb98950SDavid Howells pr_devel("diff=%d\n", diff); 8383cb98950SDavid Howells 8393cb98950SDavid Howells if (!shortcut->back_pointer) { 8403cb98950SDavid Howells edit->set[0].ptr = &edit->array->root; 8413cb98950SDavid Howells } else if (assoc_array_ptr_is_node(shortcut->back_pointer)) { 8423cb98950SDavid Howells node = assoc_array_ptr_to_node(shortcut->back_pointer); 8433cb98950SDavid Howells edit->set[0].ptr = &node->slots[shortcut->parent_slot]; 8443cb98950SDavid Howells } else { 8453cb98950SDavid Howells BUG(); 8463cb98950SDavid Howells } 8473cb98950SDavid Howells 8483cb98950SDavid Howells edit->excised_meta[0] = assoc_array_shortcut_to_ptr(shortcut); 8493cb98950SDavid Howells 8503cb98950SDavid Howells /* Create a new node now since we're going to need it anyway */ 8513cb98950SDavid Howells new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL); 8523cb98950SDavid Howells if (!new_n0) 8533cb98950SDavid Howells return false; 8543cb98950SDavid Howells edit->new_meta[0] = assoc_array_node_to_ptr(new_n0); 8553cb98950SDavid Howells edit->adjust_count_on = new_n0; 8563cb98950SDavid Howells 8573cb98950SDavid Howells /* Insert a new shortcut before the new node if this segment isn't of 8583cb98950SDavid Howells * zero length - otherwise we just connect the new node directly to the 8593cb98950SDavid Howells * parent. 8603cb98950SDavid Howells */ 8613cb98950SDavid Howells level += ASSOC_ARRAY_LEVEL_STEP; 8623cb98950SDavid Howells if (diff > level) { 8633cb98950SDavid Howells pr_devel("pre-shortcut %d...%d\n", level, diff); 8643cb98950SDavid Howells keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE); 8653cb98950SDavid Howells keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT; 8663cb98950SDavid Howells 8673cb98950SDavid Howells new_s0 = kzalloc(sizeof(struct assoc_array_shortcut) + 8683cb98950SDavid Howells keylen * sizeof(unsigned long), GFP_KERNEL); 8693cb98950SDavid Howells if (!new_s0) 8703cb98950SDavid Howells return false; 8713cb98950SDavid Howells edit->new_meta[1] = assoc_array_shortcut_to_ptr(new_s0); 8723cb98950SDavid Howells edit->set[0].to = assoc_array_shortcut_to_ptr(new_s0); 8733cb98950SDavid Howells new_s0->back_pointer = shortcut->back_pointer; 8743cb98950SDavid Howells new_s0->parent_slot = shortcut->parent_slot; 8753cb98950SDavid Howells new_s0->next_node = assoc_array_node_to_ptr(new_n0); 8763cb98950SDavid Howells new_s0->skip_to_level = diff; 8773cb98950SDavid Howells 8783cb98950SDavid Howells new_n0->back_pointer = assoc_array_shortcut_to_ptr(new_s0); 8793cb98950SDavid Howells new_n0->parent_slot = 0; 8803cb98950SDavid Howells 8813cb98950SDavid Howells memcpy(new_s0->index_key, shortcut->index_key, 8823cb98950SDavid Howells keylen * sizeof(unsigned long)); 8833cb98950SDavid Howells 8843cb98950SDavid Howells blank = ULONG_MAX << (diff & ASSOC_ARRAY_KEY_CHUNK_MASK); 8853cb98950SDavid Howells pr_devel("blank off [%zu] %d: %lx\n", keylen - 1, diff, blank); 8863cb98950SDavid Howells new_s0->index_key[keylen - 1] &= ~blank; 8873cb98950SDavid Howells } else { 8883cb98950SDavid Howells pr_devel("no pre-shortcut\n"); 8893cb98950SDavid Howells edit->set[0].to = assoc_array_node_to_ptr(new_n0); 8903cb98950SDavid Howells new_n0->back_pointer = shortcut->back_pointer; 8913cb98950SDavid Howells new_n0->parent_slot = shortcut->parent_slot; 8923cb98950SDavid Howells } 8933cb98950SDavid Howells 8943cb98950SDavid Howells side = assoc_array_ptr_to_node(shortcut->next_node); 8953cb98950SDavid Howells new_n0->nr_leaves_on_branch = side->nr_leaves_on_branch; 8963cb98950SDavid Howells 8973cb98950SDavid Howells /* We need to know which slot in the new node is going to take a 8983cb98950SDavid Howells * metadata pointer. 8993cb98950SDavid Howells */ 9003cb98950SDavid Howells sc_slot = sc_segments >> (diff & ASSOC_ARRAY_KEY_CHUNK_MASK); 9013cb98950SDavid Howells sc_slot &= ASSOC_ARRAY_FAN_MASK; 9023cb98950SDavid Howells 9033cb98950SDavid Howells pr_devel("new slot %lx >> %d -> %d\n", 9043cb98950SDavid Howells sc_segments, diff & ASSOC_ARRAY_KEY_CHUNK_MASK, sc_slot); 9053cb98950SDavid Howells 9063cb98950SDavid Howells /* Determine whether we need to follow the new node with a replacement 9073cb98950SDavid Howells * for the current shortcut. We could in theory reuse the current 9083cb98950SDavid Howells * shortcut if its parent slot number doesn't change - but that's a 9093cb98950SDavid Howells * 1-in-16 chance so not worth expending the code upon. 9103cb98950SDavid Howells */ 9113cb98950SDavid Howells level = diff + ASSOC_ARRAY_LEVEL_STEP; 9123cb98950SDavid Howells if (level < shortcut->skip_to_level) { 9133cb98950SDavid Howells pr_devel("post-shortcut %d...%d\n", level, shortcut->skip_to_level); 9143cb98950SDavid Howells keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE); 9153cb98950SDavid Howells keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT; 9163cb98950SDavid Howells 9173cb98950SDavid Howells new_s1 = kzalloc(sizeof(struct assoc_array_shortcut) + 9183cb98950SDavid Howells keylen * sizeof(unsigned long), GFP_KERNEL); 9193cb98950SDavid Howells if (!new_s1) 9203cb98950SDavid Howells return false; 9213cb98950SDavid Howells edit->new_meta[2] = assoc_array_shortcut_to_ptr(new_s1); 9223cb98950SDavid Howells 9233cb98950SDavid Howells new_s1->back_pointer = assoc_array_node_to_ptr(new_n0); 9243cb98950SDavid Howells new_s1->parent_slot = sc_slot; 9253cb98950SDavid Howells new_s1->next_node = shortcut->next_node; 9263cb98950SDavid Howells new_s1->skip_to_level = shortcut->skip_to_level; 9273cb98950SDavid Howells 9283cb98950SDavid Howells new_n0->slots[sc_slot] = assoc_array_shortcut_to_ptr(new_s1); 9293cb98950SDavid Howells 9303cb98950SDavid Howells memcpy(new_s1->index_key, shortcut->index_key, 9313cb98950SDavid Howells keylen * sizeof(unsigned long)); 9323cb98950SDavid Howells 9333cb98950SDavid Howells edit->set[1].ptr = &side->back_pointer; 9343cb98950SDavid Howells edit->set[1].to = assoc_array_shortcut_to_ptr(new_s1); 9353cb98950SDavid Howells } else { 9363cb98950SDavid Howells pr_devel("no post-shortcut\n"); 9373cb98950SDavid Howells 9383cb98950SDavid Howells /* We don't have to replace the pointed-to node as long as we 9393cb98950SDavid Howells * use memory barriers to make sure the parent slot number is 9403cb98950SDavid Howells * changed before the back pointer (the parent slot number is 9413cb98950SDavid Howells * irrelevant to the old parent shortcut). 9423cb98950SDavid Howells */ 9433cb98950SDavid Howells new_n0->slots[sc_slot] = shortcut->next_node; 9443cb98950SDavid Howells edit->set_parent_slot[0].p = &side->parent_slot; 9453cb98950SDavid Howells edit->set_parent_slot[0].to = sc_slot; 9463cb98950SDavid Howells edit->set[1].ptr = &side->back_pointer; 9473cb98950SDavid Howells edit->set[1].to = assoc_array_node_to_ptr(new_n0); 9483cb98950SDavid Howells } 9493cb98950SDavid Howells 9503cb98950SDavid Howells /* Install the new leaf in a spare slot in the new node. */ 9513cb98950SDavid Howells if (sc_slot == 0) 9523cb98950SDavid Howells edit->leaf_p = &new_n0->slots[1]; 9533cb98950SDavid Howells else 9543cb98950SDavid Howells edit->leaf_p = &new_n0->slots[0]; 9553cb98950SDavid Howells 9563cb98950SDavid Howells pr_devel("<--%s() = ok [split shortcut]\n", __func__); 9573cb98950SDavid Howells return edit; 9583cb98950SDavid Howells } 9593cb98950SDavid Howells 9603cb98950SDavid Howells /** 9613cb98950SDavid Howells * assoc_array_insert - Script insertion of an object into an associative array 9623cb98950SDavid Howells * @array: The array to insert into. 9633cb98950SDavid Howells * @ops: The operations to use. 9643cb98950SDavid Howells * @index_key: The key to insert at. 9653cb98950SDavid Howells * @object: The object to insert. 9663cb98950SDavid Howells * 9673cb98950SDavid Howells * Precalculate and preallocate a script for the insertion or replacement of an 9683cb98950SDavid Howells * object in an associative array. This results in an edit script that can 9693cb98950SDavid Howells * either be applied or cancelled. 9703cb98950SDavid Howells * 9713cb98950SDavid Howells * The function returns a pointer to an edit script or -ENOMEM. 9723cb98950SDavid Howells * 9733cb98950SDavid Howells * The caller should lock against other modifications and must continue to hold 9743cb98950SDavid Howells * the lock until assoc_array_apply_edit() has been called. 9753cb98950SDavid Howells * 9763cb98950SDavid Howells * Accesses to the tree may take place concurrently with this function, 9773cb98950SDavid Howells * provided they hold the RCU read lock. 9783cb98950SDavid Howells */ 9793cb98950SDavid Howells struct assoc_array_edit *assoc_array_insert(struct assoc_array *array, 9803cb98950SDavid Howells const struct assoc_array_ops *ops, 9813cb98950SDavid Howells const void *index_key, 9823cb98950SDavid Howells void *object) 9833cb98950SDavid Howells { 9843cb98950SDavid Howells struct assoc_array_walk_result result; 9853cb98950SDavid Howells struct assoc_array_edit *edit; 9863cb98950SDavid Howells 9873cb98950SDavid Howells pr_devel("-->%s()\n", __func__); 9883cb98950SDavid Howells 9893cb98950SDavid Howells /* The leaf pointer we're given must not have the bottom bit set as we 9903cb98950SDavid Howells * use those for type-marking the pointer. NULL pointers are also not 9913cb98950SDavid Howells * allowed as they indicate an empty slot but we have to allow them 9923cb98950SDavid Howells * here as they can be updated later. 9933cb98950SDavid Howells */ 9943cb98950SDavid Howells BUG_ON(assoc_array_ptr_is_meta(object)); 9953cb98950SDavid Howells 9963cb98950SDavid Howells edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL); 9973cb98950SDavid Howells if (!edit) 9983cb98950SDavid Howells return ERR_PTR(-ENOMEM); 9993cb98950SDavid Howells edit->array = array; 10003cb98950SDavid Howells edit->ops = ops; 10013cb98950SDavid Howells edit->leaf = assoc_array_leaf_to_ptr(object); 10023cb98950SDavid Howells edit->adjust_count_by = 1; 10033cb98950SDavid Howells 10043cb98950SDavid Howells switch (assoc_array_walk(array, ops, index_key, &result)) { 10053cb98950SDavid Howells case assoc_array_walk_tree_empty: 10063cb98950SDavid Howells /* Allocate a root node if there isn't one yet */ 10073cb98950SDavid Howells if (!assoc_array_insert_in_empty_tree(edit)) 10083cb98950SDavid Howells goto enomem; 10093cb98950SDavid Howells return edit; 10103cb98950SDavid Howells 10113cb98950SDavid Howells case assoc_array_walk_found_terminal_node: 10123cb98950SDavid Howells /* We found a node that doesn't have a node/shortcut pointer in 10133cb98950SDavid Howells * the slot corresponding to the index key that we have to 10143cb98950SDavid Howells * follow. 10153cb98950SDavid Howells */ 10163cb98950SDavid Howells if (!assoc_array_insert_into_terminal_node(edit, ops, index_key, 10173cb98950SDavid Howells &result)) 10183cb98950SDavid Howells goto enomem; 10193cb98950SDavid Howells return edit; 10203cb98950SDavid Howells 10213cb98950SDavid Howells case assoc_array_walk_found_wrong_shortcut: 10223cb98950SDavid Howells /* We found a shortcut that didn't match our key in a slot we 10233cb98950SDavid Howells * needed to follow. 10243cb98950SDavid Howells */ 10253cb98950SDavid Howells if (!assoc_array_insert_mid_shortcut(edit, ops, &result)) 10263cb98950SDavid Howells goto enomem; 10273cb98950SDavid Howells return edit; 10283cb98950SDavid Howells } 10293cb98950SDavid Howells 10303cb98950SDavid Howells enomem: 10313cb98950SDavid Howells /* Clean up after an out of memory error */ 10323cb98950SDavid Howells pr_devel("enomem\n"); 10333cb98950SDavid Howells assoc_array_cancel_edit(edit); 10343cb98950SDavid Howells return ERR_PTR(-ENOMEM); 10353cb98950SDavid Howells } 10363cb98950SDavid Howells 10373cb98950SDavid Howells /** 10383cb98950SDavid Howells * assoc_array_insert_set_object - Set the new object pointer in an edit script 10393cb98950SDavid Howells * @edit: The edit script to modify. 10403cb98950SDavid Howells * @object: The object pointer to set. 10413cb98950SDavid Howells * 10423cb98950SDavid Howells * Change the object to be inserted in an edit script. The object pointed to 10433cb98950SDavid Howells * by the old object is not freed. This must be done prior to applying the 10443cb98950SDavid Howells * script. 10453cb98950SDavid Howells */ 10463cb98950SDavid Howells void assoc_array_insert_set_object(struct assoc_array_edit *edit, void *object) 10473cb98950SDavid Howells { 10483cb98950SDavid Howells BUG_ON(!object); 10493cb98950SDavid Howells edit->leaf = assoc_array_leaf_to_ptr(object); 10503cb98950SDavid Howells } 10513cb98950SDavid Howells 10523cb98950SDavid Howells struct assoc_array_delete_collapse_context { 10533cb98950SDavid Howells struct assoc_array_node *node; 10543cb98950SDavid Howells const void *skip_leaf; 10553cb98950SDavid Howells int slot; 10563cb98950SDavid Howells }; 10573cb98950SDavid Howells 10583cb98950SDavid Howells /* 10593cb98950SDavid Howells * Subtree collapse to node iterator. 10603cb98950SDavid Howells */ 10613cb98950SDavid Howells static int assoc_array_delete_collapse_iterator(const void *leaf, 10623cb98950SDavid Howells void *iterator_data) 10633cb98950SDavid Howells { 10643cb98950SDavid Howells struct assoc_array_delete_collapse_context *collapse = iterator_data; 10653cb98950SDavid Howells 10663cb98950SDavid Howells if (leaf == collapse->skip_leaf) 10673cb98950SDavid Howells return 0; 10683cb98950SDavid Howells 10693cb98950SDavid Howells BUG_ON(collapse->slot >= ASSOC_ARRAY_FAN_OUT); 10703cb98950SDavid Howells 10713cb98950SDavid Howells collapse->node->slots[collapse->slot++] = assoc_array_leaf_to_ptr(leaf); 10723cb98950SDavid Howells return 0; 10733cb98950SDavid Howells } 10743cb98950SDavid Howells 10753cb98950SDavid Howells /** 10763cb98950SDavid Howells * assoc_array_delete - Script deletion of an object from an associative array 10773cb98950SDavid Howells * @array: The array to search. 10783cb98950SDavid Howells * @ops: The operations to use. 10793cb98950SDavid Howells * @index_key: The key to the object. 10803cb98950SDavid Howells * 10813cb98950SDavid Howells * Precalculate and preallocate a script for the deletion of an object from an 10823cb98950SDavid Howells * associative array. This results in an edit script that can either be 10833cb98950SDavid Howells * applied or cancelled. 10843cb98950SDavid Howells * 10853cb98950SDavid Howells * The function returns a pointer to an edit script if the object was found, 10863cb98950SDavid Howells * NULL if the object was not found or -ENOMEM. 10873cb98950SDavid Howells * 10883cb98950SDavid Howells * The caller should lock against other modifications and must continue to hold 10893cb98950SDavid Howells * the lock until assoc_array_apply_edit() has been called. 10903cb98950SDavid Howells * 10913cb98950SDavid Howells * Accesses to the tree may take place concurrently with this function, 10923cb98950SDavid Howells * provided they hold the RCU read lock. 10933cb98950SDavid Howells */ 10943cb98950SDavid Howells struct assoc_array_edit *assoc_array_delete(struct assoc_array *array, 10953cb98950SDavid Howells const struct assoc_array_ops *ops, 10963cb98950SDavid Howells const void *index_key) 10973cb98950SDavid Howells { 10983cb98950SDavid Howells struct assoc_array_delete_collapse_context collapse; 10993cb98950SDavid Howells struct assoc_array_walk_result result; 11003cb98950SDavid Howells struct assoc_array_node *node, *new_n0; 11013cb98950SDavid Howells struct assoc_array_edit *edit; 11023cb98950SDavid Howells struct assoc_array_ptr *ptr; 11033cb98950SDavid Howells bool has_meta; 11043cb98950SDavid Howells int slot, i; 11053cb98950SDavid Howells 11063cb98950SDavid Howells pr_devel("-->%s()\n", __func__); 11073cb98950SDavid Howells 11083cb98950SDavid Howells edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL); 11093cb98950SDavid Howells if (!edit) 11103cb98950SDavid Howells return ERR_PTR(-ENOMEM); 11113cb98950SDavid Howells edit->array = array; 11123cb98950SDavid Howells edit->ops = ops; 11133cb98950SDavid Howells edit->adjust_count_by = -1; 11143cb98950SDavid Howells 11153cb98950SDavid Howells switch (assoc_array_walk(array, ops, index_key, &result)) { 11163cb98950SDavid Howells case assoc_array_walk_found_terminal_node: 11173cb98950SDavid Howells /* We found a node that should contain the leaf we've been 11183cb98950SDavid Howells * asked to remove - *if* it's in the tree. 11193cb98950SDavid Howells */ 11203cb98950SDavid Howells pr_devel("terminal_node\n"); 11213cb98950SDavid Howells node = result.terminal_node.node; 11223cb98950SDavid Howells 11233cb98950SDavid Howells for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) { 11243cb98950SDavid Howells ptr = node->slots[slot]; 11253cb98950SDavid Howells if (ptr && 11263cb98950SDavid Howells assoc_array_ptr_is_leaf(ptr) && 11273cb98950SDavid Howells ops->compare_object(assoc_array_ptr_to_leaf(ptr), 11283cb98950SDavid Howells index_key)) 11293cb98950SDavid Howells goto found_leaf; 11303cb98950SDavid Howells } 11313cb98950SDavid Howells case assoc_array_walk_tree_empty: 11323cb98950SDavid Howells case assoc_array_walk_found_wrong_shortcut: 11333cb98950SDavid Howells default: 11343cb98950SDavid Howells assoc_array_cancel_edit(edit); 11353cb98950SDavid Howells pr_devel("not found\n"); 11363cb98950SDavid Howells return NULL; 11373cb98950SDavid Howells } 11383cb98950SDavid Howells 11393cb98950SDavid Howells found_leaf: 11403cb98950SDavid Howells BUG_ON(array->nr_leaves_on_tree <= 0); 11413cb98950SDavid Howells 11423cb98950SDavid Howells /* In the simplest form of deletion we just clear the slot and release 11433cb98950SDavid Howells * the leaf after a suitable interval. 11443cb98950SDavid Howells */ 11453cb98950SDavid Howells edit->dead_leaf = node->slots[slot]; 11463cb98950SDavid Howells edit->set[0].ptr = &node->slots[slot]; 11473cb98950SDavid Howells edit->set[0].to = NULL; 11483cb98950SDavid Howells edit->adjust_count_on = node; 11493cb98950SDavid Howells 11503cb98950SDavid Howells /* If that concludes erasure of the last leaf, then delete the entire 11513cb98950SDavid Howells * internal array. 11523cb98950SDavid Howells */ 11533cb98950SDavid Howells if (array->nr_leaves_on_tree == 1) { 11543cb98950SDavid Howells edit->set[1].ptr = &array->root; 11553cb98950SDavid Howells edit->set[1].to = NULL; 11563cb98950SDavid Howells edit->adjust_count_on = NULL; 11573cb98950SDavid Howells edit->excised_subtree = array->root; 11583cb98950SDavid Howells pr_devel("all gone\n"); 11593cb98950SDavid Howells return edit; 11603cb98950SDavid Howells } 11613cb98950SDavid Howells 11623cb98950SDavid Howells /* However, we'd also like to clear up some metadata blocks if we 11633cb98950SDavid Howells * possibly can. 11643cb98950SDavid Howells * 11653cb98950SDavid Howells * We go for a simple algorithm of: if this node has FAN_OUT or fewer 11663cb98950SDavid Howells * leaves in it, then attempt to collapse it - and attempt to 11673cb98950SDavid Howells * recursively collapse up the tree. 11683cb98950SDavid Howells * 11693cb98950SDavid Howells * We could also try and collapse in partially filled subtrees to take 11703cb98950SDavid Howells * up space in this node. 11713cb98950SDavid Howells */ 11723cb98950SDavid Howells if (node->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT + 1) { 11733cb98950SDavid Howells struct assoc_array_node *parent, *grandparent; 11743cb98950SDavid Howells struct assoc_array_ptr *ptr; 11753cb98950SDavid Howells 11763cb98950SDavid Howells /* First of all, we need to know if this node has metadata so 11773cb98950SDavid Howells * that we don't try collapsing if all the leaves are already 11783cb98950SDavid Howells * here. 11793cb98950SDavid Howells */ 11803cb98950SDavid Howells has_meta = false; 11813cb98950SDavid Howells for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) { 11823cb98950SDavid Howells ptr = node->slots[i]; 11833cb98950SDavid Howells if (assoc_array_ptr_is_meta(ptr)) { 11843cb98950SDavid Howells has_meta = true; 11853cb98950SDavid Howells break; 11863cb98950SDavid Howells } 11873cb98950SDavid Howells } 11883cb98950SDavid Howells 11893cb98950SDavid Howells pr_devel("leaves: %ld [m=%d]\n", 11903cb98950SDavid Howells node->nr_leaves_on_branch - 1, has_meta); 11913cb98950SDavid Howells 11923cb98950SDavid Howells /* Look further up the tree to see if we can collapse this node 11933cb98950SDavid Howells * into a more proximal node too. 11943cb98950SDavid Howells */ 11953cb98950SDavid Howells parent = node; 11963cb98950SDavid Howells collapse_up: 11973cb98950SDavid Howells pr_devel("collapse subtree: %ld\n", parent->nr_leaves_on_branch); 11983cb98950SDavid Howells 11993cb98950SDavid Howells ptr = parent->back_pointer; 12003cb98950SDavid Howells if (!ptr) 12013cb98950SDavid Howells goto do_collapse; 12023cb98950SDavid Howells if (assoc_array_ptr_is_shortcut(ptr)) { 12033cb98950SDavid Howells struct assoc_array_shortcut *s = assoc_array_ptr_to_shortcut(ptr); 12043cb98950SDavid Howells ptr = s->back_pointer; 12053cb98950SDavid Howells if (!ptr) 12063cb98950SDavid Howells goto do_collapse; 12073cb98950SDavid Howells } 12083cb98950SDavid Howells 12093cb98950SDavid Howells grandparent = assoc_array_ptr_to_node(ptr); 12103cb98950SDavid Howells if (grandparent->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT + 1) { 12113cb98950SDavid Howells parent = grandparent; 12123cb98950SDavid Howells goto collapse_up; 12133cb98950SDavid Howells } 12143cb98950SDavid Howells 12153cb98950SDavid Howells do_collapse: 12163cb98950SDavid Howells /* There's no point collapsing if the original node has no meta 12173cb98950SDavid Howells * pointers to discard and if we didn't merge into one of that 12183cb98950SDavid Howells * node's ancestry. 12193cb98950SDavid Howells */ 12203cb98950SDavid Howells if (has_meta || parent != node) { 12213cb98950SDavid Howells node = parent; 12223cb98950SDavid Howells 12233cb98950SDavid Howells /* Create a new node to collapse into */ 12243cb98950SDavid Howells new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL); 12253cb98950SDavid Howells if (!new_n0) 12263cb98950SDavid Howells goto enomem; 12273cb98950SDavid Howells edit->new_meta[0] = assoc_array_node_to_ptr(new_n0); 12283cb98950SDavid Howells 12293cb98950SDavid Howells new_n0->back_pointer = node->back_pointer; 12303cb98950SDavid Howells new_n0->parent_slot = node->parent_slot; 12313cb98950SDavid Howells new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch; 12323cb98950SDavid Howells edit->adjust_count_on = new_n0; 12333cb98950SDavid Howells 12343cb98950SDavid Howells collapse.node = new_n0; 12353cb98950SDavid Howells collapse.skip_leaf = assoc_array_ptr_to_leaf(edit->dead_leaf); 12363cb98950SDavid Howells collapse.slot = 0; 12373cb98950SDavid Howells assoc_array_subtree_iterate(assoc_array_node_to_ptr(node), 12383cb98950SDavid Howells node->back_pointer, 12393cb98950SDavid Howells assoc_array_delete_collapse_iterator, 12403cb98950SDavid Howells &collapse); 12413cb98950SDavid Howells pr_devel("collapsed %d,%lu\n", collapse.slot, new_n0->nr_leaves_on_branch); 12423cb98950SDavid Howells BUG_ON(collapse.slot != new_n0->nr_leaves_on_branch - 1); 12433cb98950SDavid Howells 12443cb98950SDavid Howells if (!node->back_pointer) { 12453cb98950SDavid Howells edit->set[1].ptr = &array->root; 12463cb98950SDavid Howells } else if (assoc_array_ptr_is_leaf(node->back_pointer)) { 12473cb98950SDavid Howells BUG(); 12483cb98950SDavid Howells } else if (assoc_array_ptr_is_node(node->back_pointer)) { 12493cb98950SDavid Howells struct assoc_array_node *p = 12503cb98950SDavid Howells assoc_array_ptr_to_node(node->back_pointer); 12513cb98950SDavid Howells edit->set[1].ptr = &p->slots[node->parent_slot]; 12523cb98950SDavid Howells } else if (assoc_array_ptr_is_shortcut(node->back_pointer)) { 12533cb98950SDavid Howells struct assoc_array_shortcut *s = 12543cb98950SDavid Howells assoc_array_ptr_to_shortcut(node->back_pointer); 12553cb98950SDavid Howells edit->set[1].ptr = &s->next_node; 12563cb98950SDavid Howells } 12573cb98950SDavid Howells edit->set[1].to = assoc_array_node_to_ptr(new_n0); 12583cb98950SDavid Howells edit->excised_subtree = assoc_array_node_to_ptr(node); 12593cb98950SDavid Howells } 12603cb98950SDavid Howells } 12613cb98950SDavid Howells 12623cb98950SDavid Howells return edit; 12633cb98950SDavid Howells 12643cb98950SDavid Howells enomem: 12653cb98950SDavid Howells /* Clean up after an out of memory error */ 12663cb98950SDavid Howells pr_devel("enomem\n"); 12673cb98950SDavid Howells assoc_array_cancel_edit(edit); 12683cb98950SDavid Howells return ERR_PTR(-ENOMEM); 12693cb98950SDavid Howells } 12703cb98950SDavid Howells 12713cb98950SDavid Howells /** 12723cb98950SDavid Howells * assoc_array_clear - Script deletion of all objects from an associative array 12733cb98950SDavid Howells * @array: The array to clear. 12743cb98950SDavid Howells * @ops: The operations to use. 12753cb98950SDavid Howells * 12763cb98950SDavid Howells * Precalculate and preallocate a script for the deletion of all the objects 12773cb98950SDavid Howells * from an associative array. This results in an edit script that can either 12783cb98950SDavid Howells * be applied or cancelled. 12793cb98950SDavid Howells * 12803cb98950SDavid Howells * The function returns a pointer to an edit script if there are objects to be 12813cb98950SDavid Howells * deleted, NULL if there are no objects in the array or -ENOMEM. 12823cb98950SDavid Howells * 12833cb98950SDavid Howells * The caller should lock against other modifications and must continue to hold 12843cb98950SDavid Howells * the lock until assoc_array_apply_edit() has been called. 12853cb98950SDavid Howells * 12863cb98950SDavid Howells * Accesses to the tree may take place concurrently with this function, 12873cb98950SDavid Howells * provided they hold the RCU read lock. 12883cb98950SDavid Howells */ 12893cb98950SDavid Howells struct assoc_array_edit *assoc_array_clear(struct assoc_array *array, 12903cb98950SDavid Howells const struct assoc_array_ops *ops) 12913cb98950SDavid Howells { 12923cb98950SDavid Howells struct assoc_array_edit *edit; 12933cb98950SDavid Howells 12943cb98950SDavid Howells pr_devel("-->%s()\n", __func__); 12953cb98950SDavid Howells 12963cb98950SDavid Howells if (!array->root) 12973cb98950SDavid Howells return NULL; 12983cb98950SDavid Howells 12993cb98950SDavid Howells edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL); 13003cb98950SDavid Howells if (!edit) 13013cb98950SDavid Howells return ERR_PTR(-ENOMEM); 13023cb98950SDavid Howells edit->array = array; 13033cb98950SDavid Howells edit->ops = ops; 13043cb98950SDavid Howells edit->set[1].ptr = &array->root; 13053cb98950SDavid Howells edit->set[1].to = NULL; 13063cb98950SDavid Howells edit->excised_subtree = array->root; 13073cb98950SDavid Howells edit->ops_for_excised_subtree = ops; 13083cb98950SDavid Howells pr_devel("all gone\n"); 13093cb98950SDavid Howells return edit; 13103cb98950SDavid Howells } 13113cb98950SDavid Howells 13123cb98950SDavid Howells /* 13133cb98950SDavid Howells * Handle the deferred destruction after an applied edit. 13143cb98950SDavid Howells */ 13153cb98950SDavid Howells static void assoc_array_rcu_cleanup(struct rcu_head *head) 13163cb98950SDavid Howells { 13173cb98950SDavid Howells struct assoc_array_edit *edit = 13183cb98950SDavid Howells container_of(head, struct assoc_array_edit, rcu); 13193cb98950SDavid Howells int i; 13203cb98950SDavid Howells 13213cb98950SDavid Howells pr_devel("-->%s()\n", __func__); 13223cb98950SDavid Howells 13233cb98950SDavid Howells if (edit->dead_leaf) 13243cb98950SDavid Howells edit->ops->free_object(assoc_array_ptr_to_leaf(edit->dead_leaf)); 13253cb98950SDavid Howells for (i = 0; i < ARRAY_SIZE(edit->excised_meta); i++) 13263cb98950SDavid Howells if (edit->excised_meta[i]) 13273cb98950SDavid Howells kfree(assoc_array_ptr_to_node(edit->excised_meta[i])); 13283cb98950SDavid Howells 13293cb98950SDavid Howells if (edit->excised_subtree) { 13303cb98950SDavid Howells BUG_ON(assoc_array_ptr_is_leaf(edit->excised_subtree)); 13313cb98950SDavid Howells if (assoc_array_ptr_is_node(edit->excised_subtree)) { 13323cb98950SDavid Howells struct assoc_array_node *n = 13333cb98950SDavid Howells assoc_array_ptr_to_node(edit->excised_subtree); 13343cb98950SDavid Howells n->back_pointer = NULL; 13353cb98950SDavid Howells } else { 13363cb98950SDavid Howells struct assoc_array_shortcut *s = 13373cb98950SDavid Howells assoc_array_ptr_to_shortcut(edit->excised_subtree); 13383cb98950SDavid Howells s->back_pointer = NULL; 13393cb98950SDavid Howells } 13403cb98950SDavid Howells assoc_array_destroy_subtree(edit->excised_subtree, 13413cb98950SDavid Howells edit->ops_for_excised_subtree); 13423cb98950SDavid Howells } 13433cb98950SDavid Howells 13443cb98950SDavid Howells kfree(edit); 13453cb98950SDavid Howells } 13463cb98950SDavid Howells 13473cb98950SDavid Howells /** 13483cb98950SDavid Howells * assoc_array_apply_edit - Apply an edit script to an associative array 13493cb98950SDavid Howells * @edit: The script to apply. 13503cb98950SDavid Howells * 13513cb98950SDavid Howells * Apply an edit script to an associative array to effect an insertion, 13523cb98950SDavid Howells * deletion or clearance. As the edit script includes preallocated memory, 13533cb98950SDavid Howells * this is guaranteed not to fail. 13543cb98950SDavid Howells * 13553cb98950SDavid Howells * The edit script, dead objects and dead metadata will be scheduled for 13563cb98950SDavid Howells * destruction after an RCU grace period to permit those doing read-only 13573cb98950SDavid Howells * accesses on the array to continue to do so under the RCU read lock whilst 13583cb98950SDavid Howells * the edit is taking place. 13593cb98950SDavid Howells */ 13603cb98950SDavid Howells void assoc_array_apply_edit(struct assoc_array_edit *edit) 13613cb98950SDavid Howells { 13623cb98950SDavid Howells struct assoc_array_shortcut *shortcut; 13633cb98950SDavid Howells struct assoc_array_node *node; 13643cb98950SDavid Howells struct assoc_array_ptr *ptr; 13653cb98950SDavid Howells int i; 13663cb98950SDavid Howells 13673cb98950SDavid Howells pr_devel("-->%s()\n", __func__); 13683cb98950SDavid Howells 13693cb98950SDavid Howells smp_wmb(); 13703cb98950SDavid Howells if (edit->leaf_p) 13713cb98950SDavid Howells *edit->leaf_p = edit->leaf; 13723cb98950SDavid Howells 13733cb98950SDavid Howells smp_wmb(); 13743cb98950SDavid Howells for (i = 0; i < ARRAY_SIZE(edit->set_parent_slot); i++) 13753cb98950SDavid Howells if (edit->set_parent_slot[i].p) 13763cb98950SDavid Howells *edit->set_parent_slot[i].p = edit->set_parent_slot[i].to; 13773cb98950SDavid Howells 13783cb98950SDavid Howells smp_wmb(); 13793cb98950SDavid Howells for (i = 0; i < ARRAY_SIZE(edit->set_backpointers); i++) 13803cb98950SDavid Howells if (edit->set_backpointers[i]) 13813cb98950SDavid Howells *edit->set_backpointers[i] = edit->set_backpointers_to; 13823cb98950SDavid Howells 13833cb98950SDavid Howells smp_wmb(); 13843cb98950SDavid Howells for (i = 0; i < ARRAY_SIZE(edit->set); i++) 13853cb98950SDavid Howells if (edit->set[i].ptr) 13863cb98950SDavid Howells *edit->set[i].ptr = edit->set[i].to; 13873cb98950SDavid Howells 13883cb98950SDavid Howells if (edit->array->root == NULL) { 13893cb98950SDavid Howells edit->array->nr_leaves_on_tree = 0; 13903cb98950SDavid Howells } else if (edit->adjust_count_on) { 13913cb98950SDavid Howells node = edit->adjust_count_on; 13923cb98950SDavid Howells for (;;) { 13933cb98950SDavid Howells node->nr_leaves_on_branch += edit->adjust_count_by; 13943cb98950SDavid Howells 13953cb98950SDavid Howells ptr = node->back_pointer; 13963cb98950SDavid Howells if (!ptr) 13973cb98950SDavid Howells break; 13983cb98950SDavid Howells if (assoc_array_ptr_is_shortcut(ptr)) { 13993cb98950SDavid Howells shortcut = assoc_array_ptr_to_shortcut(ptr); 14003cb98950SDavid Howells ptr = shortcut->back_pointer; 14013cb98950SDavid Howells if (!ptr) 14023cb98950SDavid Howells break; 14033cb98950SDavid Howells } 14043cb98950SDavid Howells BUG_ON(!assoc_array_ptr_is_node(ptr)); 14053cb98950SDavid Howells node = assoc_array_ptr_to_node(ptr); 14063cb98950SDavid Howells } 14073cb98950SDavid Howells 14083cb98950SDavid Howells edit->array->nr_leaves_on_tree += edit->adjust_count_by; 14093cb98950SDavid Howells } 14103cb98950SDavid Howells 14113cb98950SDavid Howells call_rcu(&edit->rcu, assoc_array_rcu_cleanup); 14123cb98950SDavid Howells } 14133cb98950SDavid Howells 14143cb98950SDavid Howells /** 14153cb98950SDavid Howells * assoc_array_cancel_edit - Discard an edit script. 14163cb98950SDavid Howells * @edit: The script to discard. 14173cb98950SDavid Howells * 14183cb98950SDavid Howells * Free an edit script and all the preallocated data it holds without making 14193cb98950SDavid Howells * any changes to the associative array it was intended for. 14203cb98950SDavid Howells * 14213cb98950SDavid Howells * NOTE! In the case of an insertion script, this does _not_ release the leaf 14223cb98950SDavid Howells * that was to be inserted. That is left to the caller. 14233cb98950SDavid Howells */ 14243cb98950SDavid Howells void assoc_array_cancel_edit(struct assoc_array_edit *edit) 14253cb98950SDavid Howells { 14263cb98950SDavid Howells struct assoc_array_ptr *ptr; 14273cb98950SDavid Howells int i; 14283cb98950SDavid Howells 14293cb98950SDavid Howells pr_devel("-->%s()\n", __func__); 14303cb98950SDavid Howells 14313cb98950SDavid Howells /* Clean up after an out of memory error */ 14323cb98950SDavid Howells for (i = 0; i < ARRAY_SIZE(edit->new_meta); i++) { 14333cb98950SDavid Howells ptr = edit->new_meta[i]; 14343cb98950SDavid Howells if (ptr) { 14353cb98950SDavid Howells if (assoc_array_ptr_is_node(ptr)) 14363cb98950SDavid Howells kfree(assoc_array_ptr_to_node(ptr)); 14373cb98950SDavid Howells else 14383cb98950SDavid Howells kfree(assoc_array_ptr_to_shortcut(ptr)); 14393cb98950SDavid Howells } 14403cb98950SDavid Howells } 14413cb98950SDavid Howells kfree(edit); 14423cb98950SDavid Howells } 14433cb98950SDavid Howells 14443cb98950SDavid Howells /** 14453cb98950SDavid Howells * assoc_array_gc - Garbage collect an associative array. 14463cb98950SDavid Howells * @array: The array to clean. 14473cb98950SDavid Howells * @ops: The operations to use. 14483cb98950SDavid Howells * @iterator: A callback function to pass judgement on each object. 14493cb98950SDavid Howells * @iterator_data: Private data for the callback function. 14503cb98950SDavid Howells * 14513cb98950SDavid Howells * Collect garbage from an associative array and pack down the internal tree to 14523cb98950SDavid Howells * save memory. 14533cb98950SDavid Howells * 14543cb98950SDavid Howells * The iterator function is asked to pass judgement upon each object in the 14553cb98950SDavid Howells * array. If it returns false, the object is discard and if it returns true, 14563cb98950SDavid Howells * the object is kept. If it returns true, it must increment the object's 14573cb98950SDavid Howells * usage count (or whatever it needs to do to retain it) before returning. 14583cb98950SDavid Howells * 14593cb98950SDavid Howells * This function returns 0 if successful or -ENOMEM if out of memory. In the 14603cb98950SDavid Howells * latter case, the array is not changed. 14613cb98950SDavid Howells * 14623cb98950SDavid Howells * The caller should lock against other modifications and must continue to hold 14633cb98950SDavid Howells * the lock until assoc_array_apply_edit() has been called. 14643cb98950SDavid Howells * 14653cb98950SDavid Howells * Accesses to the tree may take place concurrently with this function, 14663cb98950SDavid Howells * provided they hold the RCU read lock. 14673cb98950SDavid Howells */ 14683cb98950SDavid Howells int assoc_array_gc(struct assoc_array *array, 14693cb98950SDavid Howells const struct assoc_array_ops *ops, 14703cb98950SDavid Howells bool (*iterator)(void *object, void *iterator_data), 14713cb98950SDavid Howells void *iterator_data) 14723cb98950SDavid Howells { 14733cb98950SDavid Howells struct assoc_array_shortcut *shortcut, *new_s; 14743cb98950SDavid Howells struct assoc_array_node *node, *new_n; 14753cb98950SDavid Howells struct assoc_array_edit *edit; 14763cb98950SDavid Howells struct assoc_array_ptr *cursor, *ptr; 14773cb98950SDavid Howells struct assoc_array_ptr *new_root, *new_parent, **new_ptr_pp; 14783cb98950SDavid Howells unsigned long nr_leaves_on_tree; 14793cb98950SDavid Howells int keylen, slot, nr_free, next_slot, i; 14803cb98950SDavid Howells 14813cb98950SDavid Howells pr_devel("-->%s()\n", __func__); 14823cb98950SDavid Howells 14833cb98950SDavid Howells if (!array->root) 14843cb98950SDavid Howells return 0; 14853cb98950SDavid Howells 14863cb98950SDavid Howells edit = kzalloc(sizeof(struct assoc_array_edit), GFP_KERNEL); 14873cb98950SDavid Howells if (!edit) 14883cb98950SDavid Howells return -ENOMEM; 14893cb98950SDavid Howells edit->array = array; 14903cb98950SDavid Howells edit->ops = ops; 14913cb98950SDavid Howells edit->ops_for_excised_subtree = ops; 14923cb98950SDavid Howells edit->set[0].ptr = &array->root; 14933cb98950SDavid Howells edit->excised_subtree = array->root; 14943cb98950SDavid Howells 14953cb98950SDavid Howells new_root = new_parent = NULL; 14963cb98950SDavid Howells new_ptr_pp = &new_root; 14973cb98950SDavid Howells cursor = array->root; 14983cb98950SDavid Howells 14993cb98950SDavid Howells descend: 15003cb98950SDavid Howells /* If this point is a shortcut, then we need to duplicate it and 15013cb98950SDavid Howells * advance the target cursor. 15023cb98950SDavid Howells */ 15033cb98950SDavid Howells if (assoc_array_ptr_is_shortcut(cursor)) { 15043cb98950SDavid Howells shortcut = assoc_array_ptr_to_shortcut(cursor); 15053cb98950SDavid Howells keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE); 15063cb98950SDavid Howells keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT; 15073cb98950SDavid Howells new_s = kmalloc(sizeof(struct assoc_array_shortcut) + 15083cb98950SDavid Howells keylen * sizeof(unsigned long), GFP_KERNEL); 15093cb98950SDavid Howells if (!new_s) 15103cb98950SDavid Howells goto enomem; 15113cb98950SDavid Howells pr_devel("dup shortcut %p -> %p\n", shortcut, new_s); 15123cb98950SDavid Howells memcpy(new_s, shortcut, (sizeof(struct assoc_array_shortcut) + 15133cb98950SDavid Howells keylen * sizeof(unsigned long))); 15143cb98950SDavid Howells new_s->back_pointer = new_parent; 15153cb98950SDavid Howells new_s->parent_slot = shortcut->parent_slot; 15163cb98950SDavid Howells *new_ptr_pp = new_parent = assoc_array_shortcut_to_ptr(new_s); 15173cb98950SDavid Howells new_ptr_pp = &new_s->next_node; 15183cb98950SDavid Howells cursor = shortcut->next_node; 15193cb98950SDavid Howells } 15203cb98950SDavid Howells 15213cb98950SDavid Howells /* Duplicate the node at this position */ 15223cb98950SDavid Howells node = assoc_array_ptr_to_node(cursor); 15233cb98950SDavid Howells new_n = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL); 15243cb98950SDavid Howells if (!new_n) 15253cb98950SDavid Howells goto enomem; 15263cb98950SDavid Howells pr_devel("dup node %p -> %p\n", node, new_n); 15273cb98950SDavid Howells new_n->back_pointer = new_parent; 15283cb98950SDavid Howells new_n->parent_slot = node->parent_slot; 15293cb98950SDavid Howells *new_ptr_pp = new_parent = assoc_array_node_to_ptr(new_n); 15303cb98950SDavid Howells new_ptr_pp = NULL; 15313cb98950SDavid Howells slot = 0; 15323cb98950SDavid Howells 15333cb98950SDavid Howells continue_node: 15343cb98950SDavid Howells /* Filter across any leaves and gc any subtrees */ 15353cb98950SDavid Howells for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) { 15363cb98950SDavid Howells ptr = node->slots[slot]; 15373cb98950SDavid Howells if (!ptr) 15383cb98950SDavid Howells continue; 15393cb98950SDavid Howells 15403cb98950SDavid Howells if (assoc_array_ptr_is_leaf(ptr)) { 15413cb98950SDavid Howells if (iterator(assoc_array_ptr_to_leaf(ptr), 15423cb98950SDavid Howells iterator_data)) 15433cb98950SDavid Howells /* The iterator will have done any reference 15443cb98950SDavid Howells * counting on the object for us. 15453cb98950SDavid Howells */ 15463cb98950SDavid Howells new_n->slots[slot] = ptr; 15473cb98950SDavid Howells continue; 15483cb98950SDavid Howells } 15493cb98950SDavid Howells 15503cb98950SDavid Howells new_ptr_pp = &new_n->slots[slot]; 15513cb98950SDavid Howells cursor = ptr; 15523cb98950SDavid Howells goto descend; 15533cb98950SDavid Howells } 15543cb98950SDavid Howells 15553cb98950SDavid Howells pr_devel("-- compress node %p --\n", new_n); 15563cb98950SDavid Howells 15573cb98950SDavid Howells /* Count up the number of empty slots in this node and work out the 15583cb98950SDavid Howells * subtree leaf count. 15593cb98950SDavid Howells */ 15603cb98950SDavid Howells new_n->nr_leaves_on_branch = 0; 15613cb98950SDavid Howells nr_free = 0; 15623cb98950SDavid Howells for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) { 15633cb98950SDavid Howells ptr = new_n->slots[slot]; 15643cb98950SDavid Howells if (!ptr) 15653cb98950SDavid Howells nr_free++; 15663cb98950SDavid Howells else if (assoc_array_ptr_is_leaf(ptr)) 15673cb98950SDavid Howells new_n->nr_leaves_on_branch++; 15683cb98950SDavid Howells } 15693cb98950SDavid Howells pr_devel("free=%d, leaves=%lu\n", nr_free, new_n->nr_leaves_on_branch); 15703cb98950SDavid Howells 15713cb98950SDavid Howells /* See what we can fold in */ 15723cb98950SDavid Howells next_slot = 0; 15733cb98950SDavid Howells for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) { 15743cb98950SDavid Howells struct assoc_array_shortcut *s; 15753cb98950SDavid Howells struct assoc_array_node *child; 15763cb98950SDavid Howells 15773cb98950SDavid Howells ptr = new_n->slots[slot]; 15783cb98950SDavid Howells if (!ptr || assoc_array_ptr_is_leaf(ptr)) 15793cb98950SDavid Howells continue; 15803cb98950SDavid Howells 15813cb98950SDavid Howells s = NULL; 15823cb98950SDavid Howells if (assoc_array_ptr_is_shortcut(ptr)) { 15833cb98950SDavid Howells s = assoc_array_ptr_to_shortcut(ptr); 15843cb98950SDavid Howells ptr = s->next_node; 15853cb98950SDavid Howells } 15863cb98950SDavid Howells 15873cb98950SDavid Howells child = assoc_array_ptr_to_node(ptr); 15883cb98950SDavid Howells new_n->nr_leaves_on_branch += child->nr_leaves_on_branch; 15893cb98950SDavid Howells 15903cb98950SDavid Howells if (child->nr_leaves_on_branch <= nr_free + 1) { 15913cb98950SDavid Howells /* Fold the child node into this one */ 15923cb98950SDavid Howells pr_devel("[%d] fold node %lu/%d [nx %d]\n", 15933cb98950SDavid Howells slot, child->nr_leaves_on_branch, nr_free + 1, 15943cb98950SDavid Howells next_slot); 15953cb98950SDavid Howells 15963cb98950SDavid Howells /* We would already have reaped an intervening shortcut 15973cb98950SDavid Howells * on the way back up the tree. 15983cb98950SDavid Howells */ 15993cb98950SDavid Howells BUG_ON(s); 16003cb98950SDavid Howells 16013cb98950SDavid Howells new_n->slots[slot] = NULL; 16023cb98950SDavid Howells nr_free++; 16033cb98950SDavid Howells if (slot < next_slot) 16043cb98950SDavid Howells next_slot = slot; 16053cb98950SDavid Howells for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) { 16063cb98950SDavid Howells struct assoc_array_ptr *p = child->slots[i]; 16073cb98950SDavid Howells if (!p) 16083cb98950SDavid Howells continue; 16093cb98950SDavid Howells BUG_ON(assoc_array_ptr_is_meta(p)); 16103cb98950SDavid Howells while (new_n->slots[next_slot]) 16113cb98950SDavid Howells next_slot++; 16123cb98950SDavid Howells BUG_ON(next_slot >= ASSOC_ARRAY_FAN_OUT); 16133cb98950SDavid Howells new_n->slots[next_slot++] = p; 16143cb98950SDavid Howells nr_free--; 16153cb98950SDavid Howells } 16163cb98950SDavid Howells kfree(child); 16173cb98950SDavid Howells } else { 16183cb98950SDavid Howells pr_devel("[%d] retain node %lu/%d [nx %d]\n", 16193cb98950SDavid Howells slot, child->nr_leaves_on_branch, nr_free + 1, 16203cb98950SDavid Howells next_slot); 16213cb98950SDavid Howells } 16223cb98950SDavid Howells } 16233cb98950SDavid Howells 16243cb98950SDavid Howells pr_devel("after: %lu\n", new_n->nr_leaves_on_branch); 16253cb98950SDavid Howells 16263cb98950SDavid Howells nr_leaves_on_tree = new_n->nr_leaves_on_branch; 16273cb98950SDavid Howells 16283cb98950SDavid Howells /* Excise this node if it is singly occupied by a shortcut */ 16293cb98950SDavid Howells if (nr_free == ASSOC_ARRAY_FAN_OUT - 1) { 16303cb98950SDavid Howells for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) 16313cb98950SDavid Howells if ((ptr = new_n->slots[slot])) 16323cb98950SDavid Howells break; 16333cb98950SDavid Howells 16343cb98950SDavid Howells if (assoc_array_ptr_is_meta(ptr) && 16353cb98950SDavid Howells assoc_array_ptr_is_shortcut(ptr)) { 16363cb98950SDavid Howells pr_devel("excise node %p with 1 shortcut\n", new_n); 16373cb98950SDavid Howells new_s = assoc_array_ptr_to_shortcut(ptr); 16383cb98950SDavid Howells new_parent = new_n->back_pointer; 16393cb98950SDavid Howells slot = new_n->parent_slot; 16403cb98950SDavid Howells kfree(new_n); 16413cb98950SDavid Howells if (!new_parent) { 16423cb98950SDavid Howells new_s->back_pointer = NULL; 16433cb98950SDavid Howells new_s->parent_slot = 0; 16443cb98950SDavid Howells new_root = ptr; 16453cb98950SDavid Howells goto gc_complete; 16463cb98950SDavid Howells } 16473cb98950SDavid Howells 16483cb98950SDavid Howells if (assoc_array_ptr_is_shortcut(new_parent)) { 16493cb98950SDavid Howells /* We can discard any preceding shortcut also */ 16503cb98950SDavid Howells struct assoc_array_shortcut *s = 16513cb98950SDavid Howells assoc_array_ptr_to_shortcut(new_parent); 16523cb98950SDavid Howells 16533cb98950SDavid Howells pr_devel("excise preceding shortcut\n"); 16543cb98950SDavid Howells 16553cb98950SDavid Howells new_parent = new_s->back_pointer = s->back_pointer; 16563cb98950SDavid Howells slot = new_s->parent_slot = s->parent_slot; 16573cb98950SDavid Howells kfree(s); 16583cb98950SDavid Howells if (!new_parent) { 16593cb98950SDavid Howells new_s->back_pointer = NULL; 16603cb98950SDavid Howells new_s->parent_slot = 0; 16613cb98950SDavid Howells new_root = ptr; 16623cb98950SDavid Howells goto gc_complete; 16633cb98950SDavid Howells } 16643cb98950SDavid Howells } 16653cb98950SDavid Howells 16663cb98950SDavid Howells new_s->back_pointer = new_parent; 16673cb98950SDavid Howells new_s->parent_slot = slot; 16683cb98950SDavid Howells new_n = assoc_array_ptr_to_node(new_parent); 16693cb98950SDavid Howells new_n->slots[slot] = ptr; 16703cb98950SDavid Howells goto ascend_old_tree; 16713cb98950SDavid Howells } 16723cb98950SDavid Howells } 16733cb98950SDavid Howells 16743cb98950SDavid Howells /* Excise any shortcuts we might encounter that point to nodes that 16753cb98950SDavid Howells * only contain leaves. 16763cb98950SDavid Howells */ 16773cb98950SDavid Howells ptr = new_n->back_pointer; 16783cb98950SDavid Howells if (!ptr) 16793cb98950SDavid Howells goto gc_complete; 16803cb98950SDavid Howells 16813cb98950SDavid Howells if (assoc_array_ptr_is_shortcut(ptr)) { 16823cb98950SDavid Howells new_s = assoc_array_ptr_to_shortcut(ptr); 16833cb98950SDavid Howells new_parent = new_s->back_pointer; 16843cb98950SDavid Howells slot = new_s->parent_slot; 16853cb98950SDavid Howells 16863cb98950SDavid Howells if (new_n->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT) { 16873cb98950SDavid Howells struct assoc_array_node *n; 16883cb98950SDavid Howells 16893cb98950SDavid Howells pr_devel("excise shortcut\n"); 16903cb98950SDavid Howells new_n->back_pointer = new_parent; 16913cb98950SDavid Howells new_n->parent_slot = slot; 16923cb98950SDavid Howells kfree(new_s); 16933cb98950SDavid Howells if (!new_parent) { 16943cb98950SDavid Howells new_root = assoc_array_node_to_ptr(new_n); 16953cb98950SDavid Howells goto gc_complete; 16963cb98950SDavid Howells } 16973cb98950SDavid Howells 16983cb98950SDavid Howells n = assoc_array_ptr_to_node(new_parent); 16993cb98950SDavid Howells n->slots[slot] = assoc_array_node_to_ptr(new_n); 17003cb98950SDavid Howells } 17013cb98950SDavid Howells } else { 17023cb98950SDavid Howells new_parent = ptr; 17033cb98950SDavid Howells } 17043cb98950SDavid Howells new_n = assoc_array_ptr_to_node(new_parent); 17053cb98950SDavid Howells 17063cb98950SDavid Howells ascend_old_tree: 17073cb98950SDavid Howells ptr = node->back_pointer; 17083cb98950SDavid Howells if (assoc_array_ptr_is_shortcut(ptr)) { 17093cb98950SDavid Howells shortcut = assoc_array_ptr_to_shortcut(ptr); 17103cb98950SDavid Howells slot = shortcut->parent_slot; 17113cb98950SDavid Howells cursor = shortcut->back_pointer; 171295389b08SDavid Howells if (!cursor) 171395389b08SDavid Howells goto gc_complete; 17143cb98950SDavid Howells } else { 17153cb98950SDavid Howells slot = node->parent_slot; 17163cb98950SDavid Howells cursor = ptr; 17173cb98950SDavid Howells } 171895389b08SDavid Howells BUG_ON(!cursor); 17193cb98950SDavid Howells node = assoc_array_ptr_to_node(cursor); 17203cb98950SDavid Howells slot++; 17213cb98950SDavid Howells goto continue_node; 17223cb98950SDavid Howells 17233cb98950SDavid Howells gc_complete: 17243cb98950SDavid Howells edit->set[0].to = new_root; 17253cb98950SDavid Howells assoc_array_apply_edit(edit); 172627419604SDavid Howells array->nr_leaves_on_tree = nr_leaves_on_tree; 17273cb98950SDavid Howells return 0; 17283cb98950SDavid Howells 17293cb98950SDavid Howells enomem: 17303cb98950SDavid Howells pr_devel("enomem\n"); 17313cb98950SDavid Howells assoc_array_destroy_subtree(new_root, edit->ops); 17323cb98950SDavid Howells kfree(edit); 17333cb98950SDavid Howells return -ENOMEM; 17343cb98950SDavid Howells } 1735