Path: blob/main/sys/contrib/openzfs/lib/libuutil/uu_list.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/time.h>3435#define ELEM_TO_NODE(lp, e) \36((uu_list_node_impl_t *)((uintptr_t)(e) + (lp)->ul_offset))3738#define NODE_TO_ELEM(lp, n) \39((void *)((uintptr_t)(n) - (lp)->ul_offset))4041/*42* uu_list_index_ts define a location for insertion. They are simply a43* pointer to the object after the insertion point. We store a mark44* in the low-bits of the index, to help prevent mistakes.45*46* When debugging, the index mark changes on every insert and delete, to47* catch stale references.48*/49#define INDEX_MAX (sizeof (uintptr_t) - 1)50#define INDEX_NEXT(m) (((m) == INDEX_MAX)? 1 : ((m) + 1) & INDEX_MAX)5152#define INDEX_TO_NODE(i) ((uu_list_node_impl_t *)((i) & ~INDEX_MAX))53#define NODE_TO_INDEX(p, n) (((uintptr_t)(n) & ~INDEX_MAX) | (p)->ul_index)54#define INDEX_VALID(p, i) (((i) & INDEX_MAX) == (p)->ul_index)55#define INDEX_CHECK(i) (((i) & INDEX_MAX) != 0)5657#define POOL_TO_MARKER(pp) ((void *)((uintptr_t)(pp) | 1))5859static uu_list_pool_t uu_null_lpool = { &uu_null_lpool, &uu_null_lpool };60static pthread_mutex_t uu_lpool_list_lock = PTHREAD_MUTEX_INITIALIZER;6162uu_list_pool_t *63uu_list_pool_create(const char *name, size_t objsize,64size_t nodeoffset, uu_compare_fn_t *compare_func, uint32_t flags)65{66uu_list_pool_t *pp, *next, *prev;6768if (name == NULL ||69uu_check_name(name, UU_NAME_DOMAIN) == -1 ||70nodeoffset + sizeof (uu_list_node_t) > objsize) {71uu_set_error(UU_ERROR_INVALID_ARGUMENT);72return (NULL);73}7475if (flags & ~UU_LIST_POOL_DEBUG) {76uu_set_error(UU_ERROR_UNKNOWN_FLAG);77return (NULL);78}7980pp = uu_zalloc(sizeof (uu_list_pool_t));81if (pp == NULL) {82uu_set_error(UU_ERROR_NO_MEMORY);83return (NULL);84}8586(void) strlcpy(pp->ulp_name, name, sizeof (pp->ulp_name));87pp->ulp_nodeoffset = nodeoffset;88pp->ulp_objsize = objsize;89pp->ulp_cmp = compare_func;90if (flags & UU_LIST_POOL_DEBUG)91pp->ulp_debug = 1;92pp->ulp_last_index = 0;9394(void) pthread_mutex_init(&pp->ulp_lock, NULL);9596pp->ulp_null_list.ul_next = &pp->ulp_null_list;97pp->ulp_null_list.ul_prev = &pp->ulp_null_list;9899(void) pthread_mutex_lock(&uu_lpool_list_lock);100pp->ulp_next = next = &uu_null_lpool;101pp->ulp_prev = prev = next->ulp_prev;102next->ulp_prev = pp;103prev->ulp_next = pp;104(void) pthread_mutex_unlock(&uu_lpool_list_lock);105106return (pp);107}108109void110uu_list_pool_destroy(uu_list_pool_t *pp)111{112if (pp->ulp_debug) {113if (pp->ulp_null_list.ul_next != &pp->ulp_null_list ||114pp->ulp_null_list.ul_prev != &pp->ulp_null_list) {115uu_panic("uu_list_pool_destroy: Pool \"%.*s\" (%p) has "116"outstanding lists, or is corrupt.\n",117(int)sizeof (pp->ulp_name), pp->ulp_name,118(void *)pp);119}120}121(void) pthread_mutex_lock(&uu_lpool_list_lock);122pp->ulp_next->ulp_prev = pp->ulp_prev;123pp->ulp_prev->ulp_next = pp->ulp_next;124(void) pthread_mutex_unlock(&uu_lpool_list_lock);125pp->ulp_prev = NULL;126pp->ulp_next = NULL;127uu_free(pp);128}129130void131uu_list_node_init(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)132{133uu_list_node_impl_t *np = (uu_list_node_impl_t *)np_arg;134135if (pp->ulp_debug) {136uintptr_t offset = (uintptr_t)np - (uintptr_t)base;137if (offset + sizeof (*np) > pp->ulp_objsize) {138uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "139"offset %ld doesn't fit in object (size %ld)\n",140base, (void *)np, (void *)pp, pp->ulp_name,141(long)offset, (long)pp->ulp_objsize);142}143if (offset != pp->ulp_nodeoffset) {144uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "145"offset %ld doesn't match pool's offset (%ld)\n",146base, (void *)np, (void *)pp, pp->ulp_name,147(long)offset, (long)pp->ulp_objsize);148}149}150np->uln_next = POOL_TO_MARKER(pp);151np->uln_prev = NULL;152}153154void155uu_list_node_fini(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)156{157uu_list_node_impl_t *np = (uu_list_node_impl_t *)np_arg;158159if (pp->ulp_debug) {160if (np->uln_next == NULL &&161np->uln_prev == NULL) {162uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "163"node already finied\n",164base, (void *)np_arg, (void *)pp, pp->ulp_name);165}166if (np->uln_next != POOL_TO_MARKER(pp) ||167np->uln_prev != NULL) {168uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "169"node corrupt or on list\n",170base, (void *)np_arg, (void *)pp, pp->ulp_name);171}172}173np->uln_next = NULL;174np->uln_prev = NULL;175}176177uu_list_t *178uu_list_create(uu_list_pool_t *pp, void *parent, uint32_t flags)179{180uu_list_t *lp, *next, *prev;181182if (flags & ~(UU_LIST_DEBUG | UU_LIST_SORTED)) {183uu_set_error(UU_ERROR_UNKNOWN_FLAG);184return (NULL);185}186187if ((flags & UU_LIST_SORTED) && pp->ulp_cmp == NULL) {188if (pp->ulp_debug)189uu_panic("uu_list_create(%p, ...): requested "190"UU_LIST_SORTED, but pool has no comparison func\n",191(void *)pp);192uu_set_error(UU_ERROR_NOT_SUPPORTED);193return (NULL);194}195196lp = uu_zalloc(sizeof (*lp));197if (lp == NULL) {198uu_set_error(UU_ERROR_NO_MEMORY);199return (NULL);200}201202lp->ul_pool = pp;203lp->ul_parent = parent;204lp->ul_offset = pp->ulp_nodeoffset;205lp->ul_debug = pp->ulp_debug || (flags & UU_LIST_DEBUG);206lp->ul_sorted = (flags & UU_LIST_SORTED);207lp->ul_numnodes = 0;208lp->ul_index = (pp->ulp_last_index = INDEX_NEXT(pp->ulp_last_index));209210lp->ul_null_node.uln_next = &lp->ul_null_node;211lp->ul_null_node.uln_prev = &lp->ul_null_node;212213lp->ul_null_walk.ulw_next = &lp->ul_null_walk;214lp->ul_null_walk.ulw_prev = &lp->ul_null_walk;215216(void) pthread_mutex_lock(&pp->ulp_lock);217next = &pp->ulp_null_list;218prev = next->ul_prev;219lp->ul_next = next;220lp->ul_prev = prev;221next->ul_prev = lp;222prev->ul_next = lp;223(void) pthread_mutex_unlock(&pp->ulp_lock);224225return (lp);226}227228void229uu_list_destroy(uu_list_t *lp)230{231uu_list_pool_t *pp = lp->ul_pool;232233if (lp->ul_debug) {234if (lp->ul_null_node.uln_next != &lp->ul_null_node ||235lp->ul_null_node.uln_prev != &lp->ul_null_node) {236uu_panic("uu_list_destroy(%p): list not empty\n",237(void *)lp);238}239if (lp->ul_numnodes != 0) {240uu_panic("uu_list_destroy(%p): numnodes is nonzero, "241"but list is empty\n", (void *)lp);242}243if (lp->ul_null_walk.ulw_next != &lp->ul_null_walk ||244lp->ul_null_walk.ulw_prev != &lp->ul_null_walk) {245uu_panic("uu_list_destroy(%p): outstanding walkers\n",246(void *)lp);247}248}249250(void) pthread_mutex_lock(&pp->ulp_lock);251lp->ul_next->ul_prev = lp->ul_prev;252lp->ul_prev->ul_next = lp->ul_next;253(void) pthread_mutex_unlock(&pp->ulp_lock);254lp->ul_prev = NULL;255lp->ul_next = NULL;256lp->ul_pool = NULL;257uu_free(lp);258}259260static void261list_insert(uu_list_t *lp, uu_list_node_impl_t *np, uu_list_node_impl_t *prev,262uu_list_node_impl_t *next)263{264if (lp->ul_debug) {265if (next->uln_prev != prev || prev->uln_next != next)266uu_panic("insert(%p): internal error: %p and %p not "267"neighbors\n", (void *)lp, (void *)next,268(void *)prev);269270if (np->uln_next != POOL_TO_MARKER(lp->ul_pool) ||271np->uln_prev != NULL) {272uu_panic("insert(%p): elem %p node %p corrupt, "273"not initialized, or already in a list.\n",274(void *)lp, NODE_TO_ELEM(lp, np), (void *)np);275}276/*277* invalidate outstanding uu_list_index_ts.278*/279lp->ul_index = INDEX_NEXT(lp->ul_index);280}281np->uln_next = next;282np->uln_prev = prev;283next->uln_prev = np;284prev->uln_next = np;285286lp->ul_numnodes++;287}288289void290uu_list_insert(uu_list_t *lp, void *elem, uu_list_index_t idx)291{292uu_list_node_impl_t *np;293294np = INDEX_TO_NODE(idx);295if (np == NULL)296np = &lp->ul_null_node;297298if (lp->ul_debug) {299if (!INDEX_VALID(lp, idx))300uu_panic("uu_list_insert(%p, %p, %p): %s\n",301(void *)lp, elem, (void *)idx,302INDEX_CHECK(idx)? "outdated index" :303"invalid index");304if (np->uln_prev == NULL)305uu_panic("uu_list_insert(%p, %p, %p): out-of-date "306"index\n", (void *)lp, elem, (void *)idx);307}308309list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);310}311312void *313uu_list_find(uu_list_t *lp, void *elem, void *private, uu_list_index_t *out)314{315int sorted = lp->ul_sorted;316uu_compare_fn_t *func = lp->ul_pool->ulp_cmp;317uu_list_node_impl_t *np;318319if (func == NULL) {320if (out != NULL)321*out = 0;322uu_set_error(UU_ERROR_NOT_SUPPORTED);323return (NULL);324}325for (np = lp->ul_null_node.uln_next; np != &lp->ul_null_node;326np = np->uln_next) {327void *ep = NODE_TO_ELEM(lp, np);328int cmp = func(ep, elem, private);329if (cmp == 0) {330if (out != NULL)331*out = NODE_TO_INDEX(lp, np);332return (ep);333}334if (sorted && cmp > 0) {335if (out != NULL)336*out = NODE_TO_INDEX(lp, np);337return (NULL);338}339}340if (out != NULL)341*out = NODE_TO_INDEX(lp, 0);342return (NULL);343}344345void *346uu_list_nearest_next(uu_list_t *lp, uu_list_index_t idx)347{348uu_list_node_impl_t *np = INDEX_TO_NODE(idx);349350if (np == NULL)351np = &lp->ul_null_node;352353if (lp->ul_debug) {354if (!INDEX_VALID(lp, idx))355uu_panic("uu_list_nearest_next(%p, %p): %s\n",356(void *)lp, (void *)idx,357INDEX_CHECK(idx)? "outdated index" :358"invalid index");359if (np->uln_prev == NULL)360uu_panic("uu_list_nearest_next(%p, %p): out-of-date "361"index\n", (void *)lp, (void *)idx);362}363364if (np == &lp->ul_null_node)365return (NULL);366else367return (NODE_TO_ELEM(lp, np));368}369370void *371uu_list_nearest_prev(uu_list_t *lp, uu_list_index_t idx)372{373uu_list_node_impl_t *np = INDEX_TO_NODE(idx);374375if (np == NULL)376np = &lp->ul_null_node;377378if (lp->ul_debug) {379if (!INDEX_VALID(lp, idx))380uu_panic("uu_list_nearest_prev(%p, %p): %s\n",381(void *)lp, (void *)idx, INDEX_CHECK(idx)?382"outdated index" : "invalid index");383if (np->uln_prev == NULL)384uu_panic("uu_list_nearest_prev(%p, %p): out-of-date "385"index\n", (void *)lp, (void *)idx);386}387388if ((np = np->uln_prev) == &lp->ul_null_node)389return (NULL);390else391return (NODE_TO_ELEM(lp, np));392}393394static void395list_walk_init(uu_list_walk_t *wp, uu_list_t *lp, uint32_t flags)396{397uu_list_walk_t *next, *prev;398399int robust = (flags & UU_WALK_ROBUST);400int direction = (flags & UU_WALK_REVERSE)? -1 : 1;401402(void) memset(wp, 0, sizeof (*wp));403wp->ulw_list = lp;404wp->ulw_robust = robust;405wp->ulw_dir = direction;406if (direction > 0)407wp->ulw_next_result = lp->ul_null_node.uln_next;408else409wp->ulw_next_result = lp->ul_null_node.uln_prev;410411if (lp->ul_debug || robust) {412/*413* Add this walker to the list's list of walkers so414* uu_list_remove() can advance us if somebody tries to415* remove ulw_next_result.416*/417wp->ulw_next = next = &lp->ul_null_walk;418wp->ulw_prev = prev = next->ulw_prev;419next->ulw_prev = wp;420prev->ulw_next = wp;421}422}423424static uu_list_node_impl_t *425list_walk_advance(uu_list_walk_t *wp, uu_list_t *lp)426{427uu_list_node_impl_t *np = wp->ulw_next_result;428uu_list_node_impl_t *next;429430if (np == &lp->ul_null_node)431return (NULL);432433next = (wp->ulw_dir > 0)? np->uln_next : np->uln_prev;434435wp->ulw_next_result = next;436return (np);437}438439static void440list_walk_fini(uu_list_walk_t *wp)441{442/* GLXXX debugging? */443if (wp->ulw_next != NULL) {444wp->ulw_next->ulw_prev = wp->ulw_prev;445wp->ulw_prev->ulw_next = wp->ulw_next;446wp->ulw_next = NULL;447wp->ulw_prev = NULL;448}449wp->ulw_list = NULL;450wp->ulw_next_result = NULL;451}452453uu_list_walk_t *454uu_list_walk_start(uu_list_t *lp, uint32_t flags)455{456uu_list_walk_t *wp;457458if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {459uu_set_error(UU_ERROR_UNKNOWN_FLAG);460return (NULL);461}462463wp = uu_zalloc(sizeof (*wp));464if (wp == NULL) {465uu_set_error(UU_ERROR_NO_MEMORY);466return (NULL);467}468469list_walk_init(wp, lp, flags);470return (wp);471}472473void *474uu_list_walk_next(uu_list_walk_t *wp)475{476uu_list_t *lp = wp->ulw_list;477uu_list_node_impl_t *np = list_walk_advance(wp, lp);478479if (np == NULL)480return (NULL);481482return (NODE_TO_ELEM(lp, np));483}484485void486uu_list_walk_end(uu_list_walk_t *wp)487{488list_walk_fini(wp);489uu_free(wp);490}491492int493uu_list_walk(uu_list_t *lp, uu_walk_fn_t *func, void *private, uint32_t flags)494{495uu_list_node_impl_t *np;496497int status = UU_WALK_NEXT;498499int robust = (flags & UU_WALK_ROBUST);500int reverse = (flags & UU_WALK_REVERSE);501502if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {503uu_set_error(UU_ERROR_UNKNOWN_FLAG);504return (-1);505}506507if (lp->ul_debug || robust) {508uu_list_walk_t *my_walk;509void *e;510511my_walk = uu_zalloc(sizeof (*my_walk));512if (my_walk == NULL)513return (-1);514515list_walk_init(my_walk, lp, flags);516while (status == UU_WALK_NEXT &&517(e = uu_list_walk_next(my_walk)) != NULL)518status = (*func)(e, private);519list_walk_fini(my_walk);520521uu_free(my_walk);522} else {523if (!reverse) {524for (np = lp->ul_null_node.uln_next;525status == UU_WALK_NEXT && np != &lp->ul_null_node;526np = np->uln_next) {527status = (*func)(NODE_TO_ELEM(lp, np), private);528}529} else {530for (np = lp->ul_null_node.uln_prev;531status == UU_WALK_NEXT && np != &lp->ul_null_node;532np = np->uln_prev) {533status = (*func)(NODE_TO_ELEM(lp, np), private);534}535}536}537if (status >= 0)538return (0);539uu_set_error(UU_ERROR_CALLBACK_FAILED);540return (-1);541}542543void544uu_list_remove(uu_list_t *lp, void *elem)545{546uu_list_node_impl_t *np = ELEM_TO_NODE(lp, elem);547uu_list_walk_t *wp;548549if (lp->ul_debug) {550if (np->uln_prev == NULL)551uu_panic("uu_list_remove(%p, %p): elem not on list\n",552(void *)lp, elem);553/*554* invalidate outstanding uu_list_index_ts.555*/556lp->ul_index = INDEX_NEXT(lp->ul_index);557}558559/*560* robust walkers must be advanced. In debug mode, non-robust561* walkers are also on the list. If there are any, it's an error.562*/563for (wp = lp->ul_null_walk.ulw_next; wp != &lp->ul_null_walk;564wp = wp->ulw_next) {565if (wp->ulw_robust) {566if (np == wp->ulw_next_result)567(void) list_walk_advance(wp, lp);568} else if (wp->ulw_next_result != NULL) {569uu_panic("uu_list_remove(%p, %p): active non-robust "570"walker\n", (void *)lp, elem);571}572}573574np->uln_next->uln_prev = np->uln_prev;575np->uln_prev->uln_next = np->uln_next;576577lp->ul_numnodes--;578579np->uln_next = POOL_TO_MARKER(lp->ul_pool);580np->uln_prev = NULL;581}582583void *584uu_list_teardown(uu_list_t *lp, void **cookie)585{586void *ep;587588/*589* XXX: disable list modification until list is empty590*/591if (lp->ul_debug && *cookie != NULL)592uu_panic("uu_list_teardown(%p, %p): unexpected cookie\n",593(void *)lp, (void *)cookie);594595ep = uu_list_first(lp);596if (ep)597uu_list_remove(lp, ep);598return (ep);599}600601int602uu_list_insert_before(uu_list_t *lp, void *target, void *elem)603{604uu_list_node_impl_t *np = ELEM_TO_NODE(lp, target);605606if (target == NULL)607np = &lp->ul_null_node;608609if (lp->ul_debug) {610if (np->uln_prev == NULL)611uu_panic("uu_list_insert_before(%p, %p, %p): %p is "612"not currently on a list\n",613(void *)lp, target, elem, target);614}615if (lp->ul_sorted) {616if (lp->ul_debug)617uu_panic("uu_list_insert_before(%p, ...): list is "618"UU_LIST_SORTED\n", (void *)lp);619uu_set_error(UU_ERROR_NOT_SUPPORTED);620return (-1);621}622623list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);624return (0);625}626627int628uu_list_insert_after(uu_list_t *lp, void *target, void *elem)629{630uu_list_node_impl_t *np = ELEM_TO_NODE(lp, target);631632if (target == NULL)633np = &lp->ul_null_node;634635if (lp->ul_debug) {636if (np->uln_prev == NULL)637uu_panic("uu_list_insert_after(%p, %p, %p): %p is "638"not currently on a list\n",639(void *)lp, target, elem, target);640}641if (lp->ul_sorted) {642if (lp->ul_debug)643uu_panic("uu_list_insert_after(%p, ...): list is "644"UU_LIST_SORTED\n", (void *)lp);645uu_set_error(UU_ERROR_NOT_SUPPORTED);646return (-1);647}648649list_insert(lp, ELEM_TO_NODE(lp, elem), np, np->uln_next);650return (0);651}652653size_t654uu_list_numnodes(uu_list_t *lp)655{656return (lp->ul_numnodes);657}658659void *660uu_list_first(uu_list_t *lp)661{662uu_list_node_impl_t *n = lp->ul_null_node.uln_next;663if (n == &lp->ul_null_node)664return (NULL);665return (NODE_TO_ELEM(lp, n));666}667668void *669uu_list_last(uu_list_t *lp)670{671uu_list_node_impl_t *n = lp->ul_null_node.uln_prev;672if (n == &lp->ul_null_node)673return (NULL);674return (NODE_TO_ELEM(lp, n));675}676677void *678uu_list_next(uu_list_t *lp, void *elem)679{680uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);681682n = n->uln_next;683if (n == &lp->ul_null_node)684return (NULL);685return (NODE_TO_ELEM(lp, n));686}687688void *689uu_list_prev(uu_list_t *lp, void *elem)690{691uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);692693n = n->uln_prev;694if (n == &lp->ul_null_node)695return (NULL);696return (NODE_TO_ELEM(lp, n));697}698699/*700* called from uu_lockup() and uu_release(), as part of our fork1()-safety.701*/702void703uu_list_lockup(void)704{705uu_list_pool_t *pp;706707(void) pthread_mutex_lock(&uu_lpool_list_lock);708for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;709pp = pp->ulp_next)710(void) pthread_mutex_lock(&pp->ulp_lock);711}712713void714uu_list_release(void)715{716uu_list_pool_t *pp;717718for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;719pp = pp->ulp_next)720(void) pthread_mutex_unlock(&pp->ulp_lock);721(void) pthread_mutex_unlock(&uu_lpool_list_lock);722}723724725