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 58 #define ID_TO_ADDR(id) ((void *)(uintptr_t)(id + 1)) 59 #define ADDR_TO_ID(addr) ((id_t)((uintptr_t)addr - 1)) 60 61 /* 62 * Create an arena to represent the range [low, high). 63 * Caller must be in a context in which VM_SLEEP is legal. 64 */ 65 id_space_t * 66 id_space_create(const char *name, id_t low, id_t high) 67 { 68 ASSERT(low >= 0); 69 ASSERT(low < high); 70 71 return (vmem_create(name, ID_TO_ADDR(low), high - low, 1, 72 NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER)); 73 } 74 75 /* 76 * Destroy a previously created ID space. 77 * No restrictions on caller's context. 78 */ 79 void 80 id_space_destroy(id_space_t *isp) 81 { 82 vmem_destroy(isp); 83 } 84 85 void 86 id_space_extend(id_space_t *isp, id_t low, id_t high) 87 { 88 (void) vmem_add(isp, ID_TO_ADDR(low), high - low, VM_SLEEP); 89 } 90 91 /* 92 * Allocate an id_t from specified ID space. 93 * Caller must be in a context in which VM_SLEEP is legal. 94 */ 95 id_t 96 id_alloc(id_space_t *isp) 97 { 98 return (ADDR_TO_ID(vmem_alloc(isp, 1, VM_SLEEP | VM_NEXTFIT))); 99 } 100 101 /* 102 * Allocate an id_t from specified ID space. 103 * Returns -1 on failure (see module block comments for more information on 104 * failure modes). 105 */ 106 id_t 107 id_alloc_nosleep(id_space_t *isp) 108 { 109 return (ADDR_TO_ID(vmem_alloc(isp, 1, VM_NOSLEEP | VM_NEXTFIT))); 110 } 111 112 /* 113 * Allocate an id_t from specified ID space using FIRSTFIT. 114 * Caller must be in a context in which VM_SLEEP is legal. 115 */ 116 id_t 117 id_allocff(id_space_t *isp) 118 { 119 return (ADDR_TO_ID(vmem_alloc(isp, 1, VM_SLEEP | VM_FIRSTFIT))); 120 } 121 122 /* 123 * Allocate an id_t from specified ID space using FIRSTFIT 124 * Returns -1 on failure (see module block comments for more information on 125 * failure modes). 126 */ 127 id_t 128 id_allocff_nosleep(id_space_t *isp) 129 { 130 return (ADDR_TO_ID(vmem_alloc(isp, 1, VM_NOSLEEP | VM_FIRSTFIT))); 131 } 132 133 /* 134 * Allocate a specific identifier if possible, returning the id if 135 * successful, or -1 on failure. 136 */ 137 id_t 138 id_alloc_specific_nosleep(id_space_t *isp, id_t id) 139 { 140 void *minaddr = ID_TO_ADDR(id); 141 void *maxaddr = ID_TO_ADDR(id + 1); 142 143 /* 144 * Note that even though we're vmem_free()ing this later, it 145 * should be OK, since there's no quantum cache. 146 */ 147 return (ADDR_TO_ID(vmem_xalloc(isp, 1, 1, 0, 0, 148 minaddr, maxaddr, VM_NOSLEEP))); 149 } 150 151 /* 152 * Free a previously allocated ID. 153 * No restrictions on caller's context. 154 */ 155 void 156 id_free(id_space_t *isp, id_t id) 157 { 158 vmem_free(isp, ID_TO_ADDR(id), 1); 159 } 160