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