11809ef78SKonstantin Belousov /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 31809ef78SKonstantin Belousov * 41809ef78SKonstantin Belousov * Copyright (c) 2019 The FreeBSD Foundation 51809ef78SKonstantin Belousov * 61809ef78SKonstantin Belousov * This software was developed by Konstantin Belousov <kib@FreeBSD.org> 71809ef78SKonstantin Belousov * under sponsorship from the FreeBSD Foundation. 81809ef78SKonstantin Belousov * 91809ef78SKonstantin Belousov * Redistribution and use in source and binary forms, with or without 101809ef78SKonstantin Belousov * modification, are permitted provided that the following conditions 111809ef78SKonstantin Belousov * are met: 121809ef78SKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 131809ef78SKonstantin Belousov * notice, this list of conditions and the following disclaimer. 141809ef78SKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 151809ef78SKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 161809ef78SKonstantin Belousov * documentation and/or other materials provided with the distribution. 171809ef78SKonstantin Belousov * 181809ef78SKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 191809ef78SKonstantin Belousov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 201809ef78SKonstantin Belousov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 211809ef78SKonstantin Belousov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 221809ef78SKonstantin Belousov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 231809ef78SKonstantin Belousov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 241809ef78SKonstantin Belousov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 251809ef78SKonstantin Belousov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 261809ef78SKonstantin Belousov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 271809ef78SKonstantin Belousov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 281809ef78SKonstantin Belousov * SUCH DAMAGE. 291809ef78SKonstantin Belousov */ 301809ef78SKonstantin Belousov 311809ef78SKonstantin Belousov #include <sys/cdefs.h> 321809ef78SKonstantin Belousov __FBSDID("$FreeBSD$"); 331809ef78SKonstantin Belousov 341809ef78SKonstantin Belousov #include <sys/param.h> 351809ef78SKonstantin Belousov #include <sys/kernel.h> 361809ef78SKonstantin Belousov #include <sys/lock.h> 371809ef78SKonstantin Belousov #include <sys/pctrie.h> 381809ef78SKonstantin Belousov #include <sys/rangeset.h> 391809ef78SKonstantin Belousov #include <vm/uma.h> 401809ef78SKonstantin Belousov 411809ef78SKonstantin Belousov #ifdef DIAGNOSTIC 421809ef78SKonstantin Belousov static void rangeset_check(struct rangeset *rs); 431809ef78SKonstantin Belousov #else 441809ef78SKonstantin Belousov #define rangeset_check(rs) 451809ef78SKonstantin Belousov #endif 461809ef78SKonstantin Belousov 471809ef78SKonstantin Belousov static uma_zone_t rs_node_zone; 481809ef78SKonstantin Belousov 491809ef78SKonstantin Belousov static void 501809ef78SKonstantin Belousov rs_rangeset_init(void *arg __unused) 511809ef78SKonstantin Belousov { 521809ef78SKonstantin Belousov 531809ef78SKonstantin Belousov rs_node_zone = uma_zcreate("rangeset pctrie nodes", 541809ef78SKonstantin Belousov pctrie_node_size(), NULL, NULL, pctrie_zone_init, NULL, 551809ef78SKonstantin Belousov UMA_ALIGN_PTR, 0); 561809ef78SKonstantin Belousov } 571809ef78SKonstantin Belousov SYSINIT(rs, SI_SUB_LOCK, SI_ORDER_ANY, rs_rangeset_init, NULL); 581809ef78SKonstantin Belousov 591809ef78SKonstantin Belousov static void * 601809ef78SKonstantin Belousov rs_node_alloc(struct pctrie *ptree) 611809ef78SKonstantin Belousov { 621809ef78SKonstantin Belousov struct rangeset *rs; 631809ef78SKonstantin Belousov 641809ef78SKonstantin Belousov rs = __containerof(ptree, struct rangeset, rs_trie); 651809ef78SKonstantin Belousov return (uma_zalloc(rs_node_zone, rs->rs_alloc_flags)); 661809ef78SKonstantin Belousov } 671809ef78SKonstantin Belousov 681809ef78SKonstantin Belousov static void 691809ef78SKonstantin Belousov rs_node_free(struct pctrie *ptree __unused, void *node) 701809ef78SKonstantin Belousov { 711809ef78SKonstantin Belousov 721809ef78SKonstantin Belousov uma_zfree(rs_node_zone, node); 731809ef78SKonstantin Belousov } 741809ef78SKonstantin Belousov 751809ef78SKonstantin Belousov void 761809ef78SKonstantin Belousov rangeset_init(struct rangeset *rs, rs_dup_data_t dup_data, 771809ef78SKonstantin Belousov rs_free_data_t free_data, void *data_ctx, u_int alloc_flags) 781809ef78SKonstantin Belousov { 791809ef78SKonstantin Belousov 801809ef78SKonstantin Belousov pctrie_init(&rs->rs_trie); 811809ef78SKonstantin Belousov rs->rs_dup_data = dup_data; 821809ef78SKonstantin Belousov rs->rs_free_data = free_data; 831809ef78SKonstantin Belousov rs->rs_data_ctx = data_ctx; 841809ef78SKonstantin Belousov rs->rs_alloc_flags = alloc_flags; 851809ef78SKonstantin Belousov } 861809ef78SKonstantin Belousov 871809ef78SKonstantin Belousov void 881809ef78SKonstantin Belousov rangeset_fini(struct rangeset *rs) 891809ef78SKonstantin Belousov { 901809ef78SKonstantin Belousov 911809ef78SKonstantin Belousov rangeset_check(rs); 921809ef78SKonstantin Belousov rangeset_remove_all(rs); 931809ef78SKonstantin Belousov } 941809ef78SKonstantin Belousov 951809ef78SKonstantin Belousov bool 961809ef78SKonstantin Belousov rangeset_check_empty(struct rangeset *rs, uint64_t start, uint64_t end) 971809ef78SKonstantin Belousov { 981809ef78SKonstantin Belousov struct rs_el *r; 991809ef78SKonstantin Belousov uint64_t *r1; 1001809ef78SKonstantin Belousov 1011809ef78SKonstantin Belousov rangeset_check(rs); 1021809ef78SKonstantin Belousov r1 = pctrie_lookup_le(&rs->rs_trie, end); 1031809ef78SKonstantin Belousov if (r1 != NULL) { 1041809ef78SKonstantin Belousov r = __containerof(r1, struct rs_el, re_start); 1051809ef78SKonstantin Belousov if (r->re_end > start) 1061809ef78SKonstantin Belousov return (false); 1071809ef78SKonstantin Belousov } 1081809ef78SKonstantin Belousov return (true); 1091809ef78SKonstantin Belousov } 1101809ef78SKonstantin Belousov 1111809ef78SKonstantin Belousov int 1121809ef78SKonstantin Belousov rangeset_insert(struct rangeset *rs, uint64_t start, uint64_t end, 1131809ef78SKonstantin Belousov void *data) 1141809ef78SKonstantin Belousov { 1151809ef78SKonstantin Belousov struct rs_el *r; 1161809ef78SKonstantin Belousov int error; 1171809ef78SKonstantin Belousov 1181809ef78SKonstantin Belousov rangeset_check(rs); 1191809ef78SKonstantin Belousov error = rangeset_remove(rs, start, end); 1201809ef78SKonstantin Belousov if (error != 0) 1211809ef78SKonstantin Belousov return (error); 1221809ef78SKonstantin Belousov r = data; 1231809ef78SKonstantin Belousov r->re_start = start; 1241809ef78SKonstantin Belousov r->re_end = end; 1251809ef78SKonstantin Belousov error = pctrie_insert(&rs->rs_trie, &r->re_start, rs_node_alloc); 1261809ef78SKonstantin Belousov rangeset_check(rs); 1271809ef78SKonstantin Belousov return (error); 1281809ef78SKonstantin Belousov } 1291809ef78SKonstantin Belousov 1301809ef78SKonstantin Belousov int 1311809ef78SKonstantin Belousov rangeset_remove_pred(struct rangeset *rs, uint64_t start, uint64_t end, 1321809ef78SKonstantin Belousov rs_pred_t pred) 1331809ef78SKonstantin Belousov { 1341809ef78SKonstantin Belousov struct rs_el *r, *rn; 1351809ef78SKonstantin Belousov uint64_t *r1; 1361809ef78SKonstantin Belousov int error; 1371809ef78SKonstantin Belousov 1381809ef78SKonstantin Belousov rangeset_check(rs); 1391809ef78SKonstantin Belousov error = 0; 1401809ef78SKonstantin Belousov for (; end > 0 && start < end;) { 1411809ef78SKonstantin Belousov r1 = pctrie_lookup_le(&rs->rs_trie, end - 1); 1421809ef78SKonstantin Belousov if (r1 == NULL) 1431809ef78SKonstantin Belousov break; 1441809ef78SKonstantin Belousov r = __containerof(r1, struct rs_el, re_start); 1451809ef78SKonstantin Belousov 1461809ef78SKonstantin Belousov /* 1471809ef78SKonstantin Belousov * ------============================--|-------|---- 1481809ef78SKonstantin Belousov * rs re s e 1491809ef78SKonstantin Belousov */ 1501809ef78SKonstantin Belousov if (r->re_end <= start) 1511809ef78SKonstantin Belousov break; 1521809ef78SKonstantin Belousov 1531809ef78SKonstantin Belousov if (r->re_end <= end) { 1541809ef78SKonstantin Belousov if (r->re_start < start) { 1551809ef78SKonstantin Belousov /* 1561809ef78SKonstantin Belousov * ------========|==============-------|---- 1571809ef78SKonstantin Belousov * rs s re e 1581809ef78SKonstantin Belousov */ 1591809ef78SKonstantin Belousov if (pred(rs->rs_data_ctx, r)) 1601809ef78SKonstantin Belousov r->re_end = start; 1611809ef78SKonstantin Belousov break; 1621809ef78SKonstantin Belousov } 1631809ef78SKonstantin Belousov 1641809ef78SKonstantin Belousov /* 1651809ef78SKonstantin Belousov * ------|--------===================----------|---- 1661809ef78SKonstantin Belousov * s rs re e 1671809ef78SKonstantin Belousov */ 1681809ef78SKonstantin Belousov end = r->re_start; 1691809ef78SKonstantin Belousov if (pred(rs->rs_data_ctx, r)) { 1701809ef78SKonstantin Belousov pctrie_remove(&rs->rs_trie, r->re_start, 1711809ef78SKonstantin Belousov rs_node_free); 1721809ef78SKonstantin Belousov rs->rs_free_data(rs->rs_data_ctx, r); 1731809ef78SKonstantin Belousov } 1741809ef78SKonstantin Belousov continue; 1751809ef78SKonstantin Belousov } 1761809ef78SKonstantin Belousov 1771809ef78SKonstantin Belousov /* 1781809ef78SKonstantin Belousov * ------|--------====================|==========---- 1791809ef78SKonstantin Belousov * s rs e re 1801809ef78SKonstantin Belousov */ 1811809ef78SKonstantin Belousov if (r->re_start >= start) { 1821809ef78SKonstantin Belousov if (pred(rs->rs_data_ctx, r)) { 1831809ef78SKonstantin Belousov pctrie_remove(&rs->rs_trie, r->re_start, 1841809ef78SKonstantin Belousov rs_node_free); 1851809ef78SKonstantin Belousov r->re_start = end; 1861809ef78SKonstantin Belousov error = pctrie_insert(&rs->rs_trie, 1871809ef78SKonstantin Belousov &r->re_start, rs_node_alloc); 1881809ef78SKonstantin Belousov /* 1891809ef78SKonstantin Belousov * The insert above must succeed 1901809ef78SKonstantin Belousov * because rs_node zone is marked 1911809ef78SKonstantin Belousov * nofree and we freed one element 1921809ef78SKonstantin Belousov * just before. 1931809ef78SKonstantin Belousov */ 1941809ef78SKonstantin Belousov MPASS(error == 0); 1951809ef78SKonstantin Belousov } else { 1961809ef78SKonstantin Belousov end = r->re_start; 1971809ef78SKonstantin Belousov } 1981809ef78SKonstantin Belousov continue; 1991809ef78SKonstantin Belousov } 2001809ef78SKonstantin Belousov 2011809ef78SKonstantin Belousov /* 2021809ef78SKonstantin Belousov * ------=========|===================|==========---- 2031809ef78SKonstantin Belousov * rs s e re 2041809ef78SKonstantin Belousov */ 2051809ef78SKonstantin Belousov if (pred(rs->rs_data_ctx, r)) { 2061809ef78SKonstantin Belousov /* 2071809ef78SKonstantin Belousov * Split. Can only happen once, and then if 2081809ef78SKonstantin Belousov * any allocation fails, the rangeset is kept 2091809ef78SKonstantin Belousov * intact. 2101809ef78SKonstantin Belousov */ 2111809ef78SKonstantin Belousov rn = rs->rs_dup_data(rs->rs_data_ctx, r); 2121809ef78SKonstantin Belousov if (rn == NULL) { 2131809ef78SKonstantin Belousov error = ENOMEM; 2141809ef78SKonstantin Belousov break; 2151809ef78SKonstantin Belousov } 2161809ef78SKonstantin Belousov rn->re_start = end; 2171809ef78SKonstantin Belousov rn->re_end = r->re_end; 2181809ef78SKonstantin Belousov error = pctrie_insert(&rs->rs_trie, &rn->re_start, 2191809ef78SKonstantin Belousov rs_node_alloc); 2201809ef78SKonstantin Belousov if (error != 0) { 2211809ef78SKonstantin Belousov rs->rs_free_data(rs->rs_data_ctx, rn); 2221809ef78SKonstantin Belousov break; 2231809ef78SKonstantin Belousov } 2241809ef78SKonstantin Belousov r->re_end = start; 2251809ef78SKonstantin Belousov } 2261809ef78SKonstantin Belousov break; 2271809ef78SKonstantin Belousov } 2281809ef78SKonstantin Belousov rangeset_check(rs); 2291809ef78SKonstantin Belousov return (error); 2301809ef78SKonstantin Belousov } 2311809ef78SKonstantin Belousov 2321809ef78SKonstantin Belousov static bool 2331809ef78SKonstantin Belousov rangeset_true_pred(void *ctx __unused, void *r __unused) 2341809ef78SKonstantin Belousov { 2351809ef78SKonstantin Belousov 2361809ef78SKonstantin Belousov return (true); 2371809ef78SKonstantin Belousov } 2381809ef78SKonstantin Belousov 2391809ef78SKonstantin Belousov int 2401809ef78SKonstantin Belousov rangeset_remove(struct rangeset *rs, uint64_t start, uint64_t end) 2411809ef78SKonstantin Belousov { 2421809ef78SKonstantin Belousov 2431809ef78SKonstantin Belousov return (rangeset_remove_pred(rs, start, end, rangeset_true_pred)); 2441809ef78SKonstantin Belousov } 2451809ef78SKonstantin Belousov 2461809ef78SKonstantin Belousov void 2471809ef78SKonstantin Belousov rangeset_remove_all(struct rangeset *rs) 2481809ef78SKonstantin Belousov { 2491809ef78SKonstantin Belousov struct rs_el *r; 2501809ef78SKonstantin Belousov uint64_t *r1; 2511809ef78SKonstantin Belousov 2521809ef78SKonstantin Belousov for (;;) { 2531809ef78SKonstantin Belousov r1 = pctrie_lookup_ge(&rs->rs_trie, 0); 2541809ef78SKonstantin Belousov if (r1 == NULL) 2551809ef78SKonstantin Belousov break; 2561809ef78SKonstantin Belousov r = __containerof(r1, struct rs_el, re_start); 2571809ef78SKonstantin Belousov pctrie_remove(&rs->rs_trie, r->re_start, rs_node_free); 2581809ef78SKonstantin Belousov rs->rs_free_data(rs->rs_data_ctx, r); 2591809ef78SKonstantin Belousov } 2601809ef78SKonstantin Belousov } 2611809ef78SKonstantin Belousov 2621809ef78SKonstantin Belousov void * 2631809ef78SKonstantin Belousov rangeset_lookup(struct rangeset *rs, uint64_t place) 2641809ef78SKonstantin Belousov { 2651809ef78SKonstantin Belousov struct rs_el *r; 2661809ef78SKonstantin Belousov uint64_t *r1; 2671809ef78SKonstantin Belousov 2681809ef78SKonstantin Belousov rangeset_check(rs); 2691809ef78SKonstantin Belousov r1 = pctrie_lookup_le(&rs->rs_trie, place); 2701809ef78SKonstantin Belousov if (r1 == NULL) 2711809ef78SKonstantin Belousov return (NULL); 2721809ef78SKonstantin Belousov r = __containerof(r1, struct rs_el, re_start); 2731809ef78SKonstantin Belousov if (r->re_end <= place) 2741809ef78SKonstantin Belousov return (NULL); 2751809ef78SKonstantin Belousov return (r); 2761809ef78SKonstantin Belousov } 2771809ef78SKonstantin Belousov 2781809ef78SKonstantin Belousov int 2791809ef78SKonstantin Belousov rangeset_copy(struct rangeset *dst_rs, struct rangeset *src_rs) 2801809ef78SKonstantin Belousov { 2811809ef78SKonstantin Belousov struct rs_el *src_r, *dst_r; 2821809ef78SKonstantin Belousov uint64_t cursor, *r1; 2831809ef78SKonstantin Belousov int error; 2841809ef78SKonstantin Belousov 2851809ef78SKonstantin Belousov MPASS(pctrie_is_empty(&dst_rs->rs_trie)); 2861809ef78SKonstantin Belousov rangeset_check(src_rs); 2871809ef78SKonstantin Belousov MPASS(dst_rs->rs_dup_data == src_rs->rs_dup_data); 2881809ef78SKonstantin Belousov 2891809ef78SKonstantin Belousov error = 0; 2901809ef78SKonstantin Belousov for (cursor = 0;; cursor = src_r->re_start + 1) { 2911809ef78SKonstantin Belousov r1 = pctrie_lookup_ge(&src_rs->rs_trie, cursor); 2921809ef78SKonstantin Belousov if (r1 == NULL) 2931809ef78SKonstantin Belousov break; 2941809ef78SKonstantin Belousov src_r = __containerof(r1, struct rs_el, re_start); 2951809ef78SKonstantin Belousov dst_r = dst_rs->rs_dup_data(dst_rs->rs_data_ctx, src_r); 2961809ef78SKonstantin Belousov if (dst_r == NULL) { 2971809ef78SKonstantin Belousov error = ENOMEM; 2981809ef78SKonstantin Belousov break; 2991809ef78SKonstantin Belousov } 3001809ef78SKonstantin Belousov error = pctrie_insert(&dst_rs->rs_trie, &dst_r->re_start, 3011809ef78SKonstantin Belousov rs_node_alloc); 3021809ef78SKonstantin Belousov if (error != 0) 3031809ef78SKonstantin Belousov break; 3041809ef78SKonstantin Belousov } 3051809ef78SKonstantin Belousov if (error != 0) 3061809ef78SKonstantin Belousov rangeset_remove_all(dst_rs); 3071809ef78SKonstantin Belousov return (error); 3081809ef78SKonstantin Belousov } 3091809ef78SKonstantin Belousov 3101809ef78SKonstantin Belousov #ifdef DIAGNOSTIC 3111809ef78SKonstantin Belousov static void 3121809ef78SKonstantin Belousov rangeset_check(struct rangeset *rs) 3131809ef78SKonstantin Belousov { 3141809ef78SKonstantin Belousov struct rs_el *r, *rp; 3151809ef78SKonstantin Belousov uint64_t cursor, *r1; 3161809ef78SKonstantin Belousov 3171809ef78SKonstantin Belousov for (cursor = 0, rp = NULL;; cursor = r->re_start + 1, rp = r) { 3181809ef78SKonstantin Belousov r1 = pctrie_lookup_ge(&rs->rs_trie, cursor); 3191809ef78SKonstantin Belousov if (r1 == NULL) 3201809ef78SKonstantin Belousov break; 3211809ef78SKonstantin Belousov r = __containerof(r1, struct rs_el, re_start); 3221809ef78SKonstantin Belousov KASSERT(r->re_start < r->re_end, 3231809ef78SKonstantin Belousov ("invalid interval rs %p elem %p (%#jx, %#jx)", 3241809ef78SKonstantin Belousov rs, r, (uintmax_t)r->re_start, (uintmax_t)r->re_end)); 3251809ef78SKonstantin Belousov if (rp != NULL) { 3261809ef78SKonstantin Belousov KASSERT(rp->re_end <= r->re_start, 3271809ef78SKonstantin Belousov ("non-ascending neighbors rs %p " 3281809ef78SKonstantin Belousov "prev elem %p (%#jx, %#jx) elem %p (%#jx, %#jx)", 3291809ef78SKonstantin Belousov rs, rp, (uintmax_t)rp->re_start, 3301809ef78SKonstantin Belousov (uintmax_t)rp->re_end, r, (uintmax_t)r->re_start, 3311809ef78SKonstantin Belousov (uintmax_t)r->re_end)); 3321809ef78SKonstantin Belousov } 3331809ef78SKonstantin Belousov } 3341809ef78SKonstantin Belousov } 3351809ef78SKonstantin Belousov #endif 3361809ef78SKonstantin Belousov 3371809ef78SKonstantin Belousov #include "opt_ddb.h" 3381809ef78SKonstantin Belousov #ifdef DDB 3391809ef78SKonstantin Belousov #include <sys/kernel.h> 3401809ef78SKonstantin Belousov #include <ddb/ddb.h> 3411809ef78SKonstantin Belousov 3421809ef78SKonstantin Belousov DB_SHOW_COMMAND(rangeset, rangeset_show_fn) 3431809ef78SKonstantin Belousov { 3441809ef78SKonstantin Belousov struct rangeset *rs; 3451809ef78SKonstantin Belousov struct rs_el *r; 3461809ef78SKonstantin Belousov uint64_t cursor, *r1; 3471809ef78SKonstantin Belousov 3481809ef78SKonstantin Belousov if (!have_addr) { 3491809ef78SKonstantin Belousov db_printf("show rangeset addr\n"); 3501809ef78SKonstantin Belousov return; 3511809ef78SKonstantin Belousov } 3521809ef78SKonstantin Belousov 3531809ef78SKonstantin Belousov rs = (struct rangeset *)addr; 3541809ef78SKonstantin Belousov db_printf("rangeset %p\n", rs); 3551809ef78SKonstantin Belousov for (cursor = 0;; cursor = r->re_start + 1) { 3561809ef78SKonstantin Belousov r1 = pctrie_lookup_ge(&rs->rs_trie, cursor); 3571809ef78SKonstantin Belousov if (r1 == NULL) 3581809ef78SKonstantin Belousov break; 3591809ef78SKonstantin Belousov r = __containerof(r1, struct rs_el, re_start); 3601809ef78SKonstantin Belousov db_printf(" el %p start %#jx end %#jx\n", 3611809ef78SKonstantin Belousov r, r->re_start, r->re_end); 3621809ef78SKonstantin Belousov } 3631809ef78SKonstantin Belousov } 3641809ef78SKonstantin Belousov #endif 365