1 /* $NetBSD: subr_kmem.c,v 1.26 2009/02/18 13:04:59 yamt Exp $ */ 2 3 /*- 4 * Copyright (c) 2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Andrew Doran. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /*- 33 * Copyright (c)2006 YAMAMOTO Takashi, 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 * SUCH DAMAGE. 56 */ 57 58 /* 59 * allocator of kernel wired memory. 60 * 61 * TODO: 62 * - worth to have "intrsafe" version? maybe.. 63 */ 64 65 #include <sys/cdefs.h> 66 __KERNEL_RCSID(0, "$NetBSD: subr_kmem.c,v 1.26 2009/02/18 13:04:59 yamt Exp $"); 67 68 #include <sys/param.h> 69 #include <sys/callback.h> 70 #include <sys/kmem.h> 71 #include <sys/vmem.h> 72 #include <sys/debug.h> 73 #include <sys/lockdebug.h> 74 #include <sys/cpu.h> 75 76 #include <uvm/uvm_extern.h> 77 #include <uvm/uvm_map.h> 78 79 #include <lib/libkern/libkern.h> 80 81 #define KMEM_QUANTUM_SIZE (ALIGNBYTES + 1) 82 #define KMEM_QCACHE_MAX (KMEM_QUANTUM_SIZE * 32) 83 #define KMEM_CACHE_COUNT 16 84 85 typedef struct kmem_cache { 86 pool_cache_t kc_cache; 87 struct pool_allocator kc_pa; 88 char kc_name[12]; 89 } kmem_cache_t; 90 91 static vmem_t *kmem_arena; 92 static struct callback_entry kmem_kva_reclaim_entry; 93 94 static kmem_cache_t kmem_cache[KMEM_CACHE_COUNT + 1]; 95 static size_t kmem_cache_max; 96 static size_t kmem_cache_min; 97 static size_t kmem_cache_mask; 98 static int kmem_cache_shift; 99 100 #if defined(DEBUG) 101 static void *kmem_freecheck; 102 #define KMEM_POISON 103 #define KMEM_REDZONE 104 #define KMEM_SIZE 105 #endif /* defined(DEBUG) */ 106 107 #if defined(KMEM_POISON) 108 static void kmem_poison_fill(void *, size_t); 109 static void kmem_poison_check(void *, size_t); 110 #else /* defined(KMEM_POISON) */ 111 #define kmem_poison_fill(p, sz) /* nothing */ 112 #define kmem_poison_check(p, sz) /* nothing */ 113 #endif /* defined(KMEM_POISON) */ 114 115 #if defined(KMEM_REDZONE) 116 #define REDZONE_SIZE 1 117 #else /* defined(KMEM_REDZONE) */ 118 #define REDZONE_SIZE 0 119 #endif /* defined(KMEM_REDZONE) */ 120 121 #if defined(KMEM_SIZE) 122 #define SIZE_SIZE (max(KMEM_QUANTUM_SIZE, sizeof(size_t))) 123 static void kmem_size_set(void *, size_t); 124 static void kmem_size_check(void *, size_t); 125 #else 126 #define SIZE_SIZE 0 127 #define kmem_size_set(p, sz) /* nothing */ 128 #define kmem_size_check(p, sz) /* nothing */ 129 #endif 130 131 static vmem_addr_t kmem_backend_alloc(vmem_t *, vmem_size_t, vmem_size_t *, 132 vm_flag_t); 133 static void kmem_backend_free(vmem_t *, vmem_addr_t, vmem_size_t); 134 static int kmem_kva_reclaim_callback(struct callback_entry *, void *, void *); 135 136 static inline vm_flag_t 137 kmf_to_vmf(km_flag_t kmflags) 138 { 139 vm_flag_t vmflags; 140 141 KASSERT((kmflags & (KM_SLEEP|KM_NOSLEEP)) != 0); 142 KASSERT((~kmflags & (KM_SLEEP|KM_NOSLEEP)) != 0); 143 144 vmflags = 0; 145 if ((kmflags & KM_SLEEP) != 0) { 146 vmflags |= VM_SLEEP; 147 } 148 if ((kmflags & KM_NOSLEEP) != 0) { 149 vmflags |= VM_NOSLEEP; 150 } 151 152 return vmflags; 153 } 154 155 static void * 156 kmem_poolpage_alloc(struct pool *pool, int prflags) 157 { 158 159 KASSERT(KM_SLEEP == PR_WAITOK); 160 KASSERT(KM_NOSLEEP == PR_NOWAIT); 161 162 return (void *)vmem_alloc(kmem_arena, pool->pr_alloc->pa_pagesz, 163 kmf_to_vmf(prflags) | VM_INSTANTFIT); 164 165 } 166 167 static void 168 kmem_poolpage_free(struct pool *pool, void *addr) 169 { 170 171 vmem_free(kmem_arena, (vmem_addr_t)addr, pool->pr_alloc->pa_pagesz); 172 } 173 174 /* ---- kmem API */ 175 176 /* 177 * kmem_alloc: allocate wired memory. 178 * 179 * => must not be called from interrupt context. 180 */ 181 182 void * 183 kmem_alloc(size_t size, km_flag_t kmflags) 184 { 185 kmem_cache_t *kc; 186 uint8_t *p; 187 188 KASSERT(!cpu_intr_p()); 189 KASSERT((curlwp->l_pflag & LP_INTR) == 0); 190 191 size += REDZONE_SIZE + SIZE_SIZE; 192 if (size >= kmem_cache_min && size <= kmem_cache_max) { 193 kc = &kmem_cache[(size + kmem_cache_mask) >> kmem_cache_shift]; 194 KASSERT(size <= kc->kc_pa.pa_pagesz); 195 KASSERT(KM_SLEEP == PR_WAITOK); 196 KASSERT(KM_NOSLEEP == PR_NOWAIT); 197 kmflags &= (KM_SLEEP | KM_NOSLEEP); 198 p = pool_cache_get(kc->kc_cache, kmflags); 199 } else { 200 p = (void *)vmem_alloc(kmem_arena, size, 201 kmf_to_vmf(kmflags) | VM_INSTANTFIT); 202 } 203 if (__predict_true(p != NULL)) { 204 kmem_poison_check(p, kmem_roundup_size(size)); 205 FREECHECK_OUT(&kmem_freecheck, p); 206 kmem_size_set(p, size); 207 p = (uint8_t *)p + SIZE_SIZE; 208 } 209 return p; 210 } 211 212 /* 213 * kmem_zalloc: allocate wired memory. 214 * 215 * => must not be called from interrupt context. 216 */ 217 218 void * 219 kmem_zalloc(size_t size, km_flag_t kmflags) 220 { 221 void *p; 222 223 p = kmem_alloc(size, kmflags); 224 if (p != NULL) { 225 memset(p, 0, size); 226 } 227 return p; 228 } 229 230 /* 231 * kmem_free: free wired memory allocated by kmem_alloc. 232 * 233 * => must not be called from interrupt context. 234 */ 235 236 void 237 kmem_free(void *p, size_t size) 238 { 239 kmem_cache_t *kc; 240 241 KASSERT(!cpu_intr_p()); 242 KASSERT((curlwp->l_pflag & LP_INTR) == 0); 243 244 size += SIZE_SIZE; 245 p = (uint8_t *)p - SIZE_SIZE; 246 kmem_size_check(p, size + REDZONE_SIZE); 247 248 FREECHECK_IN(&kmem_freecheck, p); 249 LOCKDEBUG_MEM_CHECK(p, size); 250 kmem_poison_check((char *)p + size, 251 kmem_roundup_size(size + REDZONE_SIZE) - size); 252 kmem_poison_fill(p, size); 253 size += REDZONE_SIZE; 254 if (size >= kmem_cache_min && size <= kmem_cache_max) { 255 kc = &kmem_cache[(size + kmem_cache_mask) >> kmem_cache_shift]; 256 KASSERT(size <= kc->kc_pa.pa_pagesz); 257 pool_cache_put(kc->kc_cache, p); 258 } else { 259 vmem_free(kmem_arena, (vmem_addr_t)p, size); 260 } 261 } 262 263 264 void 265 kmem_init(void) 266 { 267 kmem_cache_t *kc; 268 size_t sz; 269 int i; 270 271 kmem_arena = vmem_create("kmem", 0, 0, KMEM_QUANTUM_SIZE, 272 kmem_backend_alloc, kmem_backend_free, NULL, KMEM_QCACHE_MAX, 273 VM_SLEEP, IPL_NONE); 274 callback_register(&vm_map_to_kernel(kernel_map)->vmk_reclaim_callback, 275 &kmem_kva_reclaim_entry, kmem_arena, kmem_kva_reclaim_callback); 276 277 /* 278 * kmem caches start at twice the size of the largest vmem qcache 279 * and end at PAGE_SIZE or earlier. assert that KMEM_QCACHE_MAX 280 * is a power of two. 281 */ 282 KASSERT(ffs(KMEM_QCACHE_MAX) != 0); 283 KASSERT(KMEM_QCACHE_MAX - (1 << (ffs(KMEM_QCACHE_MAX) - 1)) == 0); 284 kmem_cache_shift = ffs(KMEM_QCACHE_MAX); 285 kmem_cache_min = 1 << kmem_cache_shift; 286 kmem_cache_mask = kmem_cache_min - 1; 287 for (i = 1; i <= KMEM_CACHE_COUNT; i++) { 288 sz = i << kmem_cache_shift; 289 if (sz > PAGE_SIZE) { 290 break; 291 } 292 kmem_cache_max = sz; 293 kc = &kmem_cache[i]; 294 kc->kc_pa.pa_pagesz = sz; 295 kc->kc_pa.pa_alloc = kmem_poolpage_alloc; 296 kc->kc_pa.pa_free = kmem_poolpage_free; 297 sprintf(kc->kc_name, "kmem-%zu", sz); 298 kc->kc_cache = pool_cache_init(sz, 299 KMEM_QUANTUM_SIZE, 0, PR_NOALIGN | PR_NOTOUCH, 300 kc->kc_name, &kc->kc_pa, IPL_NONE, 301 NULL, NULL, NULL); 302 KASSERT(kc->kc_cache != NULL); 303 } 304 } 305 306 size_t 307 kmem_roundup_size(size_t size) 308 { 309 310 return vmem_roundup_size(kmem_arena, size); 311 } 312 313 /* ---- uvm glue */ 314 315 static vmem_addr_t 316 kmem_backend_alloc(vmem_t *dummy, vmem_size_t size, vmem_size_t *resultsize, 317 vm_flag_t vmflags) 318 { 319 uvm_flag_t uflags; 320 vaddr_t va; 321 322 KASSERT(dummy == NULL); 323 KASSERT(size != 0); 324 KASSERT((vmflags & (VM_SLEEP|VM_NOSLEEP)) != 0); 325 KASSERT((~vmflags & (VM_SLEEP|VM_NOSLEEP)) != 0); 326 327 if ((vmflags & VM_NOSLEEP) != 0) { 328 uflags = UVM_KMF_TRYLOCK | UVM_KMF_NOWAIT; 329 } else { 330 uflags = UVM_KMF_WAITVA; 331 } 332 *resultsize = size = round_page(size); 333 va = uvm_km_alloc(kernel_map, size, 0, 334 uflags | UVM_KMF_WIRED | UVM_KMF_CANFAIL); 335 if (va != 0) { 336 kmem_poison_fill((void *)va, size); 337 } 338 return (vmem_addr_t)va; 339 } 340 341 static void 342 kmem_backend_free(vmem_t *dummy, vmem_addr_t addr, vmem_size_t size) 343 { 344 345 KASSERT(dummy == NULL); 346 KASSERT(addr != 0); 347 KASSERT(size != 0); 348 KASSERT(size == round_page(size)); 349 350 kmem_poison_check((void *)addr, size); 351 uvm_km_free(kernel_map, (vaddr_t)addr, size, UVM_KMF_WIRED); 352 } 353 354 static int 355 kmem_kva_reclaim_callback(struct callback_entry *ce, void *obj, void *arg) 356 { 357 vmem_t *vm = obj; 358 359 vmem_reap(vm); 360 return CALLBACK_CHAIN_CONTINUE; 361 } 362 363 /* ---- debug */ 364 365 #if defined(KMEM_POISON) 366 367 #if defined(_LP64) 368 #define PRIME 0x9e37fffffffc0001UL 369 #else /* defined(_LP64) */ 370 #define PRIME 0x9e3779b1 371 #endif /* defined(_LP64) */ 372 373 static inline uint8_t 374 kmem_poison_pattern(const void *p) 375 { 376 377 return (uint8_t)((((uintptr_t)p) * PRIME) 378 >> ((sizeof(uintptr_t) - sizeof(uint8_t))) * CHAR_BIT); 379 } 380 381 static void 382 kmem_poison_fill(void *p, size_t sz) 383 { 384 uint8_t *cp; 385 const uint8_t *ep; 386 387 cp = p; 388 ep = cp + sz; 389 while (cp < ep) { 390 *cp = kmem_poison_pattern(cp); 391 cp++; 392 } 393 } 394 395 static void 396 kmem_poison_check(void *p, size_t sz) 397 { 398 uint8_t *cp; 399 const uint8_t *ep; 400 401 cp = p; 402 ep = cp + sz; 403 while (cp < ep) { 404 const uint8_t expected = kmem_poison_pattern(cp); 405 406 if (*cp != expected) { 407 panic("%s: %p: 0x%02x != 0x%02x\n", 408 __func__, cp, *cp, expected); 409 } 410 cp++; 411 } 412 } 413 414 #endif /* defined(KMEM_POISON) */ 415 416 #if defined(KMEM_SIZE) 417 static void 418 kmem_size_set(void *p, size_t sz) 419 { 420 421 memcpy(p, &sz, sizeof(sz)); 422 } 423 424 static void 425 kmem_size_check(void *p, size_t sz) 426 { 427 size_t psz; 428 429 memcpy(&psz, p, sizeof(psz)); 430 if (psz != sz) { 431 panic("kmem_free(%p, %zu) != allocated size %zu", 432 (uint8_t*)p + SIZE_SIZE, sz - SIZE_SIZE, psz); 433 } 434 } 435 #endif /* defined(KMEM_SIZE) */ 436