Sat May 4 17:58:24 2024 UTC (28d)
radixtree: allocate memory with KM_NOSLEEP to prevent pagedaemon hangs

Revert the part of rev 1.32 (reapplying "Do away with separate pool_cache
for some kernel objects") that changed the memory allocation for radixtree
nodes from PR_NOWAIT to KM_SLEEP as part of changing from a pool to kmem.
uvm_pageinsert_tree() calls into the radixtree code while holding
the object's vmobjlock, but that same lock is taken by the pagedaemon
in the process of reclaiming pages, and if the pagedaemon happens to
choose the same object to reclaim from that uvm_pageinsert_tree()
is being called on, then these two threads will deadlock.
The previous code already handled memory allocation failures
in uvm_pageinsert_tree() so we can simply change it back to nosleep.

Fixes a hang reported by simonb@, and the fix was also tested by him.


(chs)
diff -r1.33 -r1.34 src/common/lib/libc/gen/radixtree.c

cvs diff -r1.33 -r1.34 src/common/lib/libc/gen/radixtree.c (expand / switch to unified diff)

--- src/common/lib/libc/gen/radixtree.c 2023/09/23 19:17:38 1.33
+++ src/common/lib/libc/gen/radixtree.c 2024/05/04 17:58:24 1.34
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: radixtree.c,v 1.33 2023/09/23 19:17:38 ad Exp $ */ 1/* $NetBSD: radixtree.c,v 1.34 2024/05/04 17:58:24 chs Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c)2011,2012,2013 YAMAMOTO Takashi, 4 * Copyright (c)2011,2012,2013 YAMAMOTO Takashi,
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
@@ -102,37 +102,37 @@ @@ -102,37 +102,37 @@
102 * leaf nodes with the given tag. To reduce amount of nodes to visit for 102 * leaf nodes with the given tag. To reduce amount of nodes to visit for
103 * these functions, this implementation keeps tagging information in internal 103 * these functions, this implementation keeps tagging information in internal
104 * intermediate nodes and quickly skips uninterested parts of a tree. 104 * intermediate nodes and quickly skips uninterested parts of a tree.
105 * 105 *
106 * A tree has RADIX_TREE_TAG_ID_MAX independent tag spaces, each of which are 106 * A tree has RADIX_TREE_TAG_ID_MAX independent tag spaces, each of which are
107 * identified by a zero-origin numbers, tagid. For the current implementation, 107 * identified by a zero-origin numbers, tagid. For the current implementation,
108 * RADIX_TREE_TAG_ID_MAX is 2. A set of tags is described as a bitmask tagmask, 108 * RADIX_TREE_TAG_ID_MAX is 2. A set of tags is described as a bitmask tagmask,
109 * which is a bitwise OR of (1 << tagid). 109 * which is a bitwise OR of (1 << tagid).
110 */ 110 */
111 111
112#include <sys/cdefs.h> 112#include <sys/cdefs.h>
113 113
114#if defined(_KERNEL) || defined(_STANDALONE) 114#if defined(_KERNEL) || defined(_STANDALONE)
115__KERNEL_RCSID(0, "$NetBSD: radixtree.c,v 1.33 2023/09/23 19:17:38 ad Exp $"); 115__KERNEL_RCSID(0, "$NetBSD: radixtree.c,v 1.34 2024/05/04 17:58:24 chs Exp $");
116#include <sys/param.h> 116#include <sys/param.h>
117#include <sys/errno.h> 117#include <sys/errno.h>
118#include <sys/kmem.h> 118#include <sys/kmem.h>
119#include <sys/radixtree.h> 119#include <sys/radixtree.h>
120#include <lib/libkern/libkern.h> 120#include <lib/libkern/libkern.h>
121#if defined(_STANDALONE) 121#if defined(_STANDALONE)
122#include <lib/libsa/stand.h> 122#include <lib/libsa/stand.h>
123#endif /* defined(_STANDALONE) */ 123#endif /* defined(_STANDALONE) */
124#else /* defined(_KERNEL) || defined(_STANDALONE) */ 124#else /* defined(_KERNEL) || defined(_STANDALONE) */
125__RCSID("$NetBSD: radixtree.c,v 1.33 2023/09/23 19:17:38 ad Exp $"); 125__RCSID("$NetBSD: radixtree.c,v 1.34 2024/05/04 17:58:24 chs Exp $");
126#include <assert.h> 126#include <assert.h>
127#include <errno.h> 127#include <errno.h>
128#include <stdbool.h> 128#include <stdbool.h>
129#include <stdlib.h> 129#include <stdlib.h>
130#include <string.h> 130#include <string.h>
131#if 1 131#if 1
132#define KASSERT assert 132#define KASSERT assert
133#else 133#else
134#define KASSERT(a) /* nothing */ 134#define KASSERT(a) /* nothing */
135#endif 135#endif
136#endif /* defined(_KERNEL) || defined(_STANDALONE) */ 136#endif /* defined(_KERNEL) || defined(_STANDALONE) */
137 137
138#include <sys/radixtree.h> 138#include <sys/radixtree.h>
@@ -400,29 +400,30 @@ radix_tree_node_count_ptrs(const struct  @@ -400,29 +400,30 @@ radix_tree_node_count_ptrs(const struct
400 for (i = c = 0; i < RADIX_TREE_PTR_PER_NODE; i++) { 400 for (i = c = 0; i < RADIX_TREE_PTR_PER_NODE; i++) {
401 c += (n->n_ptrs[i] != NULL); 401 c += (n->n_ptrs[i] != NULL);
402 } 402 }
403 return c; 403 return c;
404} 404}
405 405
406static struct radix_tree_node * 406static struct radix_tree_node *
407radix_tree_alloc_node(void) 407radix_tree_alloc_node(void)
408{ 408{
409 struct radix_tree_node *n; 409 struct radix_tree_node *n;
410 410
411#if defined(_KERNEL) 411#if defined(_KERNEL)
412 /* 412 /*
413 * note that kmem_alloc can block. 413 * We must not block waiting for memory because this function
 414 * can be called in contexts where waiting for memory is illegal.
414 */ 415 */
415 n = kmem_intr_alloc(sizeof(struct radix_tree_node), KM_SLEEP); 416 n = kmem_intr_alloc(sizeof(struct radix_tree_node), KM_NOSLEEP);
416#elif defined(_STANDALONE) 417#elif defined(_STANDALONE)
417 n = alloc(sizeof(*n)); 418 n = alloc(sizeof(*n));
418#else /* defined(_STANDALONE) */ 419#else /* defined(_STANDALONE) */
419 n = malloc(sizeof(*n)); 420 n = malloc(sizeof(*n));
420#endif /* defined(_STANDALONE) */ 421#endif /* defined(_STANDALONE) */
421 if (n != NULL) { 422 if (n != NULL) {
422 radix_tree_node_init(n); 423 radix_tree_node_init(n);
423 } 424 }
424 KASSERT(n == NULL || radix_tree_sum_node(n) == 0); 425 KASSERT(n == NULL || radix_tree_sum_node(n) == 0);
425 return n; 426 return n;
426} 427}
427 428
428static void 429static void