2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2019 The FreeBSD Foundation
6 * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
7 * under sponsorship from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
34 #include <sys/param.h>
35 #include <sys/kernel.h>
37 #include <sys/pctrie.h>
38 #include <sys/rangeset.h>
42 static void rangeset_check(struct rangeset *rs);
44 #define rangeset_check(rs)
47 static uma_zone_t rs_node_zone;
50 rs_rangeset_init(void *arg __unused)
53 rs_node_zone = uma_zcreate("rangeset pctrie nodes",
54 pctrie_node_size(), NULL, NULL, pctrie_zone_init, NULL,
57 SYSINIT(rs, SI_SUB_LOCK, SI_ORDER_ANY, rs_rangeset_init, NULL);
60 rs_node_alloc(struct pctrie *ptree)
64 rs = __containerof(ptree, struct rangeset, rs_trie);
65 return (uma_zalloc(rs_node_zone, rs->rs_alloc_flags));
69 rs_node_free(struct pctrie *ptree __unused, void *node)
72 uma_zfree(rs_node_zone, node);
75 PCTRIE_DEFINE(RANGESET, rs_el, re_start, rs_node_alloc, rs_node_free);
78 rangeset_init(struct rangeset *rs, rs_dup_data_t dup_data,
79 rs_free_data_t free_data, void *data_ctx, u_int alloc_flags)
82 pctrie_init(&rs->rs_trie);
83 rs->rs_dup_data = dup_data;
84 rs->rs_free_data = free_data;
85 rs->rs_data_ctx = data_ctx;
86 rs->rs_alloc_flags = alloc_flags;
90 rangeset_fini(struct rangeset *rs)
94 rangeset_remove_all(rs);
98 rangeset_check_empty(struct rangeset *rs, uint64_t start, uint64_t end)
103 r = RANGESET_PCTRIE_LOOKUP_LE(&rs->rs_trie, end);
104 return (r == NULL || r->re_end <= start);
108 rangeset_insert(struct rangeset *rs, uint64_t start, uint64_t end,
115 error = rangeset_remove(rs, start, end);
121 error = RANGESET_PCTRIE_INSERT(&rs->rs_trie, r);
127 rangeset_remove_pred(struct rangeset *rs, uint64_t start, uint64_t end,
130 struct rs_el *r, *rn;
135 for (; end > 0 && start < end;) {
136 r = RANGESET_PCTRIE_LOOKUP_LE(&rs->rs_trie, end - 1);
141 * ------============================--|-------|----
144 if (r->re_end <= start)
147 if (r->re_end <= end) {
148 if (r->re_start < start) {
150 * ------========|==============-------|----
153 if (pred(rs->rs_data_ctx, r))
159 * ------|--------===================----------|----
163 if (pred(rs->rs_data_ctx, r)) {
164 RANGESET_PCTRIE_REMOVE(&rs->rs_trie,
166 rs->rs_free_data(rs->rs_data_ctx, r);
172 * ------|--------====================|==========----
175 if (r->re_start >= start) {
176 if (pred(rs->rs_data_ctx, r)) {
177 RANGESET_PCTRIE_REMOVE(&rs->rs_trie,
180 error = RANGESET_PCTRIE_INSERT(&rs->rs_trie, r);
182 * The insert above must succeed
183 * because rs_node zone is marked
184 * nofree and we freed one element
195 * ------=========|===================|==========----
198 if (pred(rs->rs_data_ctx, r)) {
200 * Split. Can only happen once, and then if
201 * any allocation fails, the rangeset is kept
204 rn = rs->rs_dup_data(rs->rs_data_ctx, r);
210 rn->re_end = r->re_end;
211 error = RANGESET_PCTRIE_INSERT(&rs->rs_trie, rn);
213 rs->rs_free_data(rs->rs_data_ctx, rn);
225 rangeset_true_pred(void *ctx __unused, void *r __unused)
232 rangeset_remove(struct rangeset *rs, uint64_t start, uint64_t end)
235 return (rangeset_remove_pred(rs, start, end, rangeset_true_pred));
239 rangeset_remove_all(struct rangeset *rs)
244 r = RANGESET_PCTRIE_LOOKUP_GE(&rs->rs_trie, 0);
247 RANGESET_PCTRIE_REMOVE(&rs->rs_trie, r->re_start);
248 rs->rs_free_data(rs->rs_data_ctx, r);
253 rangeset_lookup(struct rangeset *rs, uint64_t place)
258 r = RANGESET_PCTRIE_LOOKUP_LE(&rs->rs_trie, place);
261 if (r->re_end <= place)
267 rangeset_copy(struct rangeset *dst_rs, struct rangeset *src_rs)
269 struct rs_el *src_r, *dst_r;
273 MPASS(pctrie_is_empty(&dst_rs->rs_trie));
274 rangeset_check(src_rs);
275 MPASS(dst_rs->rs_dup_data == src_rs->rs_dup_data);
278 for (cursor = 0;; cursor = src_r->re_start + 1) {
279 src_r = RANGESET_PCTRIE_LOOKUP_GE(&src_rs->rs_trie, cursor);
282 dst_r = dst_rs->rs_dup_data(dst_rs->rs_data_ctx, src_r);
287 error = RANGESET_PCTRIE_INSERT(&dst_rs->rs_trie, dst_r);
292 rangeset_remove_all(dst_rs);
298 rangeset_check(struct rangeset *rs)
300 struct rs_el *r, *rp;
303 for (cursor = 0, rp = NULL;; cursor = r->re_start + 1, rp = r) {
304 r = RANGESET_PCTRIE_LOOKUP_GE(&rs->rs_trie, cursor);
307 KASSERT(r->re_start < r->re_end,
308 ("invalid interval rs %p elem %p (%#jx, %#jx)",
309 rs, r, (uintmax_t)r->re_start, (uintmax_t)r->re_end));
311 KASSERT(rp->re_end <= r->re_start,
312 ("non-ascending neighbors rs %p "
313 "prev elem %p (%#jx, %#jx) elem %p (%#jx, %#jx)",
314 rs, rp, (uintmax_t)rp->re_start,
315 (uintmax_t)rp->re_end, r, (uintmax_t)r->re_start,
316 (uintmax_t)r->re_end));
324 #include <sys/kernel.h>
327 DB_SHOW_COMMAND(rangeset, rangeset_show_fn)
334 db_printf("show rangeset addr\n");
338 rs = (struct rangeset *)addr;
339 db_printf("rangeset %p\n", rs);
340 for (cursor = 0;; cursor = r->re_start + 1) {
341 r = RANGESET_PCTRIE_LOOKUP_GE(&rs->rs_trie, cursor);
344 db_printf(" el %p start %#jx end %#jx\n",
345 r, r->re_start, r->re_end);