1 /* $NetBSD: subr_kmem.c,v 1.28 2009/06/03 22:54:51 jnemeth 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.28 2009/06/03 22:54:51 jnemeth 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 #include <uvm/uvm_kmguard.h> 79 80 #include <lib/libkern/libkern.h> 81 82 #define KMEM_QUANTUM_SIZE (ALIGNBYTES + 1) 83 #define KMEM_QCACHE_MAX (KMEM_QUANTUM_SIZE * 32) 84 #define KMEM_CACHE_COUNT 16 85 86 typedef struct kmem_cache { 87 pool_cache_t kc_cache; 88 struct pool_allocator kc_pa; 89 char kc_name[12]; 90 } kmem_cache_t; 91 92 static vmem_t *kmem_arena; 93 static struct callback_entry kmem_kva_reclaim_entry; 94 95 static kmem_cache_t kmem_cache[KMEM_CACHE_COUNT + 1]; 96 static size_t kmem_cache_max; 97 static size_t kmem_cache_min; 98 static size_t kmem_cache_mask; 99 static int kmem_cache_shift; 100 101 #if defined(DEBUG) 102 int kmem_guard_depth; 103 size_t kmem_guard_size; 104 static struct uvm_kmguard kmem_guard; 105 static void *kmem_freecheck; 106 #define KMEM_POISON 107 #define KMEM_REDZONE 108 #define KMEM_SIZE 109 #define KMEM_GUARD 110 #endif /* defined(DEBUG) */ 111 112 #if defined(KMEM_POISON) 113 static void kmem_poison_fill(void *, size_t); 114 static void kmem_poison_check(void *, size_t); 115 #else /* defined(KMEM_POISON) */ 116 #define kmem_poison_fill(p, sz) /* nothing */ 117 #define kmem_poison_check(p, sz) /* nothing */ 118 #endif /* defined(KMEM_POISON) */ 119 120 #if defined(KMEM_REDZONE) 121 #define REDZONE_SIZE 1 122 #else /* defined(KMEM_REDZONE) */ 123 #define REDZONE_SIZE 0 124 #endif /* defined(KMEM_REDZONE) */ 125 126 #if defined(KMEM_SIZE) 127 #define SIZE_SIZE (max(KMEM_QUANTUM_SIZE, sizeof(size_t))) 128 static void kmem_size_set(void *, size_t); 129 static void kmem_size_check(void *, size_t); 130 #else 131 #define SIZE_SIZE 0 132 #define kmem_size_set(p, sz) /* nothing */ 133 #define kmem_size_check(p, sz) /* nothing */ 134 #endif 135 136 static vmem_addr_t kmem_backend_alloc(vmem_t *, vmem_size_t, vmem_size_t *, 137 vm_flag_t); 138 static void kmem_backend_free(vmem_t *, vmem_addr_t, vmem_size_t); 139 static int kmem_kva_reclaim_callback(struct callback_entry *, void *, void *); 140 141 static inline vm_flag_t 142 kmf_to_vmf(km_flag_t kmflags) 143 { 144 vm_flag_t vmflags; 145 146 KASSERT((kmflags & (KM_SLEEP|KM_NOSLEEP)) != 0); 147 KASSERT((~kmflags & (KM_SLEEP|KM_NOSLEEP)) != 0); 148 149 vmflags = 0; 150 if ((kmflags & KM_SLEEP) != 0) { 151 vmflags |= VM_SLEEP; 152 } 153 if ((kmflags & KM_NOSLEEP) != 0) { 154 vmflags |= VM_NOSLEEP; 155 } 156 157 return vmflags; 158 } 159 160 static void * 161 kmem_poolpage_alloc(struct pool *pool, int prflags) 162 { 163 164 KASSERT(KM_SLEEP == PR_WAITOK); 165 KASSERT(KM_NOSLEEP == PR_NOWAIT); 166 167 return (void *)vmem_alloc(kmem_arena, pool->pr_alloc->pa_pagesz, 168 kmf_to_vmf(prflags) | VM_INSTANTFIT); 169 170 } 171 172 static void 173 kmem_poolpage_free(struct pool *pool, void *addr) 174 { 175 176 vmem_free(kmem_arena, (vmem_addr_t)addr, pool->pr_alloc->pa_pagesz); 177 } 178 179 /* ---- kmem API */ 180 181 /* 182 * kmem_alloc: allocate wired memory. 183 * 184 * => must not be called from interrupt context. 185 */ 186 187 void * 188 kmem_alloc(size_t size, km_flag_t kmflags) 189 { 190 kmem_cache_t *kc; 191 uint8_t *p; 192 193 KASSERT(!cpu_intr_p()); 194 KASSERT(!cpu_softintr_p()); 195 KASSERT(size > 0); 196 197 #ifdef KMEM_GUARD 198 if (size <= kmem_guard_size) { 199 return uvm_kmguard_alloc(&kmem_guard, size, 200 (kmflags & KM_SLEEP) != 0); 201 } 202 #endif 203 204 size += REDZONE_SIZE + SIZE_SIZE; 205 if (size >= kmem_cache_min && size <= kmem_cache_max) { 206 kc = &kmem_cache[(size + kmem_cache_mask) >> kmem_cache_shift]; 207 KASSERT(size <= kc->kc_pa.pa_pagesz); 208 KASSERT(KM_SLEEP == PR_WAITOK); 209 KASSERT(KM_NOSLEEP == PR_NOWAIT); 210 kmflags &= (KM_SLEEP | KM_NOSLEEP); 211 p = pool_cache_get(kc->kc_cache, kmflags); 212 } else { 213 p = (void *)vmem_alloc(kmem_arena, size, 214 kmf_to_vmf(kmflags) | VM_INSTANTFIT); 215 } 216 if (__predict_true(p != NULL)) { 217 kmem_poison_check(p, kmem_roundup_size(size)); 218 FREECHECK_OUT(&kmem_freecheck, p); 219 kmem_size_set(p, size); 220 p = (uint8_t *)p + SIZE_SIZE; 221 } 222 return p; 223 } 224 225 /* 226 * kmem_zalloc: allocate wired memory. 227 * 228 * => must not be called from interrupt context. 229 */ 230 231 void * 232 kmem_zalloc(size_t size, km_flag_t kmflags) 233 { 234 void *p; 235 236 p = kmem_alloc(size, kmflags); 237 if (p != NULL) { 238 memset(p, 0, size); 239 } 240 return p; 241 } 242 243 /* 244 * kmem_free: free wired memory allocated by kmem_alloc. 245 * 246 * => must not be called from interrupt context. 247 */ 248 249 void 250 kmem_free(void *p, size_t size) 251 { 252 kmem_cache_t *kc; 253 254 KASSERT(!cpu_intr_p()); 255 KASSERT(!cpu_softintr_p()); 256 KASSERT(p != NULL); 257 KASSERT(size > 0); 258 259 size += SIZE_SIZE; 260 p = (uint8_t *)p - SIZE_SIZE; 261 kmem_size_check(p, size + REDZONE_SIZE); 262 263 #ifdef KMEM_GUARD 264 if (size <= kmem_guard_size) { 265 uvm_kmguard_free(&kmem_guard, size, p); 266 return; 267 } 268 #endif 269 270 FREECHECK_IN(&kmem_freecheck, p); 271 LOCKDEBUG_MEM_CHECK(p, size); 272 kmem_poison_check((char *)p + size, 273 kmem_roundup_size(size + REDZONE_SIZE) - size); 274 kmem_poison_fill(p, size); 275 size += REDZONE_SIZE; 276 if (size >= kmem_cache_min && size <= kmem_cache_max) { 277 kc = &kmem_cache[(size + kmem_cache_mask) >> kmem_cache_shift]; 278 KASSERT(size <= kc->kc_pa.pa_pagesz); 279 pool_cache_put(kc->kc_cache, p); 280 } else { 281 vmem_free(kmem_arena, (vmem_addr_t)p, size); 282 } 283 } 284 285 286 void 287 kmem_init(void) 288 { 289 kmem_cache_t *kc; 290 size_t sz; 291 int i; 292 293 #ifdef KMEM_GUARD 294 uvm_kmguard_init(&kmem_guard, &kmem_guard_depth, &kmem_guard_size, 295 kernel_map); 296 #endif 297 298 kmem_arena = vmem_create("kmem", 0, 0, KMEM_QUANTUM_SIZE, 299 kmem_backend_alloc, kmem_backend_free, NULL, KMEM_QCACHE_MAX, 300 VM_SLEEP, IPL_NONE); 301 callback_register(&vm_map_to_kernel(kernel_map)->vmk_reclaim_callback, 302 &kmem_kva_reclaim_entry, kmem_arena, kmem_kva_reclaim_callback); 303 304 /* 305 * kmem caches start at twice the size of the largest vmem qcache 306 * and end at PAGE_SIZE or earlier. assert that KMEM_QCACHE_MAX 307 * is a power of two. 308 */ 309 KASSERT(ffs(KMEM_QCACHE_MAX) != 0); 310 KASSERT(KMEM_QCACHE_MAX - (1 << (ffs(KMEM_QCACHE_MAX) - 1)) == 0); 311 kmem_cache_shift = ffs(KMEM_QCACHE_MAX); 312 kmem_cache_min = 1 << kmem_cache_shift; 313 kmem_cache_mask = kmem_cache_min - 1; 314 for (i = 1; i <= KMEM_CACHE_COUNT; i++) { 315 sz = i << kmem_cache_shift; 316 if (sz > PAGE_SIZE) { 317 break; 318 } 319 kmem_cache_max = sz; 320 kc = &kmem_cache[i]; 321 kc->kc_pa.pa_pagesz = sz; 322 kc->kc_pa.pa_alloc = kmem_poolpage_alloc; 323 kc->kc_pa.pa_free = kmem_poolpage_free; 324 sprintf(kc->kc_name, "kmem-%zu", sz); 325 kc->kc_cache = pool_cache_init(sz, 326 KMEM_QUANTUM_SIZE, 0, PR_NOALIGN | PR_NOTOUCH, 327 kc->kc_name, &kc->kc_pa, IPL_NONE, 328 NULL, NULL, NULL); 329 KASSERT(kc->kc_cache != NULL); 330 } 331 } 332 333 size_t 334 kmem_roundup_size(size_t size) 335 { 336 337 return vmem_roundup_size(kmem_arena, size); 338 } 339 340 /* ---- uvm glue */ 341 342 static vmem_addr_t 343 kmem_backend_alloc(vmem_t *dummy, vmem_size_t size, vmem_size_t *resultsize, 344 vm_flag_t vmflags) 345 { 346 uvm_flag_t uflags; 347 vaddr_t va; 348 349 KASSERT(dummy == NULL); 350 KASSERT(size != 0); 351 KASSERT((vmflags & (VM_SLEEP|VM_NOSLEEP)) != 0); 352 KASSERT((~vmflags & (VM_SLEEP|VM_NOSLEEP)) != 0); 353 354 if ((vmflags & VM_NOSLEEP) != 0) { 355 uflags = UVM_KMF_TRYLOCK | UVM_KMF_NOWAIT; 356 } else { 357 uflags = UVM_KMF_WAITVA; 358 } 359 *resultsize = size = round_page(size); 360 va = uvm_km_alloc(kernel_map, size, 0, 361 uflags | UVM_KMF_WIRED | UVM_KMF_CANFAIL); 362 if (va != 0) { 363 kmem_poison_fill((void *)va, size); 364 } 365 return (vmem_addr_t)va; 366 } 367 368 static void 369 kmem_backend_free(vmem_t *dummy, vmem_addr_t addr, vmem_size_t size) 370 { 371 372 KASSERT(dummy == NULL); 373 KASSERT(addr != 0); 374 KASSERT(size != 0); 375 KASSERT(size == round_page(size)); 376 377 kmem_poison_check((void *)addr, size); 378 uvm_km_free(kernel_map, (vaddr_t)addr, size, UVM_KMF_WIRED); 379 } 380 381 static int 382 kmem_kva_reclaim_callback(struct callback_entry *ce, void *obj, void *arg) 383 { 384 vmem_t *vm = obj; 385 386 vmem_reap(vm); 387 return CALLBACK_CHAIN_CONTINUE; 388 } 389 390 /* ---- debug */ 391 392 #if defined(KMEM_POISON) 393 394 #if defined(_LP64) 395 #define PRIME 0x9e37fffffffc0001UL 396 #else /* defined(_LP64) */ 397 #define PRIME 0x9e3779b1 398 #endif /* defined(_LP64) */ 399 400 static inline uint8_t 401 kmem_poison_pattern(const void *p) 402 { 403 404 return (uint8_t)((((uintptr_t)p) * PRIME) 405 >> ((sizeof(uintptr_t) - sizeof(uint8_t))) * CHAR_BIT); 406 } 407 408 static void 409 kmem_poison_fill(void *p, size_t sz) 410 { 411 uint8_t *cp; 412 const uint8_t *ep; 413 414 cp = p; 415 ep = cp + sz; 416 while (cp < ep) { 417 *cp = kmem_poison_pattern(cp); 418 cp++; 419 } 420 } 421 422 static void 423 kmem_poison_check(void *p, size_t sz) 424 { 425 uint8_t *cp; 426 const uint8_t *ep; 427 428 cp = p; 429 ep = cp + sz; 430 while (cp < ep) { 431 const uint8_t expected = kmem_poison_pattern(cp); 432 433 if (*cp != expected) { 434 panic("%s: %p: 0x%02x != 0x%02x\n", 435 __func__, cp, *cp, expected); 436 } 437 cp++; 438 } 439 } 440 441 #endif /* defined(KMEM_POISON) */ 442 443 #if defined(KMEM_SIZE) 444 static void 445 kmem_size_set(void *p, size_t sz) 446 { 447 448 memcpy(p, &sz, sizeof(sz)); 449 } 450 451 static void 452 kmem_size_check(void *p, size_t sz) 453 { 454 size_t psz; 455 456 memcpy(&psz, p, sizeof(psz)); 457 if (psz != sz) { 458 panic("kmem_free(%p, %zu) != allocated size %zu", 459 (uint8_t*)p + SIZE_SIZE, sz - SIZE_SIZE, psz); 460 } 461 } 462 #endif /* defined(KMEM_SIZE) */ 463