xref: /freebsd/sys/contrib/openzfs/lib/libuutil/uu_list.c (revision 3c4ba5f55438f7afd4f4b0b56f88f2bb505fd6a6)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * 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.LICENSE
9  * or https://opensource.org/licenses/CDDL-1.0.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 
28 #include "libuutil_common.h"
29 
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/time.h>
34 
35 #define	ELEM_TO_NODE(lp, e) \
36 	((uu_list_node_impl_t *)((uintptr_t)(e) + (lp)->ul_offset))
37 
38 #define	NODE_TO_ELEM(lp, n) \
39 	((void *)((uintptr_t)(n) - (lp)->ul_offset))
40 
41 /*
42  * uu_list_index_ts define a location for insertion.  They are simply a
43  * pointer to the object after the insertion point.  We store a mark
44  * in the low-bits of the index, to help prevent mistakes.
45  *
46  * When debugging, the index mark changes on every insert and delete, to
47  * 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)
51 
52 #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)
56 
57 #define	POOL_TO_MARKER(pp) ((void *)((uintptr_t)(pp) | 1))
58 
59 static uu_list_pool_t	uu_null_lpool = { &uu_null_lpool, &uu_null_lpool };
60 static pthread_mutex_t	uu_lpool_list_lock = PTHREAD_MUTEX_INITIALIZER;
61 
62 uu_list_pool_t *
63 uu_list_pool_create(const char *name, size_t objsize,
64     size_t nodeoffset, uu_compare_fn_t *compare_func, uint32_t flags)
65 {
66 	uu_list_pool_t *pp, *next, *prev;
67 
68 	if (name == NULL ||
69 	    uu_check_name(name, UU_NAME_DOMAIN) == -1 ||
70 	    nodeoffset + sizeof (uu_list_node_t) > objsize) {
71 		uu_set_error(UU_ERROR_INVALID_ARGUMENT);
72 		return (NULL);
73 	}
74 
75 	if (flags & ~UU_LIST_POOL_DEBUG) {
76 		uu_set_error(UU_ERROR_UNKNOWN_FLAG);
77 		return (NULL);
78 	}
79 
80 	pp = uu_zalloc(sizeof (uu_list_pool_t));
81 	if (pp == NULL) {
82 		uu_set_error(UU_ERROR_NO_MEMORY);
83 		return (NULL);
84 	}
85 
86 	(void) strlcpy(pp->ulp_name, name, sizeof (pp->ulp_name));
87 	pp->ulp_nodeoffset = nodeoffset;
88 	pp->ulp_objsize = objsize;
89 	pp->ulp_cmp = compare_func;
90 	if (flags & UU_LIST_POOL_DEBUG)
91 		pp->ulp_debug = 1;
92 	pp->ulp_last_index = 0;
93 
94 	(void) pthread_mutex_init(&pp->ulp_lock, NULL);
95 
96 	pp->ulp_null_list.ul_next = &pp->ulp_null_list;
97 	pp->ulp_null_list.ul_prev = &pp->ulp_null_list;
98 
99 	(void) pthread_mutex_lock(&uu_lpool_list_lock);
100 	pp->ulp_next = next = &uu_null_lpool;
101 	pp->ulp_prev = prev = next->ulp_prev;
102 	next->ulp_prev = pp;
103 	prev->ulp_next = pp;
104 	(void) pthread_mutex_unlock(&uu_lpool_list_lock);
105 
106 	return (pp);
107 }
108 
109 void
110 uu_list_pool_destroy(uu_list_pool_t *pp)
111 {
112 	if (pp->ulp_debug) {
113 		if (pp->ulp_null_list.ul_next != &pp->ulp_null_list ||
114 		    pp->ulp_null_list.ul_prev != &pp->ulp_null_list) {
115 			uu_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);
122 	pp->ulp_next->ulp_prev = pp->ulp_prev;
123 	pp->ulp_prev->ulp_next = pp->ulp_next;
124 	(void) pthread_mutex_unlock(&uu_lpool_list_lock);
125 	pp->ulp_prev = NULL;
126 	pp->ulp_next = NULL;
127 	uu_free(pp);
128 }
129 
130 void
131 uu_list_node_init(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)
132 {
133 	uu_list_node_impl_t *np = (uu_list_node_impl_t *)np_arg;
134 
135 	if (pp->ulp_debug) {
136 		uintptr_t offset = (uintptr_t)np - (uintptr_t)base;
137 		if (offset + sizeof (*np) > pp->ulp_objsize) {
138 			uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "
139 			    "offset %ld doesn't fit in object (size %ld)\n",
140 			    base, (void *)np, (void *)pp, pp->ulp_name,
141 			    (long)offset, (long)pp->ulp_objsize);
142 		}
143 		if (offset != pp->ulp_nodeoffset) {
144 			uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "
145 			    "offset %ld doesn't match pool's offset (%ld)\n",
146 			    base, (void *)np, (void *)pp, pp->ulp_name,
147 			    (long)offset, (long)pp->ulp_objsize);
148 		}
149 	}
150 	np->uln_next = POOL_TO_MARKER(pp);
151 	np->uln_prev = NULL;
152 }
153 
154 void
155 uu_list_node_fini(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)
156 {
157 	uu_list_node_impl_t *np = (uu_list_node_impl_t *)np_arg;
158 
159 	if (pp->ulp_debug) {
160 		if (np->uln_next == NULL &&
161 		    np->uln_prev == NULL) {
162 			uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "
163 			    "node already finied\n",
164 			    base, (void *)np_arg, (void *)pp, pp->ulp_name);
165 		}
166 		if (np->uln_next != POOL_TO_MARKER(pp) ||
167 		    np->uln_prev != NULL) {
168 			uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "
169 			    "node corrupt or on list\n",
170 			    base, (void *)np_arg, (void *)pp, pp->ulp_name);
171 		}
172 	}
173 	np->uln_next = NULL;
174 	np->uln_prev = NULL;
175 }
176 
177 uu_list_t *
178 uu_list_create(uu_list_pool_t *pp, void *parent, uint32_t flags)
179 {
180 	uu_list_t *lp, *next, *prev;
181 
182 	if (flags & ~(UU_LIST_DEBUG | UU_LIST_SORTED)) {
183 		uu_set_error(UU_ERROR_UNKNOWN_FLAG);
184 		return (NULL);
185 	}
186 
187 	if ((flags & UU_LIST_SORTED) && pp->ulp_cmp == NULL) {
188 		if (pp->ulp_debug)
189 			uu_panic("uu_list_create(%p, ...): requested "
190 			    "UU_LIST_SORTED, but pool has no comparison func\n",
191 			    (void *)pp);
192 		uu_set_error(UU_ERROR_NOT_SUPPORTED);
193 		return (NULL);
194 	}
195 
196 	lp = uu_zalloc(sizeof (*lp));
197 	if (lp == NULL) {
198 		uu_set_error(UU_ERROR_NO_MEMORY);
199 		return (NULL);
200 	}
201 
202 	lp->ul_pool = pp;
203 	lp->ul_parent = parent;
204 	lp->ul_offset = pp->ulp_nodeoffset;
205 	lp->ul_debug = pp->ulp_debug || (flags & UU_LIST_DEBUG);
206 	lp->ul_sorted = (flags & UU_LIST_SORTED);
207 	lp->ul_numnodes = 0;
208 	lp->ul_index = (pp->ulp_last_index = INDEX_NEXT(pp->ulp_last_index));
209 
210 	lp->ul_null_node.uln_next = &lp->ul_null_node;
211 	lp->ul_null_node.uln_prev = &lp->ul_null_node;
212 
213 	lp->ul_null_walk.ulw_next = &lp->ul_null_walk;
214 	lp->ul_null_walk.ulw_prev = &lp->ul_null_walk;
215 
216 	(void) pthread_mutex_lock(&pp->ulp_lock);
217 	next = &pp->ulp_null_list;
218 	prev = next->ul_prev;
219 	lp->ul_next = next;
220 	lp->ul_prev = prev;
221 	next->ul_prev = lp;
222 	prev->ul_next = lp;
223 	(void) pthread_mutex_unlock(&pp->ulp_lock);
224 
225 	return (lp);
226 }
227 
228 void
229 uu_list_destroy(uu_list_t *lp)
230 {
231 	uu_list_pool_t *pp = lp->ul_pool;
232 
233 	if (lp->ul_debug) {
234 		if (lp->ul_null_node.uln_next != &lp->ul_null_node ||
235 		    lp->ul_null_node.uln_prev != &lp->ul_null_node) {
236 			uu_panic("uu_list_destroy(%p):  list not empty\n",
237 			    (void *)lp);
238 		}
239 		if (lp->ul_numnodes != 0) {
240 			uu_panic("uu_list_destroy(%p):  numnodes is nonzero, "
241 			    "but list is empty\n", (void *)lp);
242 		}
243 		if (lp->ul_null_walk.ulw_next != &lp->ul_null_walk ||
244 		    lp->ul_null_walk.ulw_prev != &lp->ul_null_walk) {
245 			uu_panic("uu_list_destroy(%p):  outstanding walkers\n",
246 			    (void *)lp);
247 		}
248 	}
249 
250 	(void) pthread_mutex_lock(&pp->ulp_lock);
251 	lp->ul_next->ul_prev = lp->ul_prev;
252 	lp->ul_prev->ul_next = lp->ul_next;
253 	(void) pthread_mutex_unlock(&pp->ulp_lock);
254 	lp->ul_prev = NULL;
255 	lp->ul_next = NULL;
256 	lp->ul_pool = NULL;
257 	uu_free(lp);
258 }
259 
260 static void
261 list_insert(uu_list_t *lp, uu_list_node_impl_t *np, uu_list_node_impl_t *prev,
262     uu_list_node_impl_t *next)
263 {
264 	if (lp->ul_debug) {
265 		if (next->uln_prev != prev || prev->uln_next != next)
266 			uu_panic("insert(%p): internal error: %p and %p not "
267 			    "neighbors\n", (void *)lp, (void *)next,
268 			    (void *)prev);
269 
270 		if (np->uln_next != POOL_TO_MARKER(lp->ul_pool) ||
271 		    np->uln_prev != NULL) {
272 			uu_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 		 */
279 		lp->ul_index = INDEX_NEXT(lp->ul_index);
280 	}
281 	np->uln_next = next;
282 	np->uln_prev = prev;
283 	next->uln_prev = np;
284 	prev->uln_next = np;
285 
286 	lp->ul_numnodes++;
287 }
288 
289 void
290 uu_list_insert(uu_list_t *lp, void *elem, uu_list_index_t idx)
291 {
292 	uu_list_node_impl_t *np;
293 
294 	np = INDEX_TO_NODE(idx);
295 	if (np == NULL)
296 		np = &lp->ul_null_node;
297 
298 	if (lp->ul_debug) {
299 		if (!INDEX_VALID(lp, idx))
300 			uu_panic("uu_list_insert(%p, %p, %p): %s\n",
301 			    (void *)lp, elem, (void *)idx,
302 			    INDEX_CHECK(idx)? "outdated index" :
303 			    "invalid index");
304 		if (np->uln_prev == NULL)
305 			uu_panic("uu_list_insert(%p, %p, %p): out-of-date "
306 			    "index\n", (void *)lp, elem, (void *)idx);
307 	}
308 
309 	list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);
310 }
311 
312 void *
313 uu_list_find(uu_list_t *lp, void *elem, void *private, uu_list_index_t *out)
314 {
315 	int sorted = lp->ul_sorted;
316 	uu_compare_fn_t *func = lp->ul_pool->ulp_cmp;
317 	uu_list_node_impl_t *np;
318 
319 	if (func == NULL) {
320 		if (out != NULL)
321 			*out = 0;
322 		uu_set_error(UU_ERROR_NOT_SUPPORTED);
323 		return (NULL);
324 	}
325 	for (np = lp->ul_null_node.uln_next; np != &lp->ul_null_node;
326 	    np = np->uln_next) {
327 		void *ep = NODE_TO_ELEM(lp, np);
328 		int cmp = func(ep, elem, private);
329 		if (cmp == 0) {
330 			if (out != NULL)
331 				*out = NODE_TO_INDEX(lp, np);
332 			return (ep);
333 		}
334 		if (sorted && cmp > 0) {
335 			if (out != NULL)
336 				*out = NODE_TO_INDEX(lp, np);
337 			return (NULL);
338 		}
339 	}
340 	if (out != NULL)
341 		*out = NODE_TO_INDEX(lp, 0);
342 	return (NULL);
343 }
344 
345 void *
346 uu_list_nearest_next(uu_list_t *lp, uu_list_index_t idx)
347 {
348 	uu_list_node_impl_t *np = INDEX_TO_NODE(idx);
349 
350 	if (np == NULL)
351 		np = &lp->ul_null_node;
352 
353 	if (lp->ul_debug) {
354 		if (!INDEX_VALID(lp, idx))
355 			uu_panic("uu_list_nearest_next(%p, %p): %s\n",
356 			    (void *)lp, (void *)idx,
357 			    INDEX_CHECK(idx)? "outdated index" :
358 			    "invalid index");
359 		if (np->uln_prev == NULL)
360 			uu_panic("uu_list_nearest_next(%p, %p): out-of-date "
361 			    "index\n", (void *)lp, (void *)idx);
362 	}
363 
364 	if (np == &lp->ul_null_node)
365 		return (NULL);
366 	else
367 		return (NODE_TO_ELEM(lp, np));
368 }
369 
370 void *
371 uu_list_nearest_prev(uu_list_t *lp, uu_list_index_t idx)
372 {
373 	uu_list_node_impl_t *np = INDEX_TO_NODE(idx);
374 
375 	if (np == NULL)
376 		np = &lp->ul_null_node;
377 
378 	if (lp->ul_debug) {
379 		if (!INDEX_VALID(lp, idx))
380 			uu_panic("uu_list_nearest_prev(%p, %p): %s\n",
381 			    (void *)lp, (void *)idx, INDEX_CHECK(idx)?
382 			    "outdated index" : "invalid index");
383 		if (np->uln_prev == NULL)
384 			uu_panic("uu_list_nearest_prev(%p, %p): out-of-date "
385 			    "index\n", (void *)lp, (void *)idx);
386 	}
387 
388 	if ((np = np->uln_prev) == &lp->ul_null_node)
389 		return (NULL);
390 	else
391 		return (NODE_TO_ELEM(lp, np));
392 }
393 
394 static void
395 list_walk_init(uu_list_walk_t *wp, uu_list_t *lp, uint32_t flags)
396 {
397 	uu_list_walk_t *next, *prev;
398 
399 	int robust = (flags & UU_WALK_ROBUST);
400 	int direction = (flags & UU_WALK_REVERSE)? -1 : 1;
401 
402 	(void) memset(wp, 0, sizeof (*wp));
403 	wp->ulw_list = lp;
404 	wp->ulw_robust = robust;
405 	wp->ulw_dir = direction;
406 	if (direction > 0)
407 		wp->ulw_next_result = lp->ul_null_node.uln_next;
408 	else
409 		wp->ulw_next_result = lp->ul_null_node.uln_prev;
410 
411 	if (lp->ul_debug || robust) {
412 		/*
413 		 * Add this walker to the list's list of walkers so
414 		 * uu_list_remove() can advance us if somebody tries to
415 		 * remove ulw_next_result.
416 		 */
417 		wp->ulw_next = next = &lp->ul_null_walk;
418 		wp->ulw_prev = prev = next->ulw_prev;
419 		next->ulw_prev = wp;
420 		prev->ulw_next = wp;
421 	}
422 }
423 
424 static uu_list_node_impl_t *
425 list_walk_advance(uu_list_walk_t *wp, uu_list_t *lp)
426 {
427 	uu_list_node_impl_t *np = wp->ulw_next_result;
428 	uu_list_node_impl_t *next;
429 
430 	if (np == &lp->ul_null_node)
431 		return (NULL);
432 
433 	next = (wp->ulw_dir > 0)? np->uln_next : np->uln_prev;
434 
435 	wp->ulw_next_result = next;
436 	return (np);
437 }
438 
439 static void
440 list_walk_fini(uu_list_walk_t *wp)
441 {
442 	/* GLXXX debugging? */
443 	if (wp->ulw_next != NULL) {
444 		wp->ulw_next->ulw_prev = wp->ulw_prev;
445 		wp->ulw_prev->ulw_next = wp->ulw_next;
446 		wp->ulw_next = NULL;
447 		wp->ulw_prev = NULL;
448 	}
449 	wp->ulw_list = NULL;
450 	wp->ulw_next_result = NULL;
451 }
452 
453 uu_list_walk_t *
454 uu_list_walk_start(uu_list_t *lp, uint32_t flags)
455 {
456 	uu_list_walk_t *wp;
457 
458 	if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
459 		uu_set_error(UU_ERROR_UNKNOWN_FLAG);
460 		return (NULL);
461 	}
462 
463 	wp = uu_zalloc(sizeof (*wp));
464 	if (wp == NULL) {
465 		uu_set_error(UU_ERROR_NO_MEMORY);
466 		return (NULL);
467 	}
468 
469 	list_walk_init(wp, lp, flags);
470 	return (wp);
471 }
472 
473 void *
474 uu_list_walk_next(uu_list_walk_t *wp)
475 {
476 	uu_list_t *lp = wp->ulw_list;
477 	uu_list_node_impl_t *np = list_walk_advance(wp, lp);
478 
479 	if (np == NULL)
480 		return (NULL);
481 
482 	return (NODE_TO_ELEM(lp, np));
483 }
484 
485 void
486 uu_list_walk_end(uu_list_walk_t *wp)
487 {
488 	list_walk_fini(wp);
489 	uu_free(wp);
490 }
491 
492 int
493 uu_list_walk(uu_list_t *lp, uu_walk_fn_t *func, void *private, uint32_t flags)
494 {
495 	uu_list_node_impl_t *np;
496 
497 	int status = UU_WALK_NEXT;
498 
499 	int robust = (flags & UU_WALK_ROBUST);
500 	int reverse = (flags & UU_WALK_REVERSE);
501 
502 	if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
503 		uu_set_error(UU_ERROR_UNKNOWN_FLAG);
504 		return (-1);
505 	}
506 
507 	if (lp->ul_debug || robust) {
508 		uu_list_walk_t my_walk;
509 		void *e;
510 
511 		list_walk_init(&my_walk, lp, flags);
512 		while (status == UU_WALK_NEXT &&
513 		    (e = uu_list_walk_next(&my_walk)) != NULL)
514 			status = (*func)(e, private);
515 		list_walk_fini(&my_walk);
516 	} else {
517 		if (!reverse) {
518 			for (np = lp->ul_null_node.uln_next;
519 			    status == UU_WALK_NEXT && np != &lp->ul_null_node;
520 			    np = np->uln_next) {
521 				status = (*func)(NODE_TO_ELEM(lp, np), private);
522 			}
523 		} else {
524 			for (np = lp->ul_null_node.uln_prev;
525 			    status == UU_WALK_NEXT && np != &lp->ul_null_node;
526 			    np = np->uln_prev) {
527 				status = (*func)(NODE_TO_ELEM(lp, np), private);
528 			}
529 		}
530 	}
531 	if (status >= 0)
532 		return (0);
533 	uu_set_error(UU_ERROR_CALLBACK_FAILED);
534 	return (-1);
535 }
536 
537 void
538 uu_list_remove(uu_list_t *lp, void *elem)
539 {
540 	uu_list_node_impl_t *np = ELEM_TO_NODE(lp, elem);
541 	uu_list_walk_t *wp;
542 
543 	if (lp->ul_debug) {
544 		if (np->uln_prev == NULL)
545 			uu_panic("uu_list_remove(%p, %p): elem not on list\n",
546 			    (void *)lp, elem);
547 		/*
548 		 * invalidate outstanding uu_list_index_ts.
549 		 */
550 		lp->ul_index = INDEX_NEXT(lp->ul_index);
551 	}
552 
553 	/*
554 	 * robust walkers must be advanced.  In debug mode, non-robust
555 	 * walkers are also on the list.  If there are any, it's an error.
556 	 */
557 	for (wp = lp->ul_null_walk.ulw_next; wp != &lp->ul_null_walk;
558 	    wp = wp->ulw_next) {
559 		if (wp->ulw_robust) {
560 			if (np == wp->ulw_next_result)
561 				(void) list_walk_advance(wp, lp);
562 		} else if (wp->ulw_next_result != NULL) {
563 			uu_panic("uu_list_remove(%p, %p): active non-robust "
564 			    "walker\n", (void *)lp, elem);
565 		}
566 	}
567 
568 	np->uln_next->uln_prev = np->uln_prev;
569 	np->uln_prev->uln_next = np->uln_next;
570 
571 	lp->ul_numnodes--;
572 
573 	np->uln_next = POOL_TO_MARKER(lp->ul_pool);
574 	np->uln_prev = NULL;
575 }
576 
577 void *
578 uu_list_teardown(uu_list_t *lp, void **cookie)
579 {
580 	void *ep;
581 
582 	/*
583 	 * XXX: disable list modification until list is empty
584 	 */
585 	if (lp->ul_debug && *cookie != NULL)
586 		uu_panic("uu_list_teardown(%p, %p): unexpected cookie\n",
587 		    (void *)lp, (void *)cookie);
588 
589 	ep = uu_list_first(lp);
590 	if (ep)
591 		uu_list_remove(lp, ep);
592 	return (ep);
593 }
594 
595 int
596 uu_list_insert_before(uu_list_t *lp, void *target, void *elem)
597 {
598 	uu_list_node_impl_t *np = ELEM_TO_NODE(lp, target);
599 
600 	if (target == NULL)
601 		np = &lp->ul_null_node;
602 
603 	if (lp->ul_debug) {
604 		if (np->uln_prev == NULL)
605 			uu_panic("uu_list_insert_before(%p, %p, %p): %p is "
606 			    "not currently on a list\n",
607 			    (void *)lp, target, elem, target);
608 	}
609 	if (lp->ul_sorted) {
610 		if (lp->ul_debug)
611 			uu_panic("uu_list_insert_before(%p, ...): list is "
612 			    "UU_LIST_SORTED\n", (void *)lp);
613 		uu_set_error(UU_ERROR_NOT_SUPPORTED);
614 		return (-1);
615 	}
616 
617 	list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);
618 	return (0);
619 }
620 
621 int
622 uu_list_insert_after(uu_list_t *lp, void *target, void *elem)
623 {
624 	uu_list_node_impl_t *np = ELEM_TO_NODE(lp, target);
625 
626 	if (target == NULL)
627 		np = &lp->ul_null_node;
628 
629 	if (lp->ul_debug) {
630 		if (np->uln_prev == NULL)
631 			uu_panic("uu_list_insert_after(%p, %p, %p): %p is "
632 			    "not currently on a list\n",
633 			    (void *)lp, target, elem, target);
634 	}
635 	if (lp->ul_sorted) {
636 		if (lp->ul_debug)
637 			uu_panic("uu_list_insert_after(%p, ...): list is "
638 			    "UU_LIST_SORTED\n", (void *)lp);
639 		uu_set_error(UU_ERROR_NOT_SUPPORTED);
640 		return (-1);
641 	}
642 
643 	list_insert(lp, ELEM_TO_NODE(lp, elem), np, np->uln_next);
644 	return (0);
645 }
646 
647 size_t
648 uu_list_numnodes(uu_list_t *lp)
649 {
650 	return (lp->ul_numnodes);
651 }
652 
653 void *
654 uu_list_first(uu_list_t *lp)
655 {
656 	uu_list_node_impl_t *n = lp->ul_null_node.uln_next;
657 	if (n == &lp->ul_null_node)
658 		return (NULL);
659 	return (NODE_TO_ELEM(lp, n));
660 }
661 
662 void *
663 uu_list_last(uu_list_t *lp)
664 {
665 	uu_list_node_impl_t *n = lp->ul_null_node.uln_prev;
666 	if (n == &lp->ul_null_node)
667 		return (NULL);
668 	return (NODE_TO_ELEM(lp, n));
669 }
670 
671 void *
672 uu_list_next(uu_list_t *lp, void *elem)
673 {
674 	uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);
675 
676 	n = n->uln_next;
677 	if (n == &lp->ul_null_node)
678 		return (NULL);
679 	return (NODE_TO_ELEM(lp, n));
680 }
681 
682 void *
683 uu_list_prev(uu_list_t *lp, void *elem)
684 {
685 	uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);
686 
687 	n = n->uln_prev;
688 	if (n == &lp->ul_null_node)
689 		return (NULL);
690 	return (NODE_TO_ELEM(lp, n));
691 }
692 
693 /*
694  * called from uu_lockup() and uu_release(), as part of our fork1()-safety.
695  */
696 void
697 uu_list_lockup(void)
698 {
699 	uu_list_pool_t *pp;
700 
701 	(void) pthread_mutex_lock(&uu_lpool_list_lock);
702 	for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;
703 	    pp = pp->ulp_next)
704 		(void) pthread_mutex_lock(&pp->ulp_lock);
705 }
706 
707 void
708 uu_list_release(void)
709 {
710 	uu_list_pool_t *pp;
711 
712 	for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;
713 	    pp = pp->ulp_next)
714 		(void) pthread_mutex_unlock(&pp->ulp_lock);
715 	(void) pthread_mutex_unlock(&uu_lpool_list_lock);
716 }
717