Path: blob/main/sys/contrib/openzfs/lib/libuutil/uu_avl.c
48375 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/21/*22* Copyright 2008 Sun Microsystems, Inc. All rights reserved.23* Use is subject to license terms.24*/25262728#include "libuutil_common.h"2930#include <stdlib.h>31#include <string.h>32#include <unistd.h>33#include <sys/avl.h>3435static uu_avl_pool_t uu_null_apool = { &uu_null_apool, &uu_null_apool };36static pthread_mutex_t uu_apool_list_lock = PTHREAD_MUTEX_INITIALIZER;3738/*39* The index mark change on every insert and delete, to catch stale40* references.41*42* We leave the low bit alone, since the avl code uses it.43*/44#define INDEX_MAX (sizeof (uintptr_t) - 2)45#define INDEX_NEXT(m) (((m) == INDEX_MAX)? 2 : ((m) + 2) & INDEX_MAX)4647#define INDEX_DECODE(i) ((i) & ~INDEX_MAX)48#define INDEX_ENCODE(p, n) (((n) & ~INDEX_MAX) | (p)->ua_index)49#define INDEX_VALID(p, i) (((i) & INDEX_MAX) == (p)->ua_index)50#define INDEX_CHECK(i) (((i) & INDEX_MAX) != 0)5152/*53* When an element is inactive (not in a tree), we keep a marked pointer to54* its containing pool in its first word, and a NULL pointer in its second.55*56* On insert, we use these to verify that it comes from the correct pool.57*/58#define NODE_ARRAY(p, n) ((uintptr_t *)((uintptr_t)(n) + \59(pp)->uap_nodeoffset))6061#define POOL_TO_MARKER(pp) (((uintptr_t)(pp) | 1))6263#define DEAD_MARKER 0xc46465uu_avl_pool_t *66uu_avl_pool_create(const char *name, size_t objsize, size_t nodeoffset,67uu_compare_fn_t *compare_func, uint32_t flags)68{69uu_avl_pool_t *pp, *next, *prev;7071if (name == NULL ||72uu_check_name(name, UU_NAME_DOMAIN) == -1 ||73nodeoffset + sizeof (uu_avl_node_t) > objsize ||74compare_func == NULL) {75uu_set_error(UU_ERROR_INVALID_ARGUMENT);76return (NULL);77}7879if (flags & ~UU_AVL_POOL_DEBUG) {80uu_set_error(UU_ERROR_UNKNOWN_FLAG);81return (NULL);82}8384pp = uu_zalloc(sizeof (uu_avl_pool_t));85if (pp == NULL) {86uu_set_error(UU_ERROR_NO_MEMORY);87return (NULL);88}8990(void) strlcpy(pp->uap_name, name, sizeof (pp->uap_name));91pp->uap_nodeoffset = nodeoffset;92pp->uap_objsize = objsize;93pp->uap_cmp = compare_func;94if (flags & UU_AVL_POOL_DEBUG)95pp->uap_debug = 1;96pp->uap_last_index = 0;9798(void) pthread_mutex_init(&pp->uap_lock, NULL);99100pp->uap_null_avl.ua_next = &pp->uap_null_avl;101pp->uap_null_avl.ua_prev = &pp->uap_null_avl;102103(void) pthread_mutex_lock(&uu_apool_list_lock);104pp->uap_next = next = &uu_null_apool;105pp->uap_prev = prev = next->uap_prev;106next->uap_prev = pp;107prev->uap_next = pp;108(void) pthread_mutex_unlock(&uu_apool_list_lock);109110return (pp);111}112113void114uu_avl_pool_destroy(uu_avl_pool_t *pp)115{116if (pp->uap_debug) {117if (pp->uap_null_avl.ua_next != &pp->uap_null_avl ||118pp->uap_null_avl.ua_prev != &pp->uap_null_avl) {119uu_panic("uu_avl_pool_destroy: Pool \"%.*s\" (%p) has "120"outstanding avls, or is corrupt.\n",121(int)sizeof (pp->uap_name), pp->uap_name,122(void *)pp);123}124}125(void) pthread_mutex_lock(&uu_apool_list_lock);126pp->uap_next->uap_prev = pp->uap_prev;127pp->uap_prev->uap_next = pp->uap_next;128(void) pthread_mutex_unlock(&uu_apool_list_lock);129(void) pthread_mutex_destroy(&pp->uap_lock);130pp->uap_prev = NULL;131pp->uap_next = NULL;132uu_free(pp);133}134135void136uu_avl_node_init(void *base, uu_avl_node_t *np, uu_avl_pool_t *pp)137{138uintptr_t *na = (uintptr_t *)np;139140if (pp->uap_debug) {141uintptr_t offset = (uintptr_t)np - (uintptr_t)base;142if (offset + sizeof (*np) > pp->uap_objsize) {143uu_panic("uu_avl_node_init(%p, %p, %p (\"%s\")): "144"offset %ld doesn't fit in object (size %ld)\n",145base, (void *)np, (void *)pp, pp->uap_name,146(long)offset, (long)pp->uap_objsize);147}148if (offset != pp->uap_nodeoffset) {149uu_panic("uu_avl_node_init(%p, %p, %p (\"%s\")): "150"offset %ld doesn't match pool's offset (%ld)\n",151base, (void *)np, (void *)pp, pp->uap_name,152(long)offset, (long)pp->uap_objsize);153}154}155156na[0] = POOL_TO_MARKER(pp);157na[1] = 0;158}159160void161uu_avl_node_fini(void *base, uu_avl_node_t *np, uu_avl_pool_t *pp)162{163uintptr_t *na = (uintptr_t *)np;164165if (pp->uap_debug) {166if (na[0] == DEAD_MARKER && na[1] == DEAD_MARKER) {167uu_panic("uu_avl_node_fini(%p, %p, %p (\"%s\")): "168"node already finied\n",169base, (void *)np, (void *)pp, pp->uap_name);170}171if (na[0] != POOL_TO_MARKER(pp) || na[1] != 0) {172uu_panic("uu_avl_node_fini(%p, %p, %p (\"%s\")): "173"node corrupt, in tree, or in different pool\n",174base, (void *)np, (void *)pp, pp->uap_name);175}176}177178na[0] = DEAD_MARKER;179na[1] = DEAD_MARKER;180na[2] = DEAD_MARKER;181}182183struct uu_avl_node_compare_info {184uu_compare_fn_t *ac_compare;185void *ac_private;186void *ac_right;187void *ac_found;188};189190static int191uu_avl_node_compare(const void *l, const void *r)192{193struct uu_avl_node_compare_info *info =194(struct uu_avl_node_compare_info *)l;195196int res = info->ac_compare(r, info->ac_right, info->ac_private);197198if (res == 0) {199if (info->ac_found == NULL)200info->ac_found = (void *)r;201return (-1);202}203if (res < 0)204return (1);205return (-1);206}207208uu_avl_t *209uu_avl_create(uu_avl_pool_t *pp, void *parent, uint32_t flags)210{211uu_avl_t *ap, *next, *prev;212213if (flags & ~UU_AVL_DEBUG) {214uu_set_error(UU_ERROR_UNKNOWN_FLAG);215return (NULL);216}217218ap = uu_zalloc(sizeof (*ap));219if (ap == NULL) {220uu_set_error(UU_ERROR_NO_MEMORY);221return (NULL);222}223224ap->ua_pool = pp;225ap->ua_parent = parent;226ap->ua_debug = pp->uap_debug || (flags & UU_AVL_DEBUG);227ap->ua_index = (pp->uap_last_index = INDEX_NEXT(pp->uap_last_index));228229avl_create(&ap->ua_tree, &uu_avl_node_compare, pp->uap_objsize,230pp->uap_nodeoffset);231232ap->ua_null_walk.uaw_next = &ap->ua_null_walk;233ap->ua_null_walk.uaw_prev = &ap->ua_null_walk;234235(void) pthread_mutex_lock(&pp->uap_lock);236next = &pp->uap_null_avl;237prev = next->ua_prev;238ap->ua_next = next;239ap->ua_prev = prev;240next->ua_prev = ap;241prev->ua_next = ap;242(void) pthread_mutex_unlock(&pp->uap_lock);243244return (ap);245}246247void248uu_avl_destroy(uu_avl_t *ap)249{250uu_avl_pool_t *pp = ap->ua_pool;251252if (ap->ua_debug) {253if (avl_numnodes(&ap->ua_tree) != 0) {254uu_panic("uu_avl_destroy(%p): tree not empty\n",255(void *)ap);256}257if (ap->ua_null_walk.uaw_next != &ap->ua_null_walk ||258ap->ua_null_walk.uaw_prev != &ap->ua_null_walk) {259uu_panic("uu_avl_destroy(%p): outstanding walkers\n",260(void *)ap);261}262}263(void) pthread_mutex_lock(&pp->uap_lock);264ap->ua_next->ua_prev = ap->ua_prev;265ap->ua_prev->ua_next = ap->ua_next;266(void) pthread_mutex_unlock(&pp->uap_lock);267ap->ua_prev = NULL;268ap->ua_next = NULL;269270ap->ua_pool = NULL;271avl_destroy(&ap->ua_tree);272273uu_free(ap);274}275276size_t277uu_avl_numnodes(uu_avl_t *ap)278{279return (avl_numnodes(&ap->ua_tree));280}281282void *283uu_avl_first(uu_avl_t *ap)284{285return (avl_first(&ap->ua_tree));286}287288void *289uu_avl_last(uu_avl_t *ap)290{291return (avl_last(&ap->ua_tree));292}293294void *295uu_avl_next(uu_avl_t *ap, void *node)296{297return (AVL_NEXT(&ap->ua_tree, node));298}299300void *301uu_avl_prev(uu_avl_t *ap, void *node)302{303return (AVL_PREV(&ap->ua_tree, node));304}305306static void307_avl_walk_init(uu_avl_walk_t *wp, uu_avl_t *ap, uint32_t flags)308{309uu_avl_walk_t *next, *prev;310311int robust = (flags & UU_WALK_ROBUST);312int direction = (flags & UU_WALK_REVERSE)? -1 : 1;313314(void) memset(wp, 0, sizeof (*wp));315wp->uaw_avl = ap;316wp->uaw_robust = robust;317wp->uaw_dir = direction;318319if (direction > 0)320wp->uaw_next_result = avl_first(&ap->ua_tree);321else322wp->uaw_next_result = avl_last(&ap->ua_tree);323324if (ap->ua_debug || robust) {325wp->uaw_next = next = &ap->ua_null_walk;326wp->uaw_prev = prev = next->uaw_prev;327next->uaw_prev = wp;328prev->uaw_next = wp;329}330}331332static void *333_avl_walk_advance(uu_avl_walk_t *wp, uu_avl_t *ap)334{335void *np = wp->uaw_next_result;336337avl_tree_t *t = &ap->ua_tree;338339if (np == NULL)340return (NULL);341342wp->uaw_next_result = (wp->uaw_dir > 0)? AVL_NEXT(t, np) :343AVL_PREV(t, np);344345return (np);346}347348static void349_avl_walk_fini(uu_avl_walk_t *wp)350{351if (wp->uaw_next != NULL) {352wp->uaw_next->uaw_prev = wp->uaw_prev;353wp->uaw_prev->uaw_next = wp->uaw_next;354wp->uaw_next = NULL;355wp->uaw_prev = NULL;356}357wp->uaw_avl = NULL;358wp->uaw_next_result = NULL;359}360361uu_avl_walk_t *362uu_avl_walk_start(uu_avl_t *ap, uint32_t flags)363{364uu_avl_walk_t *wp;365366if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {367uu_set_error(UU_ERROR_UNKNOWN_FLAG);368return (NULL);369}370371wp = uu_zalloc(sizeof (*wp));372if (wp == NULL) {373uu_set_error(UU_ERROR_NO_MEMORY);374return (NULL);375}376377_avl_walk_init(wp, ap, flags);378return (wp);379}380381void *382uu_avl_walk_next(uu_avl_walk_t *wp)383{384return (_avl_walk_advance(wp, wp->uaw_avl));385}386387void388uu_avl_walk_end(uu_avl_walk_t *wp)389{390_avl_walk_fini(wp);391uu_free(wp);392}393394int395uu_avl_walk(uu_avl_t *ap, uu_walk_fn_t *func, void *private, uint32_t flags)396{397void *e;398uu_avl_walk_t my_walk;399400int status = UU_WALK_NEXT;401402if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {403uu_set_error(UU_ERROR_UNKNOWN_FLAG);404return (-1);405}406407_avl_walk_init(&my_walk, ap, flags);408while (status == UU_WALK_NEXT &&409(e = _avl_walk_advance(&my_walk, ap)) != NULL)410status = (*func)(e, private);411_avl_walk_fini(&my_walk);412413if (status >= 0)414return (0);415uu_set_error(UU_ERROR_CALLBACK_FAILED);416return (-1);417}418419void420uu_avl_remove(uu_avl_t *ap, void *elem)421{422uu_avl_walk_t *wp;423uu_avl_pool_t *pp = ap->ua_pool;424uintptr_t *na = NODE_ARRAY(pp, elem);425426if (ap->ua_debug) {427/*428* invalidate outstanding uu_avl_index_ts.429*/430ap->ua_index = INDEX_NEXT(ap->ua_index);431}432433/*434* Robust walkers most be advanced, if we are removing the node435* they are currently using. In debug mode, non-robust walkers436* are also on the walker list.437*/438for (wp = ap->ua_null_walk.uaw_next; wp != &ap->ua_null_walk;439wp = wp->uaw_next) {440if (wp->uaw_robust) {441if (elem == wp->uaw_next_result)442(void) _avl_walk_advance(wp, ap);443} else if (wp->uaw_next_result != NULL) {444uu_panic("uu_avl_remove(%p, %p): active non-robust "445"walker\n", (void *)ap, elem);446}447}448449avl_remove(&ap->ua_tree, elem);450451na[0] = POOL_TO_MARKER(pp);452na[1] = 0;453}454455void *456uu_avl_teardown(uu_avl_t *ap, void **cookie)457{458void *elem = avl_destroy_nodes(&ap->ua_tree, cookie);459460if (elem != NULL) {461uu_avl_pool_t *pp = ap->ua_pool;462uintptr_t *na = NODE_ARRAY(pp, elem);463464na[0] = POOL_TO_MARKER(pp);465na[1] = 0;466}467return (elem);468}469470void *471uu_avl_find(uu_avl_t *ap, void *elem, void *private, uu_avl_index_t *out)472{473struct uu_avl_node_compare_info info;474void *result;475476info.ac_compare = ap->ua_pool->uap_cmp;477info.ac_private = private;478info.ac_right = elem;479info.ac_found = NULL;480481result = avl_find(&ap->ua_tree, &info, out);482if (out != NULL)483*out = INDEX_ENCODE(ap, *out);484485if (ap->ua_debug && result != NULL)486uu_panic("uu_avl_find: internal error: avl_find succeeded\n");487488return (info.ac_found);489}490491void492uu_avl_insert(uu_avl_t *ap, void *elem, uu_avl_index_t idx)493{494if (ap->ua_debug) {495uu_avl_pool_t *pp = ap->ua_pool;496uintptr_t *na = NODE_ARRAY(pp, elem);497498if (na[1] != 0)499uu_panic("uu_avl_insert(%p, %p, %p): node already "500"in tree, or corrupt\n",501(void *)ap, elem, (void *)idx);502if (na[0] == 0)503uu_panic("uu_avl_insert(%p, %p, %p): node not "504"initialized\n",505(void *)ap, elem, (void *)idx);506if (na[0] != POOL_TO_MARKER(pp))507uu_panic("uu_avl_insert(%p, %p, %p): node from "508"other pool, or corrupt\n",509(void *)ap, elem, (void *)idx);510511if (!INDEX_VALID(ap, idx))512uu_panic("uu_avl_insert(%p, %p, %p): %s\n",513(void *)ap, elem, (void *)idx,514INDEX_CHECK(idx)? "outdated index" :515"invalid index");516517/*518* invalidate outstanding uu_avl_index_ts.519*/520ap->ua_index = INDEX_NEXT(ap->ua_index);521}522avl_insert(&ap->ua_tree, elem, INDEX_DECODE(idx));523}524525void *526uu_avl_nearest_next(uu_avl_t *ap, uu_avl_index_t idx)527{528if (ap->ua_debug && !INDEX_VALID(ap, idx))529uu_panic("uu_avl_nearest_next(%p, %p): %s\n",530(void *)ap, (void *)idx, INDEX_CHECK(idx)?531"outdated index" : "invalid index");532return (avl_nearest(&ap->ua_tree, INDEX_DECODE(idx), AVL_AFTER));533}534535void *536uu_avl_nearest_prev(uu_avl_t *ap, uu_avl_index_t idx)537{538if (ap->ua_debug && !INDEX_VALID(ap, idx))539uu_panic("uu_avl_nearest_prev(%p, %p): %s\n",540(void *)ap, (void *)idx, INDEX_CHECK(idx)?541"outdated index" : "invalid index");542return (avl_nearest(&ap->ua_tree, INDEX_DECODE(idx), AVL_BEFORE));543}544545/*546* called from uu_lockup() and uu_release(), as part of our fork1()-safety.547*/548void549uu_avl_lockup(void)550{551uu_avl_pool_t *pp;552553(void) pthread_mutex_lock(&uu_apool_list_lock);554for (pp = uu_null_apool.uap_next; pp != &uu_null_apool;555pp = pp->uap_next)556(void) pthread_mutex_lock(&pp->uap_lock);557}558559void560uu_avl_release(void)561{562uu_avl_pool_t *pp;563564for (pp = uu_null_apool.uap_next; pp != &uu_null_apool;565pp = pp->uap_next)566(void) pthread_mutex_unlock(&pp->uap_lock);567(void) pthread_mutex_unlock(&uu_apool_list_lock);568}569570571