Path: blob/main/sys/compat/linuxkpi/common/src/linux_radix.c
39586 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(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 void55radix_tree_clean_root_node(struct radix_tree_root *root)56{57/* Check if the root node should be freed */58if (root->rnode->count == 0) {59free(root->rnode, M_RADIX);60root->rnode = NULL;61root->height = 0;62}63}6465void *66radix_tree_lookup(struct radix_tree_root *root, unsigned long index)67{68struct radix_tree_node *node;69void *item;70int height;7172item = NULL;73node = root->rnode;74height = root->height - 1;75if (index > radix_max(root))76goto out;77while (height && node)78node = node->slots[radix_pos(index, height--)];79if (node)80item = node->slots[radix_pos(index, 0)];8182out:83return (item);84}8586bool87radix_tree_iter_find(struct radix_tree_root *root, struct radix_tree_iter *iter,88void ***pppslot)89{90struct radix_tree_node *node;91unsigned long index = iter->index;92int height;9394restart:95node = root->rnode;96if (node == NULL)97return (false);98height = root->height - 1;99if (height == -1 || index > radix_max(root))100return (false);101do {102unsigned long mask = RADIX_TREE_MAP_MASK << (RADIX_TREE_MAP_SHIFT * height);103unsigned long step = 1UL << (RADIX_TREE_MAP_SHIFT * height);104int pos = radix_pos(index, height);105struct radix_tree_node *next;106107/* track last slot */108*pppslot = node->slots + pos;109110next = node->slots[pos];111if (next == NULL) {112index += step;113index &= -step;114if ((index & mask) == 0)115goto restart;116} else {117node = next;118height--;119}120} while (height != -1);121iter->index = index;122return (true);123}124125void *126radix_tree_delete(struct radix_tree_root *root, unsigned long index)127{128struct radix_tree_node *stack[RADIX_TREE_MAX_HEIGHT];129struct radix_tree_node *node;130void *item;131int height;132int idx;133134item = NULL;135node = root->rnode;136height = root->height - 1;137if (index > radix_max(root))138goto out;139/*140* Find the node and record the path in stack.141*/142while (height && node) {143stack[height] = node;144node = node->slots[radix_pos(index, height--)];145}146idx = radix_pos(index, 0);147if (node)148item = node->slots[idx];149/*150* If we removed something reduce the height of the tree.151*/152if (item)153for (;;) {154node->slots[idx] = NULL;155node->count--;156if (node->count > 0)157break;158free(node, M_RADIX);159if (node == root->rnode) {160root->rnode = NULL;161root->height = 0;162break;163}164height++;165node = stack[height];166idx = radix_pos(index, height);167}168out:169return (item);170}171172void173radix_tree_iter_delete(struct radix_tree_root *root,174struct radix_tree_iter *iter, void **slot)175{176radix_tree_delete(root, iter->index);177}178179int180radix_tree_insert(struct radix_tree_root *root, unsigned long index, void *item)181{182struct radix_tree_node *node;183struct radix_tree_node *temp[RADIX_TREE_MAX_HEIGHT - 1];184int height;185int idx;186187/* bail out upon insertion of a NULL item */188if (item == NULL)189return (-EINVAL);190191/* get root node, if any */192node = root->rnode;193194/* allocate root node, if any */195if (node == NULL) {196node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);197if (node == NULL)198return (-ENOMEM);199root->rnode = node;200root->height++;201}202203/* expand radix tree as needed */204while (radix_max(root) < index) {205/* check if the radix tree is getting too big */206if (root->height == RADIX_TREE_MAX_HEIGHT) {207radix_tree_clean_root_node(root);208return (-E2BIG);209}210211/*212* If the root radix level is not empty, we need to213* allocate a new radix level:214*/215if (node->count != 0) {216node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);217if (node == NULL) {218/*219* Freeing the already allocated radix220* levels, if any, will be handled by221* the radix_tree_delete() function.222* This code path can only happen when223* the tree is not empty.224*/225return (-ENOMEM);226}227node->slots[0] = root->rnode;228node->count++;229root->rnode = node;230}231root->height++;232}233234/* get radix tree height index */235height = root->height - 1;236237/* walk down the tree until the first missing node, if any */238for ( ; height != 0; height--) {239idx = radix_pos(index, height);240if (node->slots[idx] == NULL)241break;242node = node->slots[idx];243}244245/* allocate the missing radix levels, if any */246for (idx = 0; idx != height; idx++) {247temp[idx] = malloc(sizeof(*node), M_RADIX,248root->gfp_mask | M_ZERO);249if (temp[idx] == NULL) {250while (idx--)251free(temp[idx], M_RADIX);252radix_tree_clean_root_node(root);253return (-ENOMEM);254}255}256257/* setup new radix levels, if any */258for ( ; height != 0; height--) {259idx = radix_pos(index, height);260node->slots[idx] = temp[height - 1];261node->count++;262node = node->slots[idx];263}264265/*266* Insert and adjust count if the item does not already exist.267*/268idx = radix_pos(index, 0);269if (node->slots[idx])270return (-EEXIST);271node->slots[idx] = item;272node->count++;273274return (0);275}276277int278radix_tree_store(struct radix_tree_root *root, unsigned long index, void **ppitem)279{280struct radix_tree_node *node;281struct radix_tree_node *temp[RADIX_TREE_MAX_HEIGHT - 1];282void *pitem;283int height;284int idx;285286/*287* Inserting a NULL item means delete it. The old pointer is288* stored at the location pointed to by "ppitem".289*/290if (*ppitem == NULL) {291*ppitem = radix_tree_delete(root, index);292return (0);293}294295/* get root node, if any */296node = root->rnode;297298/* allocate root node, if any */299if (node == NULL) {300node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);301if (node == NULL)302return (-ENOMEM);303root->rnode = node;304root->height++;305}306307/* expand radix tree as needed */308while (radix_max(root) < index) {309/* check if the radix tree is getting too big */310if (root->height == RADIX_TREE_MAX_HEIGHT) {311radix_tree_clean_root_node(root);312return (-E2BIG);313}314315/*316* If the root radix level is not empty, we need to317* allocate a new radix level:318*/319if (node->count != 0) {320node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);321if (node == NULL) {322/*323* Freeing the already allocated radix324* levels, if any, will be handled by325* the radix_tree_delete() function.326* This code path can only happen when327* the tree is not empty.328*/329return (-ENOMEM);330}331node->slots[0] = root->rnode;332node->count++;333root->rnode = node;334}335root->height++;336}337338/* get radix tree height index */339height = root->height - 1;340341/* walk down the tree until the first missing node, if any */342for ( ; height != 0; height--) {343idx = radix_pos(index, height);344if (node->slots[idx] == NULL)345break;346node = node->slots[idx];347}348349/* allocate the missing radix levels, if any */350for (idx = 0; idx != height; idx++) {351temp[idx] = malloc(sizeof(*node), M_RADIX,352root->gfp_mask | M_ZERO);353if (temp[idx] == NULL) {354while (idx--)355free(temp[idx], M_RADIX);356radix_tree_clean_root_node(root);357return (-ENOMEM);358}359}360361/* setup new radix levels, if any */362for ( ; height != 0; height--) {363idx = radix_pos(index, height);364node->slots[idx] = temp[height - 1];365node->count++;366node = node->slots[idx];367}368369/*370* Insert and adjust count if the item does not already exist.371*/372idx = radix_pos(index, 0);373/* swap */374pitem = node->slots[idx];375node->slots[idx] = *ppitem;376*ppitem = pitem;377378if (pitem == NULL)379node->count++;380return (0);381}382383384