linux_xa.c revision 1.3 1 1.3 riastrad /* $NetBSD: linux_xa.c,v 1.3 2021/12/19 12:05:25 riastradh Exp $ */
2 1.1 riastrad
3 1.1 riastrad /*-
4 1.1 riastrad * Copyright (c) 2021 The NetBSD Foundation, Inc.
5 1.1 riastrad * All rights reserved.
6 1.1 riastrad *
7 1.1 riastrad * Redistribution and use in source and binary forms, with or without
8 1.1 riastrad * modification, are permitted provided that the following conditions
9 1.1 riastrad * are met:
10 1.1 riastrad * 1. Redistributions of source code must retain the above copyright
11 1.1 riastrad * notice, this list of conditions and the following disclaimer.
12 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 riastrad * notice, this list of conditions and the following disclaimer in the
14 1.1 riastrad * documentation and/or other materials provided with the distribution.
15 1.1 riastrad *
16 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 riastrad * POSSIBILITY OF SUCH DAMAGE.
27 1.1 riastrad */
28 1.1 riastrad
29 1.1 riastrad #include <sys/cdefs.h>
30 1.3 riastrad __KERNEL_RCSID(0, "$NetBSD: linux_xa.c,v 1.3 2021/12/19 12:05:25 riastradh Exp $");
31 1.1 riastrad
32 1.3 riastrad /*
33 1.3 riastrad * This is a lame-o implementation of the Linux xarray data type, which
34 1.3 riastrad * implements a map from 64-bit integers to pointers. The operations
35 1.3 riastrad * it supports are designed to be implemented by a radix tree, but
36 1.3 riastrad * NetBSD's radixtree(9) doesn't quite support them all, and it's a bit
37 1.3 riastrad * of work to implement them, so this just uses a red/black tree
38 1.3 riastrad * instead at the cost of some performance in certain types of lookups
39 1.3 riastrad * (and negative-lookups -- finding a free key).
40 1.3 riastrad */
41 1.1 riastrad
42 1.3 riastrad #include <sys/rbtree.h>
43 1.1 riastrad
44 1.1 riastrad #include <linux/xarray.h>
45 1.1 riastrad
46 1.3 riastrad struct node {
47 1.3 riastrad struct rb_node n_rb;
48 1.3 riastrad uint64_t n_key;
49 1.3 riastrad void *n_datum;
50 1.3 riastrad };
51 1.3 riastrad
52 1.3 riastrad static int
53 1.3 riastrad compare_nodes(void *cookie, const void *va, const void *vb)
54 1.3 riastrad {
55 1.3 riastrad const struct node *a = va, *b = vb;
56 1.3 riastrad
57 1.3 riastrad if (a->n_key < b->n_key)
58 1.3 riastrad return -1;
59 1.3 riastrad if (a->n_key > b->n_key)
60 1.3 riastrad return +1;
61 1.3 riastrad return 0;
62 1.3 riastrad }
63 1.3 riastrad
64 1.3 riastrad static int
65 1.3 riastrad compare_node_key(void *cookie, const void *vn, const void *vk)
66 1.3 riastrad {
67 1.3 riastrad const struct node *n = vn;
68 1.3 riastrad const uint64_t *k = vk;
69 1.3 riastrad
70 1.3 riastrad if (n->n_key < *k)
71 1.3 riastrad return -1;
72 1.3 riastrad if (n->n_key > *k)
73 1.3 riastrad return +1;
74 1.3 riastrad return 0;
75 1.3 riastrad }
76 1.3 riastrad
77 1.3 riastrad static const rb_tree_ops_t xa_rb_ops = {
78 1.3 riastrad .rbto_compare_nodes = compare_nodes,
79 1.3 riastrad .rbto_compare_key = compare_node_key,
80 1.3 riastrad .rbto_node_offset = offsetof(struct node, n_rb),
81 1.3 riastrad };
82 1.3 riastrad
83 1.1 riastrad const struct xa_limit xa_limit_32b = { .min = 0, .max = UINT32_MAX };
84 1.1 riastrad
85 1.1 riastrad void
86 1.1 riastrad xa_init_flags(struct xarray *xa, gfp_t gfp)
87 1.1 riastrad {
88 1.1 riastrad
89 1.1 riastrad mutex_init(&xa->xa_lock, MUTEX_DEFAULT, IPL_VM);
90 1.3 riastrad rb_tree_init(&xa->xa_tree, &xa_rb_ops);
91 1.1 riastrad xa->xa_gfp = gfp;
92 1.1 riastrad }
93 1.1 riastrad
94 1.1 riastrad void
95 1.1 riastrad xa_destroy(struct xarray *xa)
96 1.1 riastrad {
97 1.3 riastrad struct node *n;
98 1.1 riastrad
99 1.3 riastrad /*
100 1.3 riastrad * Linux allows xa to remain populated on destruction; it is
101 1.3 riastrad * our job to free any internal node structures.
102 1.3 riastrad */
103 1.3 riastrad while ((n = RB_TREE_MIN(&xa->xa_tree)) != NULL) {
104 1.3 riastrad rb_tree_remove_node(&xa->xa_tree, n);
105 1.3 riastrad kmem_free(n, sizeof(*n));
106 1.3 riastrad }
107 1.1 riastrad mutex_destroy(&xa->xa_lock);
108 1.1 riastrad }
109 1.1 riastrad
110 1.1 riastrad void *
111 1.1 riastrad xa_load(struct xarray *xa, unsigned long key)
112 1.1 riastrad {
113 1.3 riastrad const uint64_t key64 = key;
114 1.3 riastrad struct node *n;
115 1.1 riastrad
116 1.1 riastrad /* XXX pserialize */
117 1.1 riastrad mutex_enter(&xa->xa_lock);
118 1.3 riastrad n = rb_tree_find_node(&xa->xa_tree, &key64);
119 1.1 riastrad mutex_exit(&xa->xa_lock);
120 1.1 riastrad
121 1.3 riastrad return n ? n->n_datum : NULL;
122 1.1 riastrad }
123 1.1 riastrad
124 1.1 riastrad void *
125 1.1 riastrad xa_store(struct xarray *xa, unsigned long key, void *datum, gfp_t gfp)
126 1.1 riastrad {
127 1.3 riastrad struct node *n, *collision;
128 1.1 riastrad
129 1.1 riastrad KASSERT(datum != NULL);
130 1.1 riastrad KASSERT(((uintptr_t)datum & 0x3) == 0);
131 1.1 riastrad
132 1.3 riastrad n = kmem_zalloc(sizeof(*n), gfp & __GFP_WAIT ? KM_SLEEP : KM_NOSLEEP);
133 1.3 riastrad if (n == NULL)
134 1.3 riastrad return XA_ERROR(-ENOMEM);
135 1.3 riastrad n->n_key = key;
136 1.3 riastrad n->n_datum = datum;
137 1.3 riastrad
138 1.3 riastrad mutex_enter(&xa->xa_lock);
139 1.3 riastrad collision = rb_tree_insert_node(&xa->xa_tree, n);
140 1.1 riastrad mutex_exit(&xa->xa_lock);
141 1.3 riastrad
142 1.3 riastrad if (collision != n) {
143 1.3 riastrad datum = collision->n_datum;
144 1.3 riastrad kmem_free(collision, sizeof(*collision));
145 1.1 riastrad }
146 1.3 riastrad return datum;
147 1.1 riastrad }
148 1.1 riastrad
149 1.1 riastrad int
150 1.1 riastrad xa_alloc(struct xarray *xa, uint32_t *idp, void *datum, struct xa_limit limit,
151 1.1 riastrad gfp_t gfp)
152 1.1 riastrad {
153 1.3 riastrad uint64_t key64 = limit.min;
154 1.3 riastrad struct node *n, *n1, *collision __diagused;
155 1.1 riastrad int error;
156 1.1 riastrad
157 1.1 riastrad KASSERTMSG(limit.min < limit.max, "min=%"PRIu32" max=%"PRIu32,
158 1.1 riastrad limit.min, limit.max);
159 1.1 riastrad
160 1.3 riastrad n = kmem_zalloc(sizeof(*n), gfp & __GFP_WAIT ? KM_SLEEP : KM_NOSLEEP);
161 1.3 riastrad if (n == NULL)
162 1.3 riastrad return -ENOMEM;
163 1.3 riastrad n->n_datum = datum;
164 1.3 riastrad
165 1.3 riastrad mutex_enter(&xa->xa_lock);
166 1.3 riastrad while ((n1 = rb_tree_find_node_geq(&xa->xa_tree, &key64)) != NULL &&
167 1.3 riastrad n1->n_key == key64) {
168 1.3 riastrad if (key64 == limit.max) {
169 1.3 riastrad error = -EBUSY;
170 1.1 riastrad goto out;
171 1.3 riastrad }
172 1.3 riastrad KASSERT(key64 < UINT32_MAX);
173 1.3 riastrad key64++;
174 1.1 riastrad }
175 1.3 riastrad /* Found a hole -- insert in it. */
176 1.3 riastrad KASSERT(n1 == NULL || n1->n_key > key64);
177 1.3 riastrad n->n_key = key64;
178 1.3 riastrad collision = rb_tree_insert_node(&xa->xa_tree, n);
179 1.3 riastrad KASSERT(collision == n);
180 1.3 riastrad error = 0;
181 1.1 riastrad out: mutex_exit(&xa->xa_lock);
182 1.1 riastrad
183 1.3 riastrad if (error)
184 1.3 riastrad return error;
185 1.3 riastrad *idp = key64;
186 1.1 riastrad return 0;
187 1.1 riastrad }
188 1.1 riastrad
189 1.1 riastrad void *
190 1.1 riastrad xa_find(struct xarray *xa, unsigned long *startp, unsigned long max,
191 1.1 riastrad unsigned tagmask)
192 1.1 riastrad {
193 1.3 riastrad uint64_t key64 = *startp;
194 1.3 riastrad struct node *n = NULL;
195 1.3 riastrad
196 1.3 riastrad KASSERT(tagmask == -1); /* not yet supported */
197 1.1 riastrad
198 1.1 riastrad mutex_enter(&xa->xa_lock);
199 1.3 riastrad n = rb_tree_find_node_geq(&xa->xa_tree, &key64);
200 1.1 riastrad mutex_exit(&xa->xa_lock);
201 1.1 riastrad
202 1.3 riastrad if (n == NULL || n->n_key > max)
203 1.3 riastrad return NULL;
204 1.3 riastrad
205 1.3 riastrad *startp = n->n_key;
206 1.3 riastrad return n->n_datum;
207 1.1 riastrad }
208 1.1 riastrad
209 1.1 riastrad void *
210 1.1 riastrad xa_find_after(struct xarray *xa, unsigned long *startp, unsigned long max,
211 1.1 riastrad unsigned tagmask)
212 1.1 riastrad {
213 1.2 riastrad unsigned long start = *startp + 1;
214 1.1 riastrad void *found;
215 1.1 riastrad
216 1.1 riastrad if (start == max)
217 1.1 riastrad return NULL;
218 1.1 riastrad found = xa_find(xa, &start, max, tagmask);
219 1.1 riastrad if (found)
220 1.1 riastrad *startp = start;
221 1.1 riastrad return found;
222 1.1 riastrad }
223 1.1 riastrad
224 1.1 riastrad void *
225 1.1 riastrad xa_erase(struct xarray *xa, unsigned long key)
226 1.1 riastrad {
227 1.3 riastrad uint64_t key64 = key;
228 1.3 riastrad struct node *n;
229 1.3 riastrad void *datum = NULL;
230 1.1 riastrad
231 1.1 riastrad mutex_enter(&xa->xa_lock);
232 1.3 riastrad n = rb_tree_find_node(&xa->xa_tree, &key64);
233 1.3 riastrad if (n)
234 1.3 riastrad rb_tree_remove_node(&xa->xa_tree, n);
235 1.1 riastrad mutex_exit(&xa->xa_lock);
236 1.1 riastrad
237 1.3 riastrad if (n) {
238 1.3 riastrad datum = n->n_datum;
239 1.3 riastrad kmem_free(n, sizeof(*n));
240 1.3 riastrad }
241 1.1 riastrad return datum;
242 1.1 riastrad }
243