1 /* $NetBSD: subr_kmem.c,v 1.27 2009/03/29 10:51:53 ad 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.27 2009/03/29 10:51:53 ad 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(size > 0); 257 258 size += SIZE_SIZE; 259 p = (uint8_t *)p - SIZE_SIZE; 260 kmem_size_check(p, size + REDZONE_SIZE); 261 262 #ifdef KMEM_GUARD 263 if (size <= kmem_guard_size) { 264 uvm_kmguard_free(&kmem_guard, size, p); 265 return; 266 } 267 #endif 268 269 FREECHECK_IN(&kmem_freecheck, p); 270 LOCKDEBUG_MEM_CHECK(p, size); 271 kmem_poison_check((char *)p + size, 272 kmem_roundup_size(size + REDZONE_SIZE) - size); 273 kmem_poison_fill(p, size); 274 size += REDZONE_SIZE; 275 if (size >= kmem_cache_min && size <= kmem_cache_max) { 276 kc = &kmem_cache[(size + kmem_cache_mask) >> kmem_cache_shift]; 277 KASSERT(size <= kc->kc_pa.pa_pagesz); 278 pool_cache_put(kc->kc_cache, p); 279 } else { 280 vmem_free(kmem_arena, (vmem_addr_t)p, size); 281 } 282 } 283 284 285 void 286 kmem_init(void) 287 { 288 kmem_cache_t *kc; 289 size_t sz; 290 int i; 291 292 #ifdef KMEM_GUARD 293 uvm_kmguard_init(&kmem_guard, &kmem_guard_depth, &kmem_guard_size, 294 kernel_map); 295 #endif 296 297 kmem_arena = vmem_create("kmem", 0, 0, KMEM_QUANTUM_SIZE, 298 kmem_backend_alloc, kmem_backend_free, NULL, KMEM_QCACHE_MAX, 299 VM_SLEEP, IPL_NONE); 300 callback_register(&vm_map_to_kernel(kernel_map)->vmk_reclaim_callback, 301 &kmem_kva_reclaim_entry, kmem_arena, kmem_kva_reclaim_callback); 302 303 /* 304 * kmem caches start at twice the size of the largest vmem qcache 305 * and end at PAGE_SIZE or earlier. assert that KMEM_QCACHE_MAX 306 * is a power of two. 307 */ 308 KASSERT(ffs(KMEM_QCACHE_MAX) != 0); 309 KASSERT(KMEM_QCACHE_MAX - (1 << (ffs(KMEM_QCACHE_MAX) - 1)) == 0); 310 kmem_cache_shift = ffs(KMEM_QCACHE_MAX); 311 kmem_cache_min = 1 << kmem_cache_shift; 312 kmem_cache_mask = kmem_cache_min - 1; 313 for (i = 1; i <= KMEM_CACHE_COUNT; i++) { 314 sz = i << kmem_cache_shift; 315 if (sz > PAGE_SIZE) { 316 break; 317 } 318 kmem_cache_max = sz; 319 kc = &kmem_cache[i]; 320 kc->kc_pa.pa_pagesz = sz; 321 kc->kc_pa.pa_alloc = kmem_poolpage_alloc; 322 kc->kc_pa.pa_free = kmem_poolpage_free; 323 sprintf(kc->kc_name, "kmem-%zu", sz); 324 kc->kc_cache = pool_cache_init(sz, 325 KMEM_QUANTUM_SIZE, 0, PR_NOALIGN | PR_NOTOUCH, 326 kc->kc_name, &kc->kc_pa, IPL_NONE, 327 NULL, NULL, NULL); 328 KASSERT(kc->kc_cache != NULL); 329 } 330 } 331 332 size_t 333 kmem_roundup_size(size_t size) 334 { 335 336 return vmem_roundup_size(kmem_arena, size); 337 } 338 339 /* ---- uvm glue */ 340 341 static vmem_addr_t 342 kmem_backend_alloc(vmem_t *dummy, vmem_size_t size, vmem_size_t *resultsize, 343 vm_flag_t vmflags) 344 { 345 uvm_flag_t uflags; 346 vaddr_t va; 347 348 KASSERT(dummy == NULL); 349 KASSERT(size != 0); 350 KASSERT((vmflags & (VM_SLEEP|VM_NOSLEEP)) != 0); 351 KASSERT((~vmflags & (VM_SLEEP|VM_NOSLEEP)) != 0); 352 353 if ((vmflags & VM_NOSLEEP) != 0) { 354 uflags = UVM_KMF_TRYLOCK | UVM_KMF_NOWAIT; 355 } else { 356 uflags = UVM_KMF_WAITVA; 357 } 358 *resultsize = size = round_page(size); 359 va = uvm_km_alloc(kernel_map, size, 0, 360 uflags | UVM_KMF_WIRED | UVM_KMF_CANFAIL); 361 if (va != 0) { 362 kmem_poison_fill((void *)va, size); 363 } 364 return (vmem_addr_t)va; 365 } 366 367 static void 368 kmem_backend_free(vmem_t *dummy, vmem_addr_t addr, vmem_size_t size) 369 { 370 371 KASSERT(dummy == NULL); 372 KASSERT(addr != 0); 373 KASSERT(size != 0); 374 KASSERT(size == round_page(size)); 375 376 kmem_poison_check((void *)addr, size); 377 uvm_km_free(kernel_map, (vaddr_t)addr, size, UVM_KMF_WIRED); 378 } 379 380 static int 381 kmem_kva_reclaim_callback(struct callback_entry *ce, void *obj, void *arg) 382 { 383 vmem_t *vm = obj; 384 385 vmem_reap(vm); 386 return CALLBACK_CHAIN_CONTINUE; 387 } 388 389 /* ---- debug */ 390 391 #if defined(KMEM_POISON) 392 393 #if defined(_LP64) 394 #define PRIME 0x9e37fffffffc0001UL 395 #else /* defined(_LP64) */ 396 #define PRIME 0x9e3779b1 397 #endif /* defined(_LP64) */ 398 399 static inline uint8_t 400 kmem_poison_pattern(const void *p) 401 { 402 403 return (uint8_t)((((uintptr_t)p) * PRIME) 404 >> ((sizeof(uintptr_t) - sizeof(uint8_t))) * CHAR_BIT); 405 } 406 407 static void 408 kmem_poison_fill(void *p, size_t sz) 409 { 410 uint8_t *cp; 411 const uint8_t *ep; 412 413 cp = p; 414 ep = cp + sz; 415 while (cp < ep) { 416 *cp = kmem_poison_pattern(cp); 417 cp++; 418 } 419 } 420 421 static void 422 kmem_poison_check(void *p, size_t sz) 423 { 424 uint8_t *cp; 425 const uint8_t *ep; 426 427 cp = p; 428 ep = cp + sz; 429 while (cp < ep) { 430 const uint8_t expected = kmem_poison_pattern(cp); 431 432 if (*cp != expected) { 433 panic("%s: %p: 0x%02x != 0x%02x\n", 434 __func__, cp, *cp, expected); 435 } 436 cp++; 437 } 438 } 439 440 #endif /* defined(KMEM_POISON) */ 441 442 #if defined(KMEM_SIZE) 443 static void 444 kmem_size_set(void *p, size_t sz) 445 { 446 447 memcpy(p, &sz, sizeof(sz)); 448 } 449 450 static void 451 kmem_size_check(void *p, size_t sz) 452 { 453 size_t psz; 454 455 memcpy(&psz, p, sizeof(psz)); 456 if (psz != sz) { 457 panic("kmem_free(%p, %zu) != allocated size %zu", 458 (uint8_t*)p + SIZE_SIZE, sz - SIZE_SIZE, psz); 459 } 460 } 461 #endif /* defined(KMEM_SIZE) */ 462