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 http://www.opensolaris.org/os/licensing. 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 (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <sys/types.h> 26 #include <sys/id_space.h> 27 #include <sys/debug.h> 28 29 /* 30 * ID Spaces 31 * 32 * The id_space_t provides a simple implementation of a managed range of 33 * integer identifiers using a vmem arena. An ID space guarantees that the 34 * next identifer returned by an allocation is larger than the previous one, 35 * unless there are no larger slots remaining in the range. In this case, 36 * the ID space will return the first available slot in the lower part of the 37 * range (viewing the previous identifier as a partitioning element). If no 38 * slots are available, id_alloc()/id_allocff() will sleep until an 39 * identifier becomes available. Accordingly, id_space allocations must be 40 * initiated from contexts where sleeping is acceptable. id_alloc_nosleep()/ 41 * id_allocff_nosleep() will return -1 if no slots are available or if the 42 * system is low on memory. If id_alloc_nosleep() fails, callers should 43 * not try to extend the ID space. This is to avoid making a possible 44 * low-memory situation worse. 45 * 46 * As an ID space is designed for representing a range of id_t's, there 47 * is a preexisting maximal range: [0, MAXUID]. ID space requests outside 48 * that range will fail on a DEBUG kernel. The id_allocff*() functions 49 * return the first available id, and should be used when there is benefit 50 * to having a compact allocated range. 51 * 52 * (Presently, the id_space_t abstraction supports only direct allocations; ID 53 * reservation, in which an ID is allocated but placed in a internal 54 * dictionary for later use, should be added when a consuming subsystem 55 * arrives.) 56 * 57 * This code is also shared with userland. In userland, we don't have the same 58 * ability to have sleeping variants, so we effectively turn the normal 59 * versions without _nosleep into _nosleep. 60 */ 61 62 #define ID_TO_ADDR(id) ((void *)(uintptr_t)(id + 1)) 63 #define ADDR_TO_ID(addr) ((id_t)((uintptr_t)addr - 1)) 64 65 /* 66 * Create an arena to represent the range [low, high). 67 * Caller must be in a context in which VM_SLEEP is legal, 68 * for the kernel. Always VM_NOSLEEP in userland. 69 */ 70 id_space_t * 71 id_space_create(const char *name, id_t low, id_t high) 72 { 73 #ifdef _KERNEL 74 int flag = VM_SLEEP; 75 #else 76 int flag = VM_NOSLEEP; 77 #endif 78 ASSERT(low >= 0); 79 ASSERT(low < high); 80 81 return (vmem_create(name, ID_TO_ADDR(low), high - low, 1, 82 NULL, NULL, NULL, 0, flag | VMC_IDENTIFIER)); 83 } 84 85 /* 86 * Destroy a previously created ID space. 87 * No restrictions on caller's context. 88 */ 89 void 90 id_space_destroy(id_space_t *isp) 91 { 92 vmem_destroy(isp); 93 } 94 95 void 96 id_space_extend(id_space_t *isp, id_t low, id_t high) 97 { 98 #ifdef _KERNEL 99 int flag = VM_SLEEP; 100 #else 101 int flag = VM_NOSLEEP; 102 #endif 103 (void) vmem_add(isp, ID_TO_ADDR(low), high - low, flag); 104 } 105 106 /* 107 * Allocate an id_t from specified ID space. 108 * Caller must be in a context in which VM_SLEEP is legal. 109 */ 110 id_t 111 id_alloc(id_space_t *isp) 112 { 113 #ifdef _KERNEL 114 int flag = VM_SLEEP; 115 #else 116 int flag = VM_NOSLEEP; 117 #endif 118 return (ADDR_TO_ID(vmem_alloc(isp, 1, flag | VM_NEXTFIT))); 119 } 120 121 /* 122 * Allocate an id_t from specified ID space. 123 * Returns -1 on failure (see module block comments for more information on 124 * failure modes). 125 */ 126 id_t 127 id_alloc_nosleep(id_space_t *isp) 128 { 129 return (ADDR_TO_ID(vmem_alloc(isp, 1, VM_NOSLEEP | VM_NEXTFIT))); 130 } 131 132 /* 133 * Allocate an id_t from specified ID space using FIRSTFIT. 134 * Caller must be in a context in which VM_SLEEP is legal. 135 */ 136 id_t 137 id_allocff(id_space_t *isp) 138 { 139 #ifdef _KERNEL 140 int flag = VM_SLEEP; 141 #else 142 int flag = VM_NOSLEEP; 143 #endif 144 return (ADDR_TO_ID(vmem_alloc(isp, 1, flag | VM_FIRSTFIT))); 145 } 146 147 /* 148 * Allocate an id_t from specified ID space using FIRSTFIT 149 * Returns -1 on failure (see module block comments for more information on 150 * failure modes). 151 */ 152 id_t 153 id_allocff_nosleep(id_space_t *isp) 154 { 155 return (ADDR_TO_ID(vmem_alloc(isp, 1, VM_NOSLEEP | VM_FIRSTFIT))); 156 } 157 158 /* 159 * Allocate a specific identifier if possible, returning the id if 160 * successful, or -1 on failure. 161 */ 162 id_t 163 id_alloc_specific_nosleep(id_space_t *isp, id_t id) 164 { 165 void *minaddr = ID_TO_ADDR(id); 166 void *maxaddr = ID_TO_ADDR(id + 1); 167 168 /* 169 * Note that even though we're vmem_free()ing this later, it 170 * should be OK, since there's no quantum cache. 171 */ 172 return (ADDR_TO_ID(vmem_xalloc(isp, 1, 1, 0, 0, 173 minaddr, maxaddr, VM_NOSLEEP))); 174 } 175 176 /* 177 * Free a previously allocated ID. 178 * No restrictions on caller's context. 179 */ 180 void 181 id_free(id_space_t *isp, id_t id) 182 { 183 vmem_free(isp, ID_TO_ADDR(id), 1); 184 } 185