Sun Apr 19 21:11:43 2020 UTC ()
- Fix uneven performance with "bursty" vmem arenas.  Adjust locking so that
  the mutex is acquired and released only once in the happy path.  Align
  tags to cachelines.  Size the hash table according to the maximum count of
  boundary tags over the interval just gone, not the instantaneous count,
  and decay that maximum value by 50%+1 after each rehash.  Round up to the
  next power of two to eliminate divisions.  Do the rehash check unlocked.

- Hash bucket size is sizeof(vmem_hashlist), not size of a pointer to same.


(ad)
diff -r1.100 -r1.101 src/sys/kern/subr_vmem.c
diff -r1.3 -r1.4 src/sys/sys/vmem_impl.h

cvs diff -r1.100 -r1.101 src/sys/kern/subr_vmem.c (expand / switch to unified diff)

--- src/sys/kern/subr_vmem.c 2019/12/21 14:50:34 1.100
+++ src/sys/kern/subr_vmem.c 2020/04/19 21:11:42 1.101
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: subr_vmem.c,v 1.100 2019/12/21 14:50:34 ad Exp $ */ 1/* $NetBSD: subr_vmem.c,v 1.101 2020/04/19 21:11:42 ad Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c)2006,2007,2008,2009 YAMAMOTO Takashi, 4 * Copyright (c)2006,2007,2008,2009 YAMAMOTO Takashi,
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
@@ -36,27 +36,27 @@ @@ -36,27 +36,27 @@
36 * - A pool(9) is used for vmem boundary tags 36 * - A pool(9) is used for vmem boundary tags
37 * - During a pool get call the global vmem_btag_refill_lock is taken, 37 * - During a pool get call the global vmem_btag_refill_lock is taken,
38 * to serialize access to the allocation reserve, but no other 38 * to serialize access to the allocation reserve, but no other
39 * vmem arena locks. 39 * vmem arena locks.
40 * - During pool_put calls no vmem mutexes are locked. 40 * - During pool_put calls no vmem mutexes are locked.
41 * - pool_drain doesn't hold the pool's mutex while releasing memory to 41 * - pool_drain doesn't hold the pool's mutex while releasing memory to
42 * its backing therefore no interferance with any vmem mutexes. 42 * its backing therefore no interferance with any vmem mutexes.
43 * - The boundary tag pool is forced to put page headers into pool pages 43 * - The boundary tag pool is forced to put page headers into pool pages
44 * (PR_PHINPAGE) and not off page to avoid pool recursion. 44 * (PR_PHINPAGE) and not off page to avoid pool recursion.
45 * (due to sizeof(bt_t) it should be the case anyway) 45 * (due to sizeof(bt_t) it should be the case anyway)
46 */ 46 */
47 47
48#include <sys/cdefs.h> 48#include <sys/cdefs.h>
49__KERNEL_RCSID(0, "$NetBSD: subr_vmem.c,v 1.100 2019/12/21 14:50:34 ad Exp $"); 49__KERNEL_RCSID(0, "$NetBSD: subr_vmem.c,v 1.101 2020/04/19 21:11:42 ad Exp $");
50 50
51#if defined(_KERNEL) && defined(_KERNEL_OPT) 51#if defined(_KERNEL) && defined(_KERNEL_OPT)
52#include "opt_ddb.h" 52#include "opt_ddb.h"
53#endif /* defined(_KERNEL) && defined(_KERNEL_OPT) */ 53#endif /* defined(_KERNEL) && defined(_KERNEL_OPT) */
54 54
55#include <sys/param.h> 55#include <sys/param.h>
56#include <sys/hash.h> 56#include <sys/hash.h>
57#include <sys/queue.h> 57#include <sys/queue.h>
58#include <sys/bitops.h> 58#include <sys/bitops.h>
59 59
60#if defined(_KERNEL) 60#if defined(_KERNEL)
61#include <sys/systm.h> 61#include <sys/systm.h>
62#include <sys/kernel.h> /* hz */ 62#include <sys/kernel.h> /* hz */
@@ -195,26 +195,27 @@ static size_t vmem_btag_freelist_count = @@ -195,26 +195,27 @@ static size_t vmem_btag_freelist_count =
195static struct pool vmem_btag_pool; 195static struct pool vmem_btag_pool;
196 196
197static void 197static void
198vmem_kick_pdaemon(void) 198vmem_kick_pdaemon(void)
199{ 199{
200#if defined(_KERNEL) 200#if defined(_KERNEL)
201 uvm_kick_pdaemon(); 201 uvm_kick_pdaemon();
202#endif 202#endif
203} 203}
204 204
205/* ---- boundary tag */ 205/* ---- boundary tag */
206 206
207static int bt_refill(vmem_t *vm); 207static int bt_refill(vmem_t *vm);
 208static int bt_refill_locked(vmem_t *vm);
208 209
209static void * 210static void *
210pool_page_alloc_vmem_meta(struct pool *pp, int flags) 211pool_page_alloc_vmem_meta(struct pool *pp, int flags)
211{ 212{
212 const vm_flag_t vflags = (flags & PR_WAITOK) ? VM_SLEEP: VM_NOSLEEP; 213 const vm_flag_t vflags = (flags & PR_WAITOK) ? VM_SLEEP: VM_NOSLEEP;
213 vmem_addr_t va; 214 vmem_addr_t va;
214 int ret; 215 int ret;
215 216
216 ret = vmem_alloc(kmem_meta_arena, pp->pr_alloc->pa_pagesz, 217 ret = vmem_alloc(kmem_meta_arena, pp->pr_alloc->pa_pagesz,
217 (vflags & ~VM_FITMASK) | VM_INSTANTFIT | VM_POPULATING, &va); 218 (vflags & ~VM_FITMASK) | VM_INSTANTFIT | VM_POPULATING, &va);
218 219
219 return ret ? NULL : (void *)va; 220 return ret ? NULL : (void *)va;
220} 221}
@@ -224,33 +225,33 @@ pool_page_free_vmem_meta(struct pool *pp @@ -224,33 +225,33 @@ pool_page_free_vmem_meta(struct pool *pp
224{ 225{
225 226
226 vmem_free(kmem_meta_arena, (vmem_addr_t)v, pp->pr_alloc->pa_pagesz); 227 vmem_free(kmem_meta_arena, (vmem_addr_t)v, pp->pr_alloc->pa_pagesz);
227} 228}
228 229
229/* allocator for vmem-pool metadata */ 230/* allocator for vmem-pool metadata */
230struct pool_allocator pool_allocator_vmem_meta = { 231struct pool_allocator pool_allocator_vmem_meta = {
231 .pa_alloc = pool_page_alloc_vmem_meta, 232 .pa_alloc = pool_page_alloc_vmem_meta,
232 .pa_free = pool_page_free_vmem_meta, 233 .pa_free = pool_page_free_vmem_meta,
233 .pa_pagesz = 0 234 .pa_pagesz = 0
234}; 235};
235 236
236static int 237static int
237bt_refill(vmem_t *vm) 238bt_refill_locked(vmem_t *vm)
238{ 239{
239 bt_t *bt; 240 bt_t *bt;
240 241
241 VMEM_LOCK(vm); 242 VMEM_ASSERT_LOCKED(vm);
 243
242 if (vm->vm_nfreetags > BT_MINRESERVE) { 244 if (vm->vm_nfreetags > BT_MINRESERVE) {
243 VMEM_UNLOCK(vm); 
244 return 0; 245 return 0;
245 } 246 }
246 247
247 mutex_enter(&vmem_btag_lock); 248 mutex_enter(&vmem_btag_lock);
248 while (!LIST_EMPTY(&vmem_btag_freelist) && 249 while (!LIST_EMPTY(&vmem_btag_freelist) &&
249 vm->vm_nfreetags <= BT_MINRESERVE) { 250 vm->vm_nfreetags <= BT_MINRESERVE) {
250 bt = LIST_FIRST(&vmem_btag_freelist); 251 bt = LIST_FIRST(&vmem_btag_freelist);
251 LIST_REMOVE(bt, bt_freelist); 252 LIST_REMOVE(bt, bt_freelist);
252 LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist); 253 LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist);
253 vm->vm_nfreetags++; 254 vm->vm_nfreetags++;
254 vmem_btag_freelist_count--; 255 vmem_btag_freelist_count--;
255 VMEM_EVCNT_INCR(static_bt_inuse); 256 VMEM_EVCNT_INCR(static_bt_inuse);
256 } 257 }
@@ -259,94 +260,104 @@ bt_refill(vmem_t *vm) @@ -259,94 +260,104 @@ bt_refill(vmem_t *vm)
259 while (vm->vm_nfreetags <= BT_MINRESERVE) { 260 while (vm->vm_nfreetags <= BT_MINRESERVE) {
260 VMEM_UNLOCK(vm); 261 VMEM_UNLOCK(vm);
261 mutex_enter(&vmem_btag_refill_lock); 262 mutex_enter(&vmem_btag_refill_lock);
262 bt = pool_get(&vmem_btag_pool, PR_NOWAIT); 263 bt = pool_get(&vmem_btag_pool, PR_NOWAIT);
263 mutex_exit(&vmem_btag_refill_lock); 264 mutex_exit(&vmem_btag_refill_lock);
264 VMEM_LOCK(vm); 265 VMEM_LOCK(vm);
265 if (bt == NULL) 266 if (bt == NULL)
266 break; 267 break;
267 LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist); 268 LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist);
268 vm->vm_nfreetags++; 269 vm->vm_nfreetags++;
269 } 270 }
270 271
271 if (vm->vm_nfreetags <= BT_MINRESERVE) { 272 if (vm->vm_nfreetags <= BT_MINRESERVE) {
272 VMEM_UNLOCK(vm); 
273 return ENOMEM; 273 return ENOMEM;
274 } 274 }
275 275
276 VMEM_UNLOCK(vm); 
277 
278 if (kmem_meta_arena != NULL) { 276 if (kmem_meta_arena != NULL) {
 277 VMEM_UNLOCK(vm);
279 (void)bt_refill(kmem_arena); 278 (void)bt_refill(kmem_arena);
280 (void)bt_refill(kmem_va_meta_arena); 279 (void)bt_refill(kmem_va_meta_arena);
281 (void)bt_refill(kmem_meta_arena); 280 (void)bt_refill(kmem_meta_arena);
 281 VMEM_LOCK(vm);
282 } 282 }
283 283
284 return 0; 284 return 0;
285} 285}
286 286
 287static int
 288bt_refill(vmem_t *vm)
 289{
 290 int rv;
 291
 292 VMEM_LOCK(vm);
 293 rv = bt_refill_locked(vm);
 294 VMEM_UNLOCK(vm);
 295 return rv;
 296}
 297
287static bt_t * 298static bt_t *
288bt_alloc(vmem_t *vm, vm_flag_t flags) 299bt_alloc(vmem_t *vm, vm_flag_t flags)
289{ 300{
290 bt_t *bt; 301 bt_t *bt;
291 VMEM_LOCK(vm); 302
 303 VMEM_ASSERT_LOCKED(vm);
 304
292 while (vm->vm_nfreetags <= BT_MINRESERVE && (flags & VM_POPULATING) == 0) { 305 while (vm->vm_nfreetags <= BT_MINRESERVE && (flags & VM_POPULATING) == 0) {
293 VMEM_UNLOCK(vm); 306 if (bt_refill_locked(vm)) {
294 if (bt_refill(vm)) { 
295 if ((flags & VM_NOSLEEP) != 0) { 307 if ((flags & VM_NOSLEEP) != 0) {
296 return NULL; 308 return NULL;
297 } 309 }
298 310
299 /* 311 /*
300 * It would be nice to wait for something specific here 312 * It would be nice to wait for something specific here
301 * but there are multiple ways that a retry could 313 * but there are multiple ways that a retry could
302 * succeed and we can't wait for multiple things 314 * succeed and we can't wait for multiple things
303 * simultaneously. So we'll just sleep for an arbitrary 315 * simultaneously. So we'll just sleep for an arbitrary
304 * short period of time and retry regardless. 316 * short period of time and retry regardless.
305 * This should be a very rare case. 317 * This should be a very rare case.
306 */ 318 */
307 319
308 vmem_kick_pdaemon(); 320 vmem_kick_pdaemon();
309 kpause("btalloc", false, 1, NULL); 321 kpause("btalloc", false, 1, &vm->vm_lock);
310 } 322 }
311 VMEM_LOCK(vm); 
312 } 323 }
313 bt = LIST_FIRST(&vm->vm_freetags); 324 bt = LIST_FIRST(&vm->vm_freetags);
314 LIST_REMOVE(bt, bt_freelist); 325 LIST_REMOVE(bt, bt_freelist);
315 vm->vm_nfreetags--; 326 vm->vm_nfreetags--;
316 VMEM_UNLOCK(vm); 
317 327
318 return bt; 328 return bt;
319} 329}
320 330
321static void 331static void
322bt_free(vmem_t *vm, bt_t *bt) 332bt_free(vmem_t *vm, bt_t *bt)
323{ 333{
324 334
325 VMEM_LOCK(vm); 335 VMEM_ASSERT_LOCKED(vm);
 336
326 LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist); 337 LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist);
327 vm->vm_nfreetags++; 338 vm->vm_nfreetags++;
328 VMEM_UNLOCK(vm); 
329} 339}
330 340
331static void 341static void
332bt_freetrim(vmem_t *vm, int freelimit) 342bt_freetrim(vmem_t *vm, int freelimit)
333{ 343{
334 bt_t *t; 344 bt_t *t;
335 LIST_HEAD(, vmem_btag) tofree; 345 LIST_HEAD(, vmem_btag) tofree;
336 346
 347 VMEM_ASSERT_LOCKED(vm);
 348
337 LIST_INIT(&tofree); 349 LIST_INIT(&tofree);
338 350
339 VMEM_LOCK(vm); 
340 while (vm->vm_nfreetags > freelimit) { 351 while (vm->vm_nfreetags > freelimit) {
341 bt_t *bt = LIST_FIRST(&vm->vm_freetags); 352 bt_t *bt = LIST_FIRST(&vm->vm_freetags);
342 LIST_REMOVE(bt, bt_freelist); 353 LIST_REMOVE(bt, bt_freelist);
343 vm->vm_nfreetags--; 354 vm->vm_nfreetags--;
344 if (bt >= static_bts 355 if (bt >= static_bts
345 && bt < &static_bts[STATIC_BT_COUNT]) { 356 && bt < &static_bts[STATIC_BT_COUNT]) {
346 mutex_enter(&vmem_btag_lock); 357 mutex_enter(&vmem_btag_lock);
347 LIST_INSERT_HEAD(&vmem_btag_freelist, bt, bt_freelist); 358 LIST_INSERT_HEAD(&vmem_btag_freelist, bt, bt_freelist);
348 vmem_btag_freelist_count++; 359 vmem_btag_freelist_count++;
349 mutex_exit(&vmem_btag_lock); 360 mutex_exit(&vmem_btag_lock);
350 VMEM_EVCNT_DECR(static_bt_inuse); 361 VMEM_EVCNT_DECR(static_bt_inuse);
351 } else { 362 } else {
352 LIST_INSERT_HEAD(&tofree, bt, bt_freelist); 363 LIST_INSERT_HEAD(&tofree, bt, bt_freelist);
@@ -413,27 +424,27 @@ bt_freehead_toalloc(vmem_t *vm, vmem_siz @@ -413,27 +424,27 @@ bt_freehead_toalloc(vmem_t *vm, vmem_siz
413 424
414 return &vm->vm_freelist[idx]; 425 return &vm->vm_freelist[idx];
415} 426}
416 427
417/* ---- boundary tag hash */ 428/* ---- boundary tag hash */
418 429
419static struct vmem_hashlist * 430static struct vmem_hashlist *
420bt_hashhead(vmem_t *vm, vmem_addr_t addr) 431bt_hashhead(vmem_t *vm, vmem_addr_t addr)
421{ 432{
422 struct vmem_hashlist *list; 433 struct vmem_hashlist *list;
423 unsigned int hash; 434 unsigned int hash;
424 435
425 hash = hash32_buf(&addr, sizeof(addr), HASH32_BUF_INIT); 436 hash = hash32_buf(&addr, sizeof(addr), HASH32_BUF_INIT);
426 list = &vm->vm_hashlist[hash % vm->vm_hashsize]; 437 list = &vm->vm_hashlist[hash & vm->vm_hashmask];
427 438
428 return list; 439 return list;
429} 440}
430 441
431static bt_t * 442static bt_t *
432bt_lookupbusy(vmem_t *vm, vmem_addr_t addr) 443bt_lookupbusy(vmem_t *vm, vmem_addr_t addr)
433{ 444{
434 struct vmem_hashlist *list; 445 struct vmem_hashlist *list;
435 bt_t *bt; 446 bt_t *bt;
436 447
437 list = bt_hashhead(vm, addr); 448 list = bt_hashhead(vm, addr);
438 LIST_FOREACH(bt, list, bt_hashlist) { 449 LIST_FOREACH(bt, list, bt_hashlist) {
439 if (bt->bt_start == addr) { 450 if (bt->bt_start == addr) {
@@ -453,27 +464,29 @@ bt_rembusy(vmem_t *vm, bt_t *bt) @@ -453,27 +464,29 @@ bt_rembusy(vmem_t *vm, bt_t *bt)
453 vm->vm_nbusytag--; 464 vm->vm_nbusytag--;
454 LIST_REMOVE(bt, bt_hashlist); 465 LIST_REMOVE(bt, bt_hashlist);
455} 466}
456 467
457static void 468static void
458bt_insbusy(vmem_t *vm, bt_t *bt) 469bt_insbusy(vmem_t *vm, bt_t *bt)
459{ 470{
460 struct vmem_hashlist *list; 471 struct vmem_hashlist *list;
461 472
462 KASSERT(bt->bt_type == BT_TYPE_BUSY); 473 KASSERT(bt->bt_type == BT_TYPE_BUSY);
463 474
464 list = bt_hashhead(vm, bt->bt_start); 475 list = bt_hashhead(vm, bt->bt_start);
465 LIST_INSERT_HEAD(list, bt, bt_hashlist); 476 LIST_INSERT_HEAD(list, bt, bt_hashlist);
466 vm->vm_nbusytag++; 477 if (++vm->vm_nbusytag > vm->vm_maxbusytag) {
 478 vm->vm_maxbusytag = vm->vm_nbusytag;
 479 }
467 vm->vm_inuse += bt->bt_size; 480 vm->vm_inuse += bt->bt_size;
468} 481}
469 482
470/* ---- boundary tag list */ 483/* ---- boundary tag list */
471 484
472static void 485static void
473bt_remseg(vmem_t *vm, bt_t *bt) 486bt_remseg(vmem_t *vm, bt_t *bt)
474{ 487{
475 488
476 TAILQ_REMOVE(&vm->vm_seglist, bt, bt_seglist); 489 TAILQ_REMOVE(&vm->vm_seglist, bt, bt_seglist);
477} 490}
478 491
479static void 492static void
@@ -666,178 +679,193 @@ void @@ -666,178 +679,193 @@ void
666vmem_subsystem_init(vmem_t *vm) 679vmem_subsystem_init(vmem_t *vm)
667{ 680{
668 681
669 kmem_va_meta_arena = vmem_init(&kmem_va_meta_arena_store, "vmem-va", 682 kmem_va_meta_arena = vmem_init(&kmem_va_meta_arena_store, "vmem-va",
670 0, 0, PAGE_SIZE, vmem_alloc, vmem_free, vm, 683 0, 0, PAGE_SIZE, vmem_alloc, vmem_free, vm,
671 0, VM_NOSLEEP | VM_BOOTSTRAP | VM_LARGEIMPORT, 684 0, VM_NOSLEEP | VM_BOOTSTRAP | VM_LARGEIMPORT,
672 IPL_VM); 685 IPL_VM);
673 686
674 kmem_meta_arena = vmem_init(&kmem_meta_arena_store, "vmem-meta", 687 kmem_meta_arena = vmem_init(&kmem_meta_arena_store, "vmem-meta",
675 0, 0, PAGE_SIZE, 688 0, 0, PAGE_SIZE,
676 uvm_km_kmem_alloc, uvm_km_kmem_free, kmem_va_meta_arena, 689 uvm_km_kmem_alloc, uvm_km_kmem_free, kmem_va_meta_arena,
677 0, VM_NOSLEEP | VM_BOOTSTRAP, IPL_VM); 690 0, VM_NOSLEEP | VM_BOOTSTRAP, IPL_VM);
678 691
679 pool_init(&vmem_btag_pool, sizeof(bt_t), 0, 0, PR_PHINPAGE, 692 pool_init(&vmem_btag_pool, sizeof(bt_t), coherency_unit, 0,
680 "vmembt", &pool_allocator_vmem_meta, IPL_VM); 693 PR_PHINPAGE, "vmembt", &pool_allocator_vmem_meta, IPL_VM);
681} 694}
682#endif /* defined(_KERNEL) */ 695#endif /* defined(_KERNEL) */
683 696
684static int 697static int
685vmem_add1(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, vm_flag_t flags, 698vmem_add1(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, vm_flag_t flags,
686 int spanbttype) 699 int spanbttype)
687{ 700{
688 bt_t *btspan; 701 bt_t *btspan;
689 bt_t *btfree; 702 bt_t *btfree;
690 703
 704 VMEM_ASSERT_LOCKED(vm);
691 KASSERT((flags & (VM_SLEEP|VM_NOSLEEP)) != 0); 705 KASSERT((flags & (VM_SLEEP|VM_NOSLEEP)) != 0);
692 KASSERT((~flags & (VM_SLEEP|VM_NOSLEEP)) != 0); 706 KASSERT((~flags & (VM_SLEEP|VM_NOSLEEP)) != 0);
693 KASSERT(spanbttype == BT_TYPE_SPAN || 707 KASSERT(spanbttype == BT_TYPE_SPAN ||
694 spanbttype == BT_TYPE_SPAN_STATIC); 708 spanbttype == BT_TYPE_SPAN_STATIC);
695 709
696 btspan = bt_alloc(vm, flags); 710 btspan = bt_alloc(vm, flags);
697 if (btspan == NULL) { 711 if (btspan == NULL) {
698 return ENOMEM; 712 return ENOMEM;
699 } 713 }
700 btfree = bt_alloc(vm, flags); 714 btfree = bt_alloc(vm, flags);
701 if (btfree == NULL) { 715 if (btfree == NULL) {
702 bt_free(vm, btspan); 716 bt_free(vm, btspan);
703 return ENOMEM; 717 return ENOMEM;
704 } 718 }
705 719
706 btspan->bt_type = spanbttype; 720 btspan->bt_type = spanbttype;
707 btspan->bt_start = addr; 721 btspan->bt_start = addr;
708 btspan->bt_size = size; 722 btspan->bt_size = size;
709 723
710 btfree->bt_type = BT_TYPE_FREE; 724 btfree->bt_type = BT_TYPE_FREE;
711 btfree->bt_start = addr; 725 btfree->bt_start = addr;
712 btfree->bt_size = size; 726 btfree->bt_size = size;
713 727
714 VMEM_LOCK(vm); 
715 bt_insseg_tail(vm, btspan); 728 bt_insseg_tail(vm, btspan);
716 bt_insseg(vm, btfree, btspan); 729 bt_insseg(vm, btfree, btspan);
717 bt_insfree(vm, btfree); 730 bt_insfree(vm, btfree);
718 vm->vm_size += size; 731 vm->vm_size += size;
719 VMEM_UNLOCK(vm); 
720 732
721 return 0; 733 return 0;
722} 734}
723 735
724static void 736static void
725vmem_destroy1(vmem_t *vm) 737vmem_destroy1(vmem_t *vm)
726{ 738{
727 739
728#if defined(QCACHE) 740#if defined(QCACHE)
729 qc_destroy(vm); 741 qc_destroy(vm);
730#endif /* defined(QCACHE) */ 742#endif /* defined(QCACHE) */
731 if (vm->vm_hashlist != NULL) { 743 VMEM_LOCK(vm);
732 int i; 
733 744
734 for (i = 0; i < vm->vm_hashsize; i++) { 745 for (int i = 0; i < vm->vm_hashsize; i++) {
735 bt_t *bt; 746 bt_t *bt;
736 747
737 while ((bt = LIST_FIRST(&vm->vm_hashlist[i])) != NULL) { 748 while ((bt = LIST_FIRST(&vm->vm_hashlist[i])) != NULL) {
738 KASSERT(bt->bt_type == BT_TYPE_SPAN_STATIC); 749 KASSERT(bt->bt_type == BT_TYPE_SPAN_STATIC);
739 bt_free(vm, bt); 750 LIST_REMOVE(bt, bt_hashlist);
740 } 751 bt_free(vm, bt);
741 } 
742 if (vm->vm_hashlist != &vm->vm_hash0) { 
743 xfree(vm->vm_hashlist, 
744 sizeof(struct vmem_hashlist *) * vm->vm_hashsize); 
745 } 752 }
746 } 753 }
747 754
 755 /* bt_freetrim() drops the lock. */
748 bt_freetrim(vm, 0); 756 bt_freetrim(vm, 0);
 757 if (vm->vm_hashlist != &vm->vm_hash0) {
 758 xfree(vm->vm_hashlist,
 759 sizeof(struct vmem_hashlist) * vm->vm_hashsize);
 760 }
749 761
750 VMEM_CONDVAR_DESTROY(vm); 762 VMEM_CONDVAR_DESTROY(vm);
751 VMEM_LOCK_DESTROY(vm); 763 VMEM_LOCK_DESTROY(vm);
752 xfree(vm, sizeof(*vm)); 764 xfree(vm, sizeof(*vm));
753} 765}
754 766
755static int 767static int
756vmem_import(vmem_t *vm, vmem_size_t size, vm_flag_t flags) 768vmem_import(vmem_t *vm, vmem_size_t size, vm_flag_t flags)
757{ 769{
758 vmem_addr_t addr; 770 vmem_addr_t addr;
759 int rc; 771 int rc;
760 772
 773 VMEM_ASSERT_LOCKED(vm);
 774
761 if (vm->vm_importfn == NULL) { 775 if (vm->vm_importfn == NULL) {
762 return EINVAL; 776 return EINVAL;
763 } 777 }
764 778
765 if (vm->vm_flags & VM_LARGEIMPORT) { 779 if (vm->vm_flags & VM_LARGEIMPORT) {
766 size *= 16; 780 size *= 16;
767 } 781 }
768 782
 783 VMEM_UNLOCK(vm);
769 if (vm->vm_flags & VM_XIMPORT) { 784 if (vm->vm_flags & VM_XIMPORT) {
770 rc = __FPTRCAST(vmem_ximport_t *, vm->vm_importfn)(vm->vm_arg, 785 rc = __FPTRCAST(vmem_ximport_t *, vm->vm_importfn)(vm->vm_arg,
771 size, &size, flags, &addr); 786 size, &size, flags, &addr);
772 } else { 787 } else {
773 rc = (vm->vm_importfn)(vm->vm_arg, size, flags, &addr); 788 rc = (vm->vm_importfn)(vm->vm_arg, size, flags, &addr);
774 } 789 }
 790 VMEM_LOCK(vm);
 791
775 if (rc) { 792 if (rc) {
776 return ENOMEM; 793 return ENOMEM;
777 } 794 }
778 795
779 if (vmem_add1(vm, addr, size, flags, BT_TYPE_SPAN) != 0) { 796 if (vmem_add1(vm, addr, size, flags, BT_TYPE_SPAN) != 0) {
 797 VMEM_UNLOCK(vm);
780 (*vm->vm_releasefn)(vm->vm_arg, addr, size); 798 (*vm->vm_releasefn)(vm->vm_arg, addr, size);
 799 VMEM_LOCK(vm);
781 return ENOMEM; 800 return ENOMEM;
782 } 801 }
783 802
784 return 0; 803 return 0;
785} 804}
786 805
787static int 806static int
788vmem_rehash(vmem_t *vm, size_t newhashsize, vm_flag_t flags) 807vmem_rehash(vmem_t *vm, size_t newhashsize, vm_flag_t flags)
789{ 808{
790 bt_t *bt; 809 bt_t *bt;
791 int i; 810 int i;
792 struct vmem_hashlist *newhashlist; 811 struct vmem_hashlist *newhashlist;
793 struct vmem_hashlist *oldhashlist; 812 struct vmem_hashlist *oldhashlist;
794 size_t oldhashsize; 813 size_t oldhashsize;
795 814
796 KASSERT(newhashsize > 0); 815 KASSERT(newhashsize > 0);
797 816
 817 /* Round hash size up to a power of 2. */
 818 newhashsize = 1 << (ilog2(newhashsize) + 1);
 819
798 newhashlist = 820 newhashlist =
799 xmalloc(sizeof(struct vmem_hashlist *) * newhashsize, flags); 821 xmalloc(sizeof(struct vmem_hashlist) * newhashsize, flags);
800 if (newhashlist == NULL) { 822 if (newhashlist == NULL) {
801 return ENOMEM; 823 return ENOMEM;
802 } 824 }
803 for (i = 0; i < newhashsize; i++) { 825 for (i = 0; i < newhashsize; i++) {
804 LIST_INIT(&newhashlist[i]); 826 LIST_INIT(&newhashlist[i]);
805 } 827 }
806 828
807 if (!VMEM_TRYLOCK(vm)) { 829 VMEM_LOCK(vm);
808 xfree(newhashlist, 830 /* Decay back to a small hash slowly. */
809 sizeof(struct vmem_hashlist *) * newhashsize); 831 if (vm->vm_maxbusytag >= 2) {
810 return EBUSY; 832 vm->vm_maxbusytag = vm->vm_maxbusytag / 2 - 1;
 833 if (vm->vm_nbusytag > vm->vm_maxbusytag) {
 834 vm->vm_maxbusytag = vm->vm_nbusytag;
 835 }
 836 } else {
 837 vm->vm_maxbusytag = vm->vm_nbusytag;
811 } 838 }
812 oldhashlist = vm->vm_hashlist; 839 oldhashlist = vm->vm_hashlist;
813 oldhashsize = vm->vm_hashsize; 840 oldhashsize = vm->vm_hashsize;
814 vm->vm_hashlist = newhashlist; 841 vm->vm_hashlist = newhashlist;
815 vm->vm_hashsize = newhashsize; 842 vm->vm_hashsize = newhashsize;
 843 vm->vm_hashmask = newhashsize - 1;
816 if (oldhashlist == NULL) { 844 if (oldhashlist == NULL) {
817 VMEM_UNLOCK(vm); 845 VMEM_UNLOCK(vm);
818 return 0; 846 return 0;
819 } 847 }
820 for (i = 0; i < oldhashsize; i++) { 848 for (i = 0; i < oldhashsize; i++) {
821 while ((bt = LIST_FIRST(&oldhashlist[i])) != NULL) { 849 while ((bt = LIST_FIRST(&oldhashlist[i])) != NULL) {
822 bt_rembusy(vm, bt); /* XXX */ 850 bt_rembusy(vm, bt); /* XXX */
823 bt_insbusy(vm, bt); 851 bt_insbusy(vm, bt);
824 } 852 }
825 } 853 }
826 VMEM_UNLOCK(vm); 854 VMEM_UNLOCK(vm);
827 855
828 if (oldhashlist != &vm->vm_hash0) { 856 if (oldhashlist != &vm->vm_hash0) {
829 xfree(oldhashlist, 857 xfree(oldhashlist,
830 sizeof(struct vmem_hashlist *) * oldhashsize); 858 sizeof(struct vmem_hashlist) * oldhashsize);
831 } 859 }
832 860
833 return 0; 861 return 0;
834} 862}
835 863
836/* 864/*
837 * vmem_fit: check if a bt can satisfy the given restrictions. 865 * vmem_fit: check if a bt can satisfy the given restrictions.
838 * 866 *
839 * it's a caller's responsibility to ensure the region is big enough 867 * it's a caller's responsibility to ensure the region is big enough
840 * before calling us. 868 * before calling us.
841 */ 869 */
842 870
843static int 871static int
@@ -924,38 +952,40 @@ vmem_init(vmem_t *vm, const char *name, @@ -924,38 +952,40 @@ vmem_init(vmem_t *vm, const char *name,
924 VMEM_CONDVAR_INIT(vm, "vmem"); 952 VMEM_CONDVAR_INIT(vm, "vmem");
925 VMEM_LOCK_INIT(vm, ipl); 953 VMEM_LOCK_INIT(vm, ipl);
926 vm->vm_flags = flags; 954 vm->vm_flags = flags;
927 vm->vm_nfreetags = 0; 955 vm->vm_nfreetags = 0;
928 LIST_INIT(&vm->vm_freetags); 956 LIST_INIT(&vm->vm_freetags);
929 strlcpy(vm->vm_name, name, sizeof(vm->vm_name)); 957 strlcpy(vm->vm_name, name, sizeof(vm->vm_name));
930 vm->vm_quantum_mask = quantum - 1; 958 vm->vm_quantum_mask = quantum - 1;
931 vm->vm_quantum_shift = SIZE2ORDER(quantum); 959 vm->vm_quantum_shift = SIZE2ORDER(quantum);
932 KASSERT(ORDER2SIZE(vm->vm_quantum_shift) == quantum); 960 KASSERT(ORDER2SIZE(vm->vm_quantum_shift) == quantum);
933 vm->vm_importfn = importfn; 961 vm->vm_importfn = importfn;
934 vm->vm_releasefn = releasefn; 962 vm->vm_releasefn = releasefn;
935 vm->vm_arg = arg; 963 vm->vm_arg = arg;
936 vm->vm_nbusytag = 0; 964 vm->vm_nbusytag = 0;
 965 vm->vm_maxbusytag = 0;
937 vm->vm_size = 0; 966 vm->vm_size = 0;
938 vm->vm_inuse = 0; 967 vm->vm_inuse = 0;
939#if defined(QCACHE) 968#if defined(QCACHE)
940 qc_init(vm, qcache_max, ipl); 969 qc_init(vm, qcache_max, ipl);
941#endif /* defined(QCACHE) */ 970#endif /* defined(QCACHE) */
942 971
943 TAILQ_INIT(&vm->vm_seglist); 972 TAILQ_INIT(&vm->vm_seglist);
944 for (i = 0; i < VMEM_MAXORDER; i++) { 973 for (i = 0; i < VMEM_MAXORDER; i++) {
945 LIST_INIT(&vm->vm_freelist[i]); 974 LIST_INIT(&vm->vm_freelist[i]);
946 } 975 }
947 memset(&vm->vm_hash0, 0, sizeof(struct vmem_hashlist)); 976 memset(&vm->vm_hash0, 0, sizeof(vm->vm_hash0));
948 vm->vm_hashsize = 1; 977 vm->vm_hashsize = 1;
 978 vm->vm_hashmask = vm->vm_hashsize - 1;
949 vm->vm_hashlist = &vm->vm_hash0; 979 vm->vm_hashlist = &vm->vm_hash0;
950 980
951 if (size != 0) { 981 if (size != 0) {
952 if (vmem_add(vm, base, size, flags) != 0) { 982 if (vmem_add(vm, base, size, flags) != 0) {
953 vmem_destroy1(vm); 983 vmem_destroy1(vm);
954 return NULL; 984 return NULL;
955 } 985 }
956 } 986 }
957 987
958#if defined(_KERNEL) 988#if defined(_KERNEL)
959 if (flags & VM_BOOTSTRAP) { 989 if (flags & VM_BOOTSTRAP) {
960 bt_refill(vm); 990 bt_refill(vm);
961 } 991 }
@@ -1097,45 +1127,47 @@ vmem_xalloc(vmem_t *vm, const vmem_size_ @@ -1097,45 +1127,47 @@ vmem_xalloc(vmem_t *vm, const vmem_size_
1097 KASSERT((nocross & (nocross - 1)) == 0); 1127 KASSERT((nocross & (nocross - 1)) == 0);
1098 KASSERT((align == 0 && phase == 0) || phase < align); 1128 KASSERT((align == 0 && phase == 0) || phase < align);
1099 KASSERT(nocross == 0 || nocross >= size); 1129 KASSERT(nocross == 0 || nocross >= size);
1100 KASSERT(minaddr <= maxaddr); 1130 KASSERT(minaddr <= maxaddr);
1101 KASSERT(!VMEM_CROSS_P(phase, phase + size - 1, nocross)); 1131 KASSERT(!VMEM_CROSS_P(phase, phase + size - 1, nocross));
1102 1132
1103 if (align == 0) { 1133 if (align == 0) {
1104 align = vm->vm_quantum_mask + 1; 1134 align = vm->vm_quantum_mask + 1;
1105 } 1135 }
1106 1136
1107 /* 1137 /*
1108 * allocate boundary tags before acquiring the vmem lock. 1138 * allocate boundary tags before acquiring the vmem lock.
1109 */ 1139 */
 1140 VMEM_LOCK(vm);
1110 btnew = bt_alloc(vm, flags); 1141 btnew = bt_alloc(vm, flags);
1111 if (btnew == NULL) { 1142 if (btnew == NULL) {
 1143 VMEM_UNLOCK(vm);
1112 return ENOMEM; 1144 return ENOMEM;
1113 } 1145 }
1114 btnew2 = bt_alloc(vm, flags); /* XXX not necessary if no restrictions */ 1146 btnew2 = bt_alloc(vm, flags); /* XXX not necessary if no restrictions */
1115 if (btnew2 == NULL) { 1147 if (btnew2 == NULL) {
1116 bt_free(vm, btnew); 1148 bt_free(vm, btnew);
 1149 VMEM_UNLOCK(vm);
1117 return ENOMEM; 1150 return ENOMEM;
1118 } 1151 }
1119 1152
1120 /* 1153 /*
1121 * choose a free block from which we allocate. 1154 * choose a free block from which we allocate.
1122 */ 1155 */
1123retry_strat: 1156retry_strat:
1124 first = bt_freehead_toalloc(vm, size, strat); 1157 first = bt_freehead_toalloc(vm, size, strat);
1125 end = &vm->vm_freelist[VMEM_MAXORDER]; 1158 end = &vm->vm_freelist[VMEM_MAXORDER];
1126retry: 1159retry:
1127 bt = NULL; 1160 bt = NULL;
1128 VMEM_LOCK(vm); 
1129 vmem_check(vm); 1161 vmem_check(vm);
1130 if (strat == VM_INSTANTFIT) { 1162 if (strat == VM_INSTANTFIT) {
1131 /* 1163 /*
1132 * just choose the first block which satisfies our restrictions. 1164 * just choose the first block which satisfies our restrictions.
1133 * 1165 *
1134 * note that we don't need to check the size of the blocks 1166 * note that we don't need to check the size of the blocks
1135 * because any blocks found on these list should be larger than 1167 * because any blocks found on these list should be larger than
1136 * the given size. 1168 * the given size.
1137 */ 1169 */
1138 for (list = first; list < end; list++) { 1170 for (list = first; list < end; list++) {
1139 bt = LIST_FIRST(list); 1171 bt = LIST_FIRST(list);
1140 if (bt != NULL) { 1172 if (bt != NULL) {
1141 rc = vmem_fit(bt, size, align, phase, 1173 rc = vmem_fit(bt, size, align, phase,
@@ -1166,58 +1198,56 @@ retry: @@ -1166,58 +1198,56 @@ retry:
1166 */ 1198 */
1167 for (list = first; list < end; list++) { 1199 for (list = first; list < end; list++) {
1168 LIST_FOREACH(bt, list, bt_freelist) { 1200 LIST_FOREACH(bt, list, bt_freelist) {
1169 if (bt->bt_size >= size) { 1201 if (bt->bt_size >= size) {
1170 rc = vmem_fit(bt, size, align, phase, 1202 rc = vmem_fit(bt, size, align, phase,
1171 nocross, minaddr, maxaddr, &start); 1203 nocross, minaddr, maxaddr, &start);
1172 if (rc == 0) { 1204 if (rc == 0) {
1173 goto gotit; 1205 goto gotit;
1174 } 1206 }
1175 } 1207 }
1176 } 1208 }
1177 } 1209 }
1178 } 1210 }
1179 VMEM_UNLOCK(vm); 
1180#if 1 1211#if 1
1181 if (strat == VM_INSTANTFIT) { 1212 if (strat == VM_INSTANTFIT) {
1182 strat = VM_BESTFIT; 1213 strat = VM_BESTFIT;
1183 goto retry_strat; 1214 goto retry_strat;
1184 } 1215 }
1185#endif 1216#endif
1186 if (align != vm->vm_quantum_mask + 1 || phase != 0 || nocross != 0) { 1217 if (align != vm->vm_quantum_mask + 1 || phase != 0 || nocross != 0) {
1187 1218
1188 /* 1219 /*
1189 * XXX should try to import a region large enough to 1220 * XXX should try to import a region large enough to
1190 * satisfy restrictions? 1221 * satisfy restrictions?
1191 */ 1222 */
1192 1223
1193 goto fail; 1224 goto fail;
1194 } 1225 }
1195 /* XXX eeek, minaddr & maxaddr not respected */ 1226 /* XXX eeek, minaddr & maxaddr not respected */
1196 if (vmem_import(vm, size, flags) == 0) { 1227 if (vmem_import(vm, size, flags) == 0) {
1197 goto retry; 1228 goto retry;
1198 } 1229 }
1199 /* XXX */ 1230 /* XXX */
1200 1231
1201 if ((flags & VM_SLEEP) != 0) { 1232 if ((flags & VM_SLEEP) != 0) {
1202 vmem_kick_pdaemon(); 1233 vmem_kick_pdaemon();
1203 VMEM_LOCK(vm); 
1204 VMEM_CONDVAR_WAIT(vm); 1234 VMEM_CONDVAR_WAIT(vm);
1205 VMEM_UNLOCK(vm); 
1206 goto retry; 1235 goto retry;
1207 } 1236 }
1208fail: 1237fail:
1209 bt_free(vm, btnew); 1238 bt_free(vm, btnew);
1210 bt_free(vm, btnew2); 1239 bt_free(vm, btnew2);
 1240 VMEM_UNLOCK(vm);
1211 return ENOMEM; 1241 return ENOMEM;
1212 1242
1213gotit: 1243gotit:
1214 KASSERT(bt->bt_type == BT_TYPE_FREE); 1244 KASSERT(bt->bt_type == BT_TYPE_FREE);
1215 KASSERT(bt->bt_size >= size); 1245 KASSERT(bt->bt_size >= size);
1216 bt_remfree(vm, bt); 1246 bt_remfree(vm, bt);
1217 vmem_check(vm); 1247 vmem_check(vm);
1218 if (bt->bt_start != start) { 1248 if (bt->bt_start != start) {
1219 btnew2->bt_type = BT_TYPE_FREE; 1249 btnew2->bt_type = BT_TYPE_FREE;
1220 btnew2->bt_start = bt->bt_start; 1250 btnew2->bt_start = bt->bt_start;
1221 btnew2->bt_size = start - bt->bt_start; 1251 btnew2->bt_size = start - bt->bt_start;
1222 bt->bt_start = start; 1252 bt->bt_start = start;
1223 bt->bt_size -= btnew2->bt_size; 1253 bt->bt_size -= btnew2->bt_size;
@@ -1228,43 +1258,41 @@ gotit: @@ -1228,43 +1258,41 @@ gotit:
1228 } 1258 }
1229 KASSERT(bt->bt_start == start); 1259 KASSERT(bt->bt_start == start);
1230 if (bt->bt_size != size && bt->bt_size - size > vm->vm_quantum_mask) { 1260 if (bt->bt_size != size && bt->bt_size - size > vm->vm_quantum_mask) {
1231 /* split */ 1261 /* split */
1232 btnew->bt_type = BT_TYPE_BUSY; 1262 btnew->bt_type = BT_TYPE_BUSY;
1233 btnew->bt_start = bt->bt_start; 1263 btnew->bt_start = bt->bt_start;
1234 btnew->bt_size = size; 1264 btnew->bt_size = size;
1235 bt->bt_start = bt->bt_start + size; 1265 bt->bt_start = bt->bt_start + size;
1236 bt->bt_size -= size; 1266 bt->bt_size -= size;
1237 bt_insfree(vm, bt); 1267 bt_insfree(vm, bt);
1238 bt_insseg(vm, btnew, TAILQ_PREV(bt, vmem_seglist, bt_seglist)); 1268 bt_insseg(vm, btnew, TAILQ_PREV(bt, vmem_seglist, bt_seglist));
1239 bt_insbusy(vm, btnew); 1269 bt_insbusy(vm, btnew);
1240 vmem_check(vm); 1270 vmem_check(vm);
1241 VMEM_UNLOCK(vm); 
1242 } else { 1271 } else {
1243 bt->bt_type = BT_TYPE_BUSY; 1272 bt->bt_type = BT_TYPE_BUSY;
1244 bt_insbusy(vm, bt); 1273 bt_insbusy(vm, bt);
1245 vmem_check(vm); 1274 vmem_check(vm);
1246 VMEM_UNLOCK(vm); 
1247 bt_free(vm, btnew); 1275 bt_free(vm, btnew);
1248 btnew = bt; 1276 btnew = bt;
1249 } 1277 }
1250 if (btnew2 != NULL) { 1278 if (btnew2 != NULL) {
1251 bt_free(vm, btnew2); 1279 bt_free(vm, btnew2);
1252 } 1280 }
1253 KASSERT(btnew->bt_size >= size); 1281 KASSERT(btnew->bt_size >= size);
1254 btnew->bt_type = BT_TYPE_BUSY; 1282 btnew->bt_type = BT_TYPE_BUSY;
1255 
1256 if (addrp != NULL) 1283 if (addrp != NULL)
1257 *addrp = btnew->bt_start; 1284 *addrp = btnew->bt_start;
 1285 VMEM_UNLOCK(vm);
1258 return 0; 1286 return 0;
1259} 1287}
1260 1288
1261/* 1289/*
1262 * vmem_free: free the resource to the arena. 1290 * vmem_free: free the resource to the arena.
1263 */ 1291 */
1264 1292
1265void 1293void
1266vmem_free(vmem_t *vm, vmem_addr_t addr, vmem_size_t size) 1294vmem_free(vmem_t *vm, vmem_addr_t addr, vmem_size_t size)
1267{ 1295{
1268 1296
1269 KASSERT(size > 0); 1297 KASSERT(size > 0);
1270 1298
@@ -1276,108 +1304,104 @@ vmem_free(vmem_t *vm, vmem_addr_t addr,  @@ -1276,108 +1304,104 @@ vmem_free(vmem_t *vm, vmem_addr_t addr,
1276 pool_cache_put(qc->qc_cache, (void *)addr); 1304 pool_cache_put(qc->qc_cache, (void *)addr);
1277 return; 1305 return;
1278 } 1306 }
1279#endif /* defined(QCACHE) */ 1307#endif /* defined(QCACHE) */
1280 1308
1281 vmem_xfree(vm, addr, size); 1309 vmem_xfree(vm, addr, size);
1282} 1310}
1283 1311
1284void 1312void
1285vmem_xfree(vmem_t *vm, vmem_addr_t addr, vmem_size_t size) 1313vmem_xfree(vmem_t *vm, vmem_addr_t addr, vmem_size_t size)
1286{ 1314{
1287 bt_t *bt; 1315 bt_t *bt;
1288 bt_t *t; 1316 bt_t *t;
1289 LIST_HEAD(, vmem_btag) tofree; 
1290 
1291 LIST_INIT(&tofree); 
1292 1317
1293 KASSERT(size > 0); 1318 KASSERT(size > 0);
1294 1319
1295 VMEM_LOCK(vm); 1320 VMEM_LOCK(vm);
1296 1321
1297 bt = bt_lookupbusy(vm, addr); 1322 bt = bt_lookupbusy(vm, addr);
1298 KASSERT(bt != NULL); 1323 KASSERT(bt != NULL);
1299 KASSERT(bt->bt_start == addr); 1324 KASSERT(bt->bt_start == addr);
1300 KASSERT(bt->bt_size == vmem_roundup_size(vm, size) || 1325 KASSERT(bt->bt_size == vmem_roundup_size(vm, size) ||
1301 bt->bt_size - vmem_roundup_size(vm, size) <= vm->vm_quantum_mask); 1326 bt->bt_size - vmem_roundup_size(vm, size) <= vm->vm_quantum_mask);
1302 KASSERT(bt->bt_type == BT_TYPE_BUSY); 1327 KASSERT(bt->bt_type == BT_TYPE_BUSY);
1303 bt_rembusy(vm, bt); 1328 bt_rembusy(vm, bt);
1304 bt->bt_type = BT_TYPE_FREE; 1329 bt->bt_type = BT_TYPE_FREE;
1305 1330
1306 /* coalesce */ 1331 /* coalesce */
1307 t = TAILQ_NEXT(bt, bt_seglist); 1332 t = TAILQ_NEXT(bt, bt_seglist);
1308 if (t != NULL && t->bt_type == BT_TYPE_FREE) { 1333 if (t != NULL && t->bt_type == BT_TYPE_FREE) {
1309 KASSERT(BT_END(bt) < t->bt_start); /* YYY */ 1334 KASSERT(BT_END(bt) < t->bt_start); /* YYY */
1310 bt_remfree(vm, t); 1335 bt_remfree(vm, t);
1311 bt_remseg(vm, t); 1336 bt_remseg(vm, t);
1312 bt->bt_size += t->bt_size; 1337 bt->bt_size += t->bt_size;
1313 LIST_INSERT_HEAD(&tofree, t, bt_freelist); 1338 bt_free(vm, t);
1314 } 1339 }
1315 t = TAILQ_PREV(bt, vmem_seglist, bt_seglist); 1340 t = TAILQ_PREV(bt, vmem_seglist, bt_seglist);
1316 if (t != NULL && t->bt_type == BT_TYPE_FREE) { 1341 if (t != NULL && t->bt_type == BT_TYPE_FREE) {
1317 KASSERT(BT_END(t) < bt->bt_start); /* YYY */ 1342 KASSERT(BT_END(t) < bt->bt_start); /* YYY */
1318 bt_remfree(vm, t); 1343 bt_remfree(vm, t);
1319 bt_remseg(vm, t); 1344 bt_remseg(vm, t);
1320 bt->bt_size += t->bt_size; 1345 bt->bt_size += t->bt_size;
1321 bt->bt_start = t->bt_start; 1346 bt->bt_start = t->bt_start;
1322 LIST_INSERT_HEAD(&tofree, t, bt_freelist); 1347 bt_free(vm, t);
1323 } 1348 }
1324 1349
1325 t = TAILQ_PREV(bt, vmem_seglist, bt_seglist); 1350 t = TAILQ_PREV(bt, vmem_seglist, bt_seglist);
1326 KASSERT(t != NULL); 1351 KASSERT(t != NULL);
1327 KASSERT(BT_ISSPAN_P(t) || t->bt_type == BT_TYPE_BUSY); 1352 KASSERT(BT_ISSPAN_P(t) || t->bt_type == BT_TYPE_BUSY);
1328 if (vm->vm_releasefn != NULL && t->bt_type == BT_TYPE_SPAN && 1353 if (vm->vm_releasefn != NULL && t->bt_type == BT_TYPE_SPAN &&
1329 t->bt_size == bt->bt_size) { 1354 t->bt_size == bt->bt_size) {
1330 vmem_addr_t spanaddr; 1355 vmem_addr_t spanaddr;
1331 vmem_size_t spansize; 1356 vmem_size_t spansize;
1332 1357
1333 KASSERT(t->bt_start == bt->bt_start); 1358 KASSERT(t->bt_start == bt->bt_start);
1334 spanaddr = bt->bt_start; 1359 spanaddr = bt->bt_start;
1335 spansize = bt->bt_size; 1360 spansize = bt->bt_size;
1336 bt_remseg(vm, bt); 1361 bt_remseg(vm, bt);
1337 LIST_INSERT_HEAD(&tofree, bt, bt_freelist); 1362 bt_free(vm, bt);
1338 bt_remseg(vm, t); 1363 bt_remseg(vm, t);
1339 LIST_INSERT_HEAD(&tofree, t, bt_freelist); 1364 bt_free(vm, t);
1340 vm->vm_size -= spansize; 1365 vm->vm_size -= spansize;
1341 VMEM_CONDVAR_BROADCAST(vm); 1366 VMEM_CONDVAR_BROADCAST(vm);
1342 VMEM_UNLOCK(vm); 1367 /* bt_freetrim() drops the lock. */
 1368 bt_freetrim(vm, BT_MAXFREE);
1343 (*vm->vm_releasefn)(vm->vm_arg, spanaddr, spansize); 1369 (*vm->vm_releasefn)(vm->vm_arg, spanaddr, spansize);
1344 } else { 1370 } else {
1345 bt_insfree(vm, bt); 1371 bt_insfree(vm, bt);
1346 VMEM_CONDVAR_BROADCAST(vm); 1372 VMEM_CONDVAR_BROADCAST(vm);
1347 VMEM_UNLOCK(vm); 1373 /* bt_freetrim() drops the lock. */
 1374 bt_freetrim(vm, BT_MAXFREE);
1348 } 1375 }
1349 
1350 while (!LIST_EMPTY(&tofree)) { 
1351 t = LIST_FIRST(&tofree); 
1352 LIST_REMOVE(t, bt_freelist); 
1353 bt_free(vm, t); 
1354 } 
1355 
1356 bt_freetrim(vm, BT_MAXFREE); 
1357} 1376}
1358 1377
1359/* 1378/*
1360 * vmem_add: 1379 * vmem_add:
1361 * 1380 *
1362 * => caller must ensure appropriate spl, 1381 * => caller must ensure appropriate spl,
1363 * if the arena can be accessed from interrupt context. 1382 * if the arena can be accessed from interrupt context.
1364 */ 1383 */
1365 1384
1366int 1385int
1367vmem_add(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, vm_flag_t flags) 1386vmem_add(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, vm_flag_t flags)
1368{ 1387{
 1388 int rv;
1369 1389
1370 return vmem_add1(vm, addr, size, flags, BT_TYPE_SPAN_STATIC); 1390 VMEM_LOCK(vm);
 1391 rv = vmem_add1(vm, addr, size, flags, BT_TYPE_SPAN_STATIC);
 1392 VMEM_UNLOCK(vm);
 1393
 1394 return rv;
1371} 1395}
1372 1396
1373/* 1397/*
1374 * vmem_size: information about arenas size 1398 * vmem_size: information about arenas size
1375 * 1399 *
1376 * => return free/allocated size in arena 1400 * => return free/allocated size in arena
1377 */ 1401 */
1378vmem_size_t 1402vmem_size_t
1379vmem_size(vmem_t *vm, int typemask) 1403vmem_size(vmem_t *vm, int typemask)
1380{ 1404{
1381 1405
1382 switch (typemask) { 1406 switch (typemask) {
1383 case VMEM_ALLOC: 1407 case VMEM_ALLOC:
@@ -1400,32 +1424,28 @@ static struct workqueue *vmem_rehash_wq; @@ -1400,32 +1424,28 @@ static struct workqueue *vmem_rehash_wq;
1400static struct work vmem_rehash_wk; 1424static struct work vmem_rehash_wk;
1401 1425
1402static void 1426static void
1403vmem_rehash_all(struct work *wk, void *dummy) 1427vmem_rehash_all(struct work *wk, void *dummy)
1404{ 1428{
1405 vmem_t *vm; 1429 vmem_t *vm;
1406 1430
1407 KASSERT(wk == &vmem_rehash_wk); 1431 KASSERT(wk == &vmem_rehash_wk);
1408 mutex_enter(&vmem_list_lock); 1432 mutex_enter(&vmem_list_lock);
1409 LIST_FOREACH(vm, &vmem_list, vm_alllist) { 1433 LIST_FOREACH(vm, &vmem_list, vm_alllist) {
1410 size_t desired; 1434 size_t desired;
1411 size_t current; 1435 size_t current;
1412 1436
1413 if (!VMEM_TRYLOCK(vm)) { 1437 desired = atomic_load_relaxed(&vm->vm_maxbusytag);
1414 continue; 1438 current = atomic_load_relaxed(&vm->vm_hashsize);
1415 } 
1416 desired = vm->vm_nbusytag; 
1417 current = vm->vm_hashsize; 
1418 VMEM_UNLOCK(vm); 
1419 1439
1420 if (desired > VMEM_HASHSIZE_MAX) { 1440 if (desired > VMEM_HASHSIZE_MAX) {
1421 desired = VMEM_HASHSIZE_MAX; 1441 desired = VMEM_HASHSIZE_MAX;
1422 } else if (desired < VMEM_HASHSIZE_MIN) { 1442 } else if (desired < VMEM_HASHSIZE_MIN) {
1423 desired = VMEM_HASHSIZE_MIN; 1443 desired = VMEM_HASHSIZE_MIN;
1424 } 1444 }
1425 if (desired > current * 2 || desired * 2 < current) { 1445 if (desired > current * 2 || desired * 2 < current) {
1426 vmem_rehash(vm, desired, VM_NOSLEEP); 1446 vmem_rehash(vm, desired, VM_NOSLEEP);
1427 } 1447 }
1428 } 1448 }
1429 mutex_exit(&vmem_list_lock); 1449 mutex_exit(&vmem_list_lock);
1430 1450
1431 callout_schedule(&vmem_rehash_ch, vmem_rehash_interval); 1451 callout_schedule(&vmem_rehash_ch, vmem_rehash_interval);

cvs diff -r1.3 -r1.4 src/sys/sys/vmem_impl.h (expand / switch to unified diff)

--- src/sys/sys/vmem_impl.h 2013/11/22 21:04:11 1.3
+++ src/sys/sys/vmem_impl.h 2020/04/19 21:11:42 1.4
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: vmem_impl.h,v 1.3 2013/11/22 21:04:11 christos Exp $ */ 1/* $NetBSD: vmem_impl.h,v 1.4 2020/04/19 21:11:42 ad Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c)2006 YAMAMOTO Takashi, 4 * Copyright (c)2006 YAMAMOTO Takashi,
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
@@ -85,27 +85,29 @@ typedef struct qcache qcache_t; @@ -85,27 +85,29 @@ typedef struct qcache qcache_t;
85/* vmem arena */ 85/* vmem arena */
86struct vmem { 86struct vmem {
87 CONDVAR_DECL(vm_cv); 87 CONDVAR_DECL(vm_cv);
88 LOCK_DECL(vm_lock); 88 LOCK_DECL(vm_lock);
89 vm_flag_t vm_flags; 89 vm_flag_t vm_flags;
90 vmem_import_t *vm_importfn; 90 vmem_import_t *vm_importfn;
91 vmem_release_t *vm_releasefn; 91 vmem_release_t *vm_releasefn;
92 size_t vm_nfreetags; 92 size_t vm_nfreetags;
93 LIST_HEAD(, vmem_btag) vm_freetags; 93 LIST_HEAD(, vmem_btag) vm_freetags;
94 void *vm_arg; 94 void *vm_arg;
95 struct vmem_seglist vm_seglist; 95 struct vmem_seglist vm_seglist;
96 struct vmem_freelist vm_freelist[VMEM_MAXORDER]; 96 struct vmem_freelist vm_freelist[VMEM_MAXORDER];
97 size_t vm_hashsize; 97 size_t vm_hashsize;
 98 size_t vm_hashmask;
98 size_t vm_nbusytag; 99 size_t vm_nbusytag;
 100 size_t vm_maxbusytag;
99 struct vmem_hashlist *vm_hashlist; 101 struct vmem_hashlist *vm_hashlist;
100 struct vmem_hashlist vm_hash0; 102 struct vmem_hashlist vm_hash0;
101 size_t vm_quantum_mask; 103 size_t vm_quantum_mask;
102 int vm_quantum_shift; 104 int vm_quantum_shift;
103 size_t vm_size; 105 size_t vm_size;
104 size_t vm_inuse; 106 size_t vm_inuse;
105 char vm_name[VMEM_NAME_MAX+1]; 107 char vm_name[VMEM_NAME_MAX+1];
106 LIST_ENTRY(vmem) vm_alllist; 108 LIST_ENTRY(vmem) vm_alllist;
107 109
108#if defined(QCACHE) 110#if defined(QCACHE)
109 /* quantum cache */ 111 /* quantum cache */
110 size_t vm_qcache_max; 112 size_t vm_qcache_max;
111 struct pool_allocator vm_qcache_allocator; 113 struct pool_allocator vm_qcache_allocator;