Path: blob/main/sys/compat/linuxkpi/common/src/linux_radix.c
102438 views
/*-1* Copyright (c) 2010 Isilon Systems, Inc.2* Copyright (c) 2010 iX Systems, Inc.3* Copyright (c) 2010 Panasas, Inc.4* Copyright (c) 2013-2020 Mellanox Technologies, Ltd.5* All rights reserved.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions9* are met:10* 1. Redistributions of source code must retain the above copyright11* notice unmodified, this list of conditions, and the following12* disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR18* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES19* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.20* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,21* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT22* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF26* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.27*/2829#include <sys/param.h>30#include <sys/systm.h>31#include <sys/malloc.h>32#include <sys/kernel.h>33#include <sys/sysctl.h>3435#include <linux/slab.h>36#include <linux/kernel.h>37#include <linux/radix-tree.h>38#include <linux/err.h>3940static MALLOC_DEFINE(M_RADIX, "radix", "Linux radix compat");4142static inline unsigned long43radix_max(const struct radix_tree_root *root)44{45return ((1UL << (root->height * RADIX_TREE_MAP_SHIFT)) - 1UL);46}4748static inline int49radix_pos(long id, int height)50{51return (id >> (RADIX_TREE_MAP_SHIFT * height)) & RADIX_TREE_MAP_MASK;52}5354static inline int55root_tag_get(const struct radix_tree_root *root, unsigned tag)56{57return (test_bit(tag, root->tags));58}5960static inline void61root_tag_set(struct radix_tree_root *root, unsigned tag)62{63set_bit(tag, root->tags);64}6566static inline void67root_tag_clear(struct radix_tree_root *root, unsigned tag)68{69clear_bit(tag, root->tags);70}7172static inline int73tag_get(const struct radix_tree_node *node, unsigned int tag, int pos)74{75return (test_bit(pos, node->tags[tag]));76}7778static inline void79tag_set(struct radix_tree_node *node, unsigned int tag, int pos)80{81set_bit(pos, node->tags[tag]);82}8384static inline void85tag_clear(struct radix_tree_node *node, unsigned int tag, int pos)86{87clear_bit(pos, node->tags[tag]);88}8990static inline bool91any_tag_set(const struct radix_tree_node *node, unsigned int tag)92{93unsigned int pos;9495for (pos = 0; pos < RADIX_TREE_TAG_LONGS; pos++)96if (node->tags[tag][pos])97return (true);9899return (false);100}101102static void103node_tag_clear(struct radix_tree_root *root, struct radix_tree_node *stack[],104unsigned long index, unsigned int tag)105{106struct radix_tree_node *node;107int height, pos;108109height = 1;110node = stack[height];111112while (node && height <= root->height - 1) {113pos = radix_pos(index, height);114if (!tag_get(node, tag, pos))115return;116117tag_clear(node, tag, pos);118if (any_tag_set(node, tag))119return;120121height++;122node = stack[height];123}124125if (root_tag_get(root, tag))126root_tag_clear(root, tag);127}128129static void130radix_tree_clean_root_node(struct radix_tree_root *root)131{132/* Check if the root node should be freed */133if (root->rnode->count == 0) {134free(root->rnode, M_RADIX);135root->rnode = NULL;136root->height = 0;137}138}139140void *141radix_tree_lookup(const struct radix_tree_root *root, unsigned long index)142{143struct radix_tree_node *node;144void *item;145int height;146147item = NULL;148node = root->rnode;149height = root->height - 1;150if (index > radix_max(root))151goto out;152while (height && node)153node = node->slots[radix_pos(index, height--)];154if (node)155item = node->slots[radix_pos(index, 0)];156157out:158return (item);159}160161unsigned int162radix_tree_gang_lookup(const struct radix_tree_root *root, void **results,163unsigned long first_index, unsigned int max_items)164{165struct radix_tree_iter iter;166void **slot;167int count;168169count = 0;170if (max_items == 0)171return (count);172173radix_tree_for_each_slot(slot, root, &iter, first_index) {174results[count] = *slot;175if (results[count] == NULL)176continue;177178count++;179if (count == max_items)180break;181}182183return (count);184}185186unsigned int187radix_tree_gang_lookup_tag(const struct radix_tree_root *root,188void **results, unsigned long first_index, unsigned int max_items,189unsigned int tag)190{191struct radix_tree_iter iter;192void **slot;193int count;194195count = 0;196if (max_items == 0)197return (count);198199radix_tree_for_each_slot_tagged(slot, root, &iter, first_index, tag) {200results[count] = *slot;201if (results[count] == NULL)202continue;203204count++;205if (count == max_items)206break;207}208209return (count);210}211212bool213radix_tree_iter_find(const struct radix_tree_root *root,214struct radix_tree_iter *iter, void ***pppslot, int flags)215{216struct radix_tree_node *node;217unsigned long index = iter->index;218unsigned int tag;219int height;220221tag = flags & RADIX_TREE_ITER_TAG_MASK;222if ((flags & RADIX_TREE_ITER_TAGGED) && !root_tag_get(root, tag))223return (false);224225restart:226node = root->rnode;227if (node == NULL)228return (false);229height = root->height - 1;230if (height == -1 || index > radix_max(root))231return (false);232do {233unsigned long mask = RADIX_TREE_MAP_MASK << (RADIX_TREE_MAP_SHIFT * height);234unsigned long step = 1UL << (RADIX_TREE_MAP_SHIFT * height);235int pos = radix_pos(index, height);236struct radix_tree_node *next;237238/* track last slot */239*pppslot = node->slots + pos;240241next = node->slots[pos];242if (next == NULL ||243(flags & RADIX_TREE_ITER_TAGGED &&244!tag_get(next, tag, pos))) {245index += step;246index &= -step;247if ((index & mask) == 0)248goto restart;249} else {250node = next;251height--;252}253} while (height != -1);254iter->index = index;255return (true);256}257258void *259radix_tree_delete(struct radix_tree_root *root, unsigned long index)260{261struct radix_tree_node *stack[RADIX_TREE_MAX_HEIGHT];262struct radix_tree_node *node;263void *item;264int height;265int idx;266int tag;267268item = NULL;269node = root->rnode;270height = root->height - 1;271if (index > radix_max(root))272goto out;273/*274* Find the node and record the path in stack.275*/276while (height && node) {277stack[height] = node;278node = node->slots[radix_pos(index, height--)];279}280281if (node) {282idx = radix_pos(index, 0);283item = node->slots[idx];284285for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++)286node_tag_clear(root, stack, index, tag);287}288289/*290* If we removed something reduce the height of the tree.291*/292if (item)293for (;;) {294node->slots[idx] = NULL;295node->count--;296if (node->count > 0)297break;298free(node, M_RADIX);299if (node == root->rnode) {300root->rnode = NULL;301root->height = 0;302break;303}304height++;305node = stack[height];306idx = radix_pos(index, height);307}308out:309return (item);310}311312void313radix_tree_iter_delete(struct radix_tree_root *root,314struct radix_tree_iter *iter, void **slot)315{316radix_tree_delete(root, iter->index);317}318319int320radix_tree_insert(struct radix_tree_root *root, unsigned long index, void *item)321{322struct radix_tree_node *node;323struct radix_tree_node *temp[RADIX_TREE_MAX_HEIGHT - 1];324int height;325int idx;326327/* bail out upon insertion of a NULL item */328if (item == NULL)329return (-EINVAL);330331/* get root node, if any */332node = root->rnode;333334/* allocate root node, if any */335if (node == NULL) {336node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);337if (node == NULL)338return (-ENOMEM);339root->rnode = node;340root->height++;341}342343/* expand radix tree as needed */344while (radix_max(root) < index) {345/* check if the radix tree is getting too big */346if (root->height == RADIX_TREE_MAX_HEIGHT) {347radix_tree_clean_root_node(root);348return (-E2BIG);349}350351/*352* If the root radix level is not empty, we need to353* allocate a new radix level:354*/355if (node->count != 0) {356node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);357if (node == NULL) {358/*359* Freeing the already allocated radix360* levels, if any, will be handled by361* the radix_tree_delete() function.362* This code path can only happen when363* the tree is not empty.364*/365return (-ENOMEM);366}367node->slots[0] = root->rnode;368node->count++;369root->rnode = node;370}371root->height++;372}373374/* get radix tree height index */375height = root->height - 1;376377/* walk down the tree until the first missing node, if any */378for ( ; height != 0; height--) {379idx = radix_pos(index, height);380if (node->slots[idx] == NULL)381break;382node = node->slots[idx];383}384385/* allocate the missing radix levels, if any */386for (idx = 0; idx != height; idx++) {387temp[idx] = malloc(sizeof(*node), M_RADIX,388root->gfp_mask | M_ZERO);389if (temp[idx] == NULL) {390while (idx--)391free(temp[idx], M_RADIX);392radix_tree_clean_root_node(root);393return (-ENOMEM);394}395}396397/* setup new radix levels, if any */398for ( ; height != 0; height--) {399idx = radix_pos(index, height);400node->slots[idx] = temp[height - 1];401node->count++;402node = node->slots[idx];403}404405/*406* Insert and adjust count if the item does not already exist.407*/408idx = radix_pos(index, 0);409if (node->slots[idx])410return (-EEXIST);411node->slots[idx] = item;412node->count++;413414return (0);415}416417int418radix_tree_store(struct radix_tree_root *root, unsigned long index, void **ppitem)419{420struct radix_tree_node *node;421struct radix_tree_node *temp[RADIX_TREE_MAX_HEIGHT - 1];422void *pitem;423int height;424int idx;425426/*427* Inserting a NULL item means delete it. The old pointer is428* stored at the location pointed to by "ppitem".429*/430if (*ppitem == NULL) {431*ppitem = radix_tree_delete(root, index);432return (0);433}434435/* get root node, if any */436node = root->rnode;437438/* allocate root node, if any */439if (node == NULL) {440node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);441if (node == NULL)442return (-ENOMEM);443root->rnode = node;444root->height++;445}446447/* expand radix tree as needed */448while (radix_max(root) < index) {449/* check if the radix tree is getting too big */450if (root->height == RADIX_TREE_MAX_HEIGHT) {451radix_tree_clean_root_node(root);452return (-E2BIG);453}454455/*456* If the root radix level is not empty, we need to457* allocate a new radix level:458*/459if (node->count != 0) {460node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);461if (node == NULL) {462/*463* Freeing the already allocated radix464* levels, if any, will be handled by465* the radix_tree_delete() function.466* This code path can only happen when467* the tree is not empty.468*/469return (-ENOMEM);470}471node->slots[0] = root->rnode;472node->count++;473root->rnode = node;474}475root->height++;476}477478/* get radix tree height index */479height = root->height - 1;480481/* walk down the tree until the first missing node, if any */482for ( ; height != 0; height--) {483idx = radix_pos(index, height);484if (node->slots[idx] == NULL)485break;486node = node->slots[idx];487}488489/* allocate the missing radix levels, if any */490for (idx = 0; idx != height; idx++) {491temp[idx] = malloc(sizeof(*node), M_RADIX,492root->gfp_mask | M_ZERO);493if (temp[idx] == NULL) {494while (idx--)495free(temp[idx], M_RADIX);496radix_tree_clean_root_node(root);497return (-ENOMEM);498}499}500501/* setup new radix levels, if any */502for ( ; height != 0; height--) {503idx = radix_pos(index, height);504node->slots[idx] = temp[height - 1];505node->count++;506node = node->slots[idx];507}508509/*510* Insert and adjust count if the item does not already exist.511*/512idx = radix_pos(index, 0);513/* swap */514pitem = node->slots[idx];515node->slots[idx] = *ppitem;516*ppitem = pitem;517518if (pitem == NULL)519node->count++;520return (0);521}522523void *524radix_tree_tag_set(struct radix_tree_root *root, unsigned long index,525unsigned int tag)526{527struct radix_tree_node *node;528void *item;529int height, pos;530531item = NULL;532node = root->rnode;533height = root->height - 1;534if (index > radix_max(root))535goto out;536537while (height) {538KASSERT(539node != NULL,540("Node at index %lu does not exist in radix tree %p",541index, root));542543pos = radix_pos(index, height--);544node = node->slots[pos];545546if (!tag_get(node, tag, pos))547tag_set(node, tag, pos);548}549550item = node->slots[radix_pos(index, 0)];551552root_tag_set(root, tag);553554out:555return (item);556}557558void *559radix_tree_tag_clear(struct radix_tree_root *root,560unsigned long index, unsigned int tag)561{562struct radix_tree_node *stack[RADIX_TREE_MAX_HEIGHT];563struct radix_tree_node *node;564void *item;565int height;566567item = NULL;568node = root->rnode;569height = root->height - 1;570if (index > radix_max(root))571goto out;572573while (height && node) {574stack[height] = node;575node = node->slots[radix_pos(index, height--)];576}577578if (node) {579item = node->slots[radix_pos(index, 0)];580581node_tag_clear(root, stack, index, tag);582}583584out:585return (item);586}587588int589radix_tree_tagged(const struct radix_tree_root *root, unsigned int tag)590{591return (root_tag_get(root, tag));592}593594595