subr_vmem.c revision 1.42.10.1 1 /* $NetBSD: subr_vmem.c,v 1.42.10.1 2008/12/13 01:15:08 haad Exp $ */
2
3 /*-
4 * Copyright (c)2006 YAMAMOTO Takashi,
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /*
30 * reference:
31 * - Magazines and Vmem: Extending the Slab Allocator
32 * to Many CPUs and Arbitrary Resources
33 * http://www.usenix.org/event/usenix01/bonwick.html
34 *
35 * todo:
36 * - decide how to import segments for vmem_xalloc.
37 * - don't rely on malloc(9).
38 */
39
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: subr_vmem.c,v 1.42.10.1 2008/12/13 01:15:08 haad Exp $");
42
43 #if defined(_KERNEL)
44 #include "opt_ddb.h"
45 #define QCACHE
46 #endif /* defined(_KERNEL) */
47
48 #include <sys/param.h>
49 #include <sys/hash.h>
50 #include <sys/queue.h>
51
52 #if defined(_KERNEL)
53 #include <sys/systm.h>
54 #include <sys/kernel.h> /* hz */
55 #include <sys/callout.h>
56 #include <sys/malloc.h>
57 #include <sys/once.h>
58 #include <sys/pool.h>
59 #include <sys/vmem.h>
60 #include <sys/workqueue.h>
61 #else /* defined(_KERNEL) */
62 #include "../sys/vmem.h"
63 #endif /* defined(_KERNEL) */
64
65 #if defined(_KERNEL)
66 #define LOCK_DECL(name) kmutex_t name
67 #else /* defined(_KERNEL) */
68 #include <errno.h>
69 #include <assert.h>
70 #include <stdlib.h>
71
72 #define KASSERT(a) assert(a)
73 #define LOCK_DECL(name) /* nothing */
74 #define mutex_init(a, b, c) /* nothing */
75 #define mutex_destroy(a) /* nothing */
76 #define mutex_enter(a) /* nothing */
77 #define mutex_exit(a) /* nothing */
78 #define mutex_owned(a) /* nothing */
79 #define ASSERT_SLEEPABLE() /* nothing */
80 #define IPL_VM 0
81 #endif /* defined(_KERNEL) */
82
83 struct vmem;
84 struct vmem_btag;
85
86 #if defined(VMEM_DEBUG)
87 void vmem_dump(const vmem_t *);
88 #endif /* defined(VMEM_DEBUG) */
89
90 #define VMEM_MAXORDER (sizeof(vmem_size_t) * CHAR_BIT)
91
92 #define VMEM_HASHSIZE_MIN 1 /* XXX */
93 #define VMEM_HASHSIZE_MAX 8192 /* XXX */
94 #define VMEM_HASHSIZE_INIT VMEM_HASHSIZE_MIN
95
96 #define VM_FITMASK (VM_BESTFIT | VM_INSTANTFIT)
97
98 CIRCLEQ_HEAD(vmem_seglist, vmem_btag);
99 LIST_HEAD(vmem_freelist, vmem_btag);
100 LIST_HEAD(vmem_hashlist, vmem_btag);
101
102 #if defined(QCACHE)
103 #define VMEM_QCACHE_IDX_MAX 32
104
105 #define QC_NAME_MAX 16
106
107 struct qcache {
108 pool_cache_t qc_cache;
109 vmem_t *qc_vmem;
110 char qc_name[QC_NAME_MAX];
111 };
112 typedef struct qcache qcache_t;
113 #define QC_POOL_TO_QCACHE(pool) ((qcache_t *)(pool->pr_qcache))
114 #endif /* defined(QCACHE) */
115
116 /* vmem arena */
117 struct vmem {
118 LOCK_DECL(vm_lock);
119 vmem_addr_t (*vm_allocfn)(vmem_t *, vmem_size_t, vmem_size_t *,
120 vm_flag_t);
121 void (*vm_freefn)(vmem_t *, vmem_addr_t, vmem_size_t);
122 vmem_t *vm_source;
123 struct vmem_seglist vm_seglist;
124 struct vmem_freelist vm_freelist[VMEM_MAXORDER];
125 size_t vm_hashsize;
126 size_t vm_nbusytag;
127 struct vmem_hashlist *vm_hashlist;
128 size_t vm_quantum_mask;
129 int vm_quantum_shift;
130 const char *vm_name;
131 LIST_ENTRY(vmem) vm_alllist;
132
133 #if defined(QCACHE)
134 /* quantum cache */
135 size_t vm_qcache_max;
136 struct pool_allocator vm_qcache_allocator;
137 qcache_t vm_qcache_store[VMEM_QCACHE_IDX_MAX];
138 qcache_t *vm_qcache[VMEM_QCACHE_IDX_MAX];
139 #endif /* defined(QCACHE) */
140 };
141
142 #define VMEM_LOCK(vm) mutex_enter(&vm->vm_lock)
143 #define VMEM_TRYLOCK(vm) mutex_tryenter(&vm->vm_lock)
144 #define VMEM_UNLOCK(vm) mutex_exit(&vm->vm_lock)
145 #define VMEM_LOCK_INIT(vm, ipl) mutex_init(&vm->vm_lock, MUTEX_DEFAULT, ipl)
146 #define VMEM_LOCK_DESTROY(vm) mutex_destroy(&vm->vm_lock)
147 #define VMEM_ASSERT_LOCKED(vm) KASSERT(mutex_owned(&vm->vm_lock))
148
149 /* boundary tag */
150 struct vmem_btag {
151 CIRCLEQ_ENTRY(vmem_btag) bt_seglist;
152 union {
153 LIST_ENTRY(vmem_btag) u_freelist; /* BT_TYPE_FREE */
154 LIST_ENTRY(vmem_btag) u_hashlist; /* BT_TYPE_BUSY */
155 } bt_u;
156 #define bt_hashlist bt_u.u_hashlist
157 #define bt_freelist bt_u.u_freelist
158 vmem_addr_t bt_start;
159 vmem_size_t bt_size;
160 int bt_type;
161 };
162
163 #define BT_TYPE_SPAN 1
164 #define BT_TYPE_SPAN_STATIC 2
165 #define BT_TYPE_FREE 3
166 #define BT_TYPE_BUSY 4
167 #define BT_ISSPAN_P(bt) ((bt)->bt_type <= BT_TYPE_SPAN_STATIC)
168
169 #define BT_END(bt) ((bt)->bt_start + (bt)->bt_size)
170
171 typedef struct vmem_btag bt_t;
172
173 /* ---- misc */
174
175 #define VMEM_ALIGNUP(addr, align) \
176 (-(-(addr) & -(align)))
177 #define VMEM_CROSS_P(addr1, addr2, boundary) \
178 ((((addr1) ^ (addr2)) & -(boundary)) != 0)
179
180 #define ORDER2SIZE(order) ((vmem_size_t)1 << (order))
181
182 static int
183 calc_order(vmem_size_t size)
184 {
185 vmem_size_t target;
186 int i;
187
188 KASSERT(size != 0);
189
190 i = 0;
191 target = size >> 1;
192 while (ORDER2SIZE(i) <= target) {
193 i++;
194 }
195
196 KASSERT(ORDER2SIZE(i) <= size);
197 KASSERT(size < ORDER2SIZE(i + 1) || ORDER2SIZE(i + 1) < ORDER2SIZE(i));
198
199 return i;
200 }
201
202 #if defined(_KERNEL)
203 static MALLOC_DEFINE(M_VMEM, "vmem", "vmem");
204 #endif /* defined(_KERNEL) */
205
206 static void *
207 xmalloc(size_t sz, vm_flag_t flags)
208 {
209
210 #if defined(_KERNEL)
211 return malloc(sz, M_VMEM,
212 M_CANFAIL | ((flags & VM_SLEEP) ? M_WAITOK : M_NOWAIT));
213 #else /* defined(_KERNEL) */
214 return malloc(sz);
215 #endif /* defined(_KERNEL) */
216 }
217
218 static void
219 xfree(void *p)
220 {
221
222 #if defined(_KERNEL)
223 return free(p, M_VMEM);
224 #else /* defined(_KERNEL) */
225 return free(p);
226 #endif /* defined(_KERNEL) */
227 }
228
229 /* ---- boundary tag */
230
231 #if defined(_KERNEL)
232 static struct pool_cache bt_cache;
233 #endif /* defined(_KERNEL) */
234
235 static bt_t *
236 bt_alloc(vmem_t *vm, vm_flag_t flags)
237 {
238 bt_t *bt;
239
240 #if defined(_KERNEL)
241 bt = pool_cache_get(&bt_cache,
242 (flags & VM_SLEEP) != 0 ? PR_WAITOK : PR_NOWAIT);
243 #else /* defined(_KERNEL) */
244 bt = malloc(sizeof *bt);
245 #endif /* defined(_KERNEL) */
246
247 return bt;
248 }
249
250 static void
251 bt_free(vmem_t *vm, bt_t *bt)
252 {
253
254 #if defined(_KERNEL)
255 pool_cache_put(&bt_cache, bt);
256 #else /* defined(_KERNEL) */
257 free(bt);
258 #endif /* defined(_KERNEL) */
259 }
260
261 /*
262 * freelist[0] ... [1, 1]
263 * freelist[1] ... [2, 3]
264 * freelist[2] ... [4, 7]
265 * freelist[3] ... [8, 15]
266 * :
267 * freelist[n] ... [(1 << n), (1 << (n + 1)) - 1]
268 * :
269 */
270
271 static struct vmem_freelist *
272 bt_freehead_tofree(vmem_t *vm, vmem_size_t size)
273 {
274 const vmem_size_t qsize = size >> vm->vm_quantum_shift;
275 int idx;
276
277 KASSERT((size & vm->vm_quantum_mask) == 0);
278 KASSERT(size != 0);
279
280 idx = calc_order(qsize);
281 KASSERT(idx >= 0);
282 KASSERT(idx < VMEM_MAXORDER);
283
284 return &vm->vm_freelist[idx];
285 }
286
287 static struct vmem_freelist *
288 bt_freehead_toalloc(vmem_t *vm, vmem_size_t size, vm_flag_t strat)
289 {
290 const vmem_size_t qsize = size >> vm->vm_quantum_shift;
291 int idx;
292
293 KASSERT((size & vm->vm_quantum_mask) == 0);
294 KASSERT(size != 0);
295
296 idx = calc_order(qsize);
297 if (strat == VM_INSTANTFIT && ORDER2SIZE(idx) != qsize) {
298 idx++;
299 /* check too large request? */
300 }
301 KASSERT(idx >= 0);
302 KASSERT(idx < VMEM_MAXORDER);
303
304 return &vm->vm_freelist[idx];
305 }
306
307 /* ---- boundary tag hash */
308
309 static struct vmem_hashlist *
310 bt_hashhead(vmem_t *vm, vmem_addr_t addr)
311 {
312 struct vmem_hashlist *list;
313 unsigned int hash;
314
315 hash = hash32_buf(&addr, sizeof(addr), HASH32_BUF_INIT);
316 list = &vm->vm_hashlist[hash % vm->vm_hashsize];
317
318 return list;
319 }
320
321 static bt_t *
322 bt_lookupbusy(vmem_t *vm, vmem_addr_t addr)
323 {
324 struct vmem_hashlist *list;
325 bt_t *bt;
326
327 list = bt_hashhead(vm, addr);
328 LIST_FOREACH(bt, list, bt_hashlist) {
329 if (bt->bt_start == addr) {
330 break;
331 }
332 }
333
334 return bt;
335 }
336
337 static void
338 bt_rembusy(vmem_t *vm, bt_t *bt)
339 {
340
341 KASSERT(vm->vm_nbusytag > 0);
342 vm->vm_nbusytag--;
343 LIST_REMOVE(bt, bt_hashlist);
344 }
345
346 static void
347 bt_insbusy(vmem_t *vm, bt_t *bt)
348 {
349 struct vmem_hashlist *list;
350
351 KASSERT(bt->bt_type == BT_TYPE_BUSY);
352
353 list = bt_hashhead(vm, bt->bt_start);
354 LIST_INSERT_HEAD(list, bt, bt_hashlist);
355 vm->vm_nbusytag++;
356 }
357
358 /* ---- boundary tag list */
359
360 static void
361 bt_remseg(vmem_t *vm, bt_t *bt)
362 {
363
364 CIRCLEQ_REMOVE(&vm->vm_seglist, bt, bt_seglist);
365 }
366
367 static void
368 bt_insseg(vmem_t *vm, bt_t *bt, bt_t *prev)
369 {
370
371 CIRCLEQ_INSERT_AFTER(&vm->vm_seglist, prev, bt, bt_seglist);
372 }
373
374 static void
375 bt_insseg_tail(vmem_t *vm, bt_t *bt)
376 {
377
378 CIRCLEQ_INSERT_TAIL(&vm->vm_seglist, bt, bt_seglist);
379 }
380
381 static void
382 bt_remfree(vmem_t *vm, bt_t *bt)
383 {
384
385 KASSERT(bt->bt_type == BT_TYPE_FREE);
386
387 LIST_REMOVE(bt, bt_freelist);
388 }
389
390 static void
391 bt_insfree(vmem_t *vm, bt_t *bt)
392 {
393 struct vmem_freelist *list;
394
395 list = bt_freehead_tofree(vm, bt->bt_size);
396 LIST_INSERT_HEAD(list, bt, bt_freelist);
397 }
398
399 /* ---- vmem internal functions */
400
401 #if defined(_KERNEL)
402 static kmutex_t vmem_list_lock;
403 static LIST_HEAD(, vmem) vmem_list = LIST_HEAD_INITIALIZER(vmem_list);
404 #endif /* defined(_KERNEL) */
405
406 #if defined(QCACHE)
407 static inline vm_flag_t
408 prf_to_vmf(int prflags)
409 {
410 vm_flag_t vmflags;
411
412 KASSERT((prflags & ~(PR_LIMITFAIL | PR_WAITOK | PR_NOWAIT)) == 0);
413 if ((prflags & PR_WAITOK) != 0) {
414 vmflags = VM_SLEEP;
415 } else {
416 vmflags = VM_NOSLEEP;
417 }
418 return vmflags;
419 }
420
421 static inline int
422 vmf_to_prf(vm_flag_t vmflags)
423 {
424 int prflags;
425
426 if ((vmflags & VM_SLEEP) != 0) {
427 prflags = PR_WAITOK;
428 } else {
429 prflags = PR_NOWAIT;
430 }
431 return prflags;
432 }
433
434 static size_t
435 qc_poolpage_size(size_t qcache_max)
436 {
437 int i;
438
439 for (i = 0; ORDER2SIZE(i) <= qcache_max * 3; i++) {
440 /* nothing */
441 }
442 return ORDER2SIZE(i);
443 }
444
445 static void *
446 qc_poolpage_alloc(struct pool *pool, int prflags)
447 {
448 qcache_t *qc = QC_POOL_TO_QCACHE(pool);
449 vmem_t *vm = qc->qc_vmem;
450
451 return (void *)vmem_alloc(vm, pool->pr_alloc->pa_pagesz,
452 prf_to_vmf(prflags) | VM_INSTANTFIT);
453 }
454
455 static void
456 qc_poolpage_free(struct pool *pool, void *addr)
457 {
458 qcache_t *qc = QC_POOL_TO_QCACHE(pool);
459 vmem_t *vm = qc->qc_vmem;
460
461 vmem_free(vm, (vmem_addr_t)addr, pool->pr_alloc->pa_pagesz);
462 }
463
464 static void
465 qc_init(vmem_t *vm, size_t qcache_max, int ipl)
466 {
467 qcache_t *prevqc;
468 struct pool_allocator *pa;
469 int qcache_idx_max;
470 int i;
471
472 KASSERT((qcache_max & vm->vm_quantum_mask) == 0);
473 if (qcache_max > (VMEM_QCACHE_IDX_MAX << vm->vm_quantum_shift)) {
474 qcache_max = VMEM_QCACHE_IDX_MAX << vm->vm_quantum_shift;
475 }
476 vm->vm_qcache_max = qcache_max;
477 pa = &vm->vm_qcache_allocator;
478 memset(pa, 0, sizeof(*pa));
479 pa->pa_alloc = qc_poolpage_alloc;
480 pa->pa_free = qc_poolpage_free;
481 pa->pa_pagesz = qc_poolpage_size(qcache_max);
482
483 qcache_idx_max = qcache_max >> vm->vm_quantum_shift;
484 prevqc = NULL;
485 for (i = qcache_idx_max; i > 0; i--) {
486 qcache_t *qc = &vm->vm_qcache_store[i - 1];
487 size_t size = i << vm->vm_quantum_shift;
488
489 qc->qc_vmem = vm;
490 snprintf(qc->qc_name, sizeof(qc->qc_name), "%s-%zu",
491 vm->vm_name, size);
492 qc->qc_cache = pool_cache_init(size,
493 ORDER2SIZE(vm->vm_quantum_shift), 0,
494 PR_NOALIGN | PR_NOTOUCH /* XXX */,
495 qc->qc_name, pa, ipl, NULL, NULL, NULL);
496 KASSERT(qc->qc_cache != NULL); /* XXX */
497 if (prevqc != NULL &&
498 qc->qc_cache->pc_pool.pr_itemsperpage ==
499 prevqc->qc_cache->pc_pool.pr_itemsperpage) {
500 pool_cache_destroy(qc->qc_cache);
501 vm->vm_qcache[i - 1] = prevqc;
502 continue;
503 }
504 qc->qc_cache->pc_pool.pr_qcache = qc;
505 vm->vm_qcache[i - 1] = qc;
506 prevqc = qc;
507 }
508 }
509
510 static void
511 qc_destroy(vmem_t *vm)
512 {
513 const qcache_t *prevqc;
514 int i;
515 int qcache_idx_max;
516
517 qcache_idx_max = vm->vm_qcache_max >> vm->vm_quantum_shift;
518 prevqc = NULL;
519 for (i = 0; i < qcache_idx_max; i++) {
520 qcache_t *qc = vm->vm_qcache[i];
521
522 if (prevqc == qc) {
523 continue;
524 }
525 pool_cache_destroy(qc->qc_cache);
526 prevqc = qc;
527 }
528 }
529
530 static bool
531 qc_reap(vmem_t *vm)
532 {
533 const qcache_t *prevqc;
534 int i;
535 int qcache_idx_max;
536 bool didsomething = false;
537
538 qcache_idx_max = vm->vm_qcache_max >> vm->vm_quantum_shift;
539 prevqc = NULL;
540 for (i = 0; i < qcache_idx_max; i++) {
541 qcache_t *qc = vm->vm_qcache[i];
542
543 if (prevqc == qc) {
544 continue;
545 }
546 if (pool_cache_reclaim(qc->qc_cache) != 0) {
547 didsomething = true;
548 }
549 prevqc = qc;
550 }
551
552 return didsomething;
553 }
554 #endif /* defined(QCACHE) */
555
556 #if defined(_KERNEL)
557 static int
558 vmem_init(void)
559 {
560
561 mutex_init(&vmem_list_lock, MUTEX_DEFAULT, IPL_NONE);
562 pool_cache_bootstrap(&bt_cache, sizeof(bt_t), 0, 0, 0, "vmembt",
563 NULL, IPL_VM, NULL, NULL, NULL);
564 return 0;
565 }
566 #endif /* defined(_KERNEL) */
567
568 static vmem_addr_t
569 vmem_add1(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, vm_flag_t flags,
570 int spanbttype)
571 {
572 bt_t *btspan;
573 bt_t *btfree;
574
575 KASSERT((flags & (VM_SLEEP|VM_NOSLEEP)) != 0);
576 KASSERT((~flags & (VM_SLEEP|VM_NOSLEEP)) != 0);
577 KASSERT(spanbttype == BT_TYPE_SPAN || spanbttype == BT_TYPE_SPAN_STATIC);
578
579 btspan = bt_alloc(vm, flags);
580 if (btspan == NULL) {
581 return VMEM_ADDR_NULL;
582 }
583 btfree = bt_alloc(vm, flags);
584 if (btfree == NULL) {
585 bt_free(vm, btspan);
586 return VMEM_ADDR_NULL;
587 }
588
589 btspan->bt_type = spanbttype;
590 btspan->bt_start = addr;
591 btspan->bt_size = size;
592
593 btfree->bt_type = BT_TYPE_FREE;
594 btfree->bt_start = addr;
595 btfree->bt_size = size;
596
597 VMEM_LOCK(vm);
598 bt_insseg_tail(vm, btspan);
599 bt_insseg(vm, btfree, btspan);
600 bt_insfree(vm, btfree);
601 VMEM_UNLOCK(vm);
602
603 return addr;
604 }
605
606 static void
607 vmem_destroy1(vmem_t *vm)
608 {
609
610 #if defined(QCACHE)
611 qc_destroy(vm);
612 #endif /* defined(QCACHE) */
613 if (vm->vm_hashlist != NULL) {
614 int i;
615
616 for (i = 0; i < vm->vm_hashsize; i++) {
617 bt_t *bt;
618
619 while ((bt = LIST_FIRST(&vm->vm_hashlist[i])) != NULL) {
620 KASSERT(bt->bt_type == BT_TYPE_SPAN_STATIC);
621 bt_free(vm, bt);
622 }
623 }
624 xfree(vm->vm_hashlist);
625 }
626 VMEM_LOCK_DESTROY(vm);
627 xfree(vm);
628 }
629
630 static int
631 vmem_import(vmem_t *vm, vmem_size_t size, vm_flag_t flags)
632 {
633 vmem_addr_t addr;
634
635 if (vm->vm_allocfn == NULL) {
636 return EINVAL;
637 }
638
639 addr = (*vm->vm_allocfn)(vm->vm_source, size, &size, flags);
640 if (addr == VMEM_ADDR_NULL) {
641 return ENOMEM;
642 }
643
644 if (vmem_add1(vm, addr, size, flags, BT_TYPE_SPAN) == VMEM_ADDR_NULL) {
645 (*vm->vm_freefn)(vm->vm_source, addr, size);
646 return ENOMEM;
647 }
648
649 return 0;
650 }
651
652 static int
653 vmem_rehash(vmem_t *vm, size_t newhashsize, vm_flag_t flags)
654 {
655 bt_t *bt;
656 int i;
657 struct vmem_hashlist *newhashlist;
658 struct vmem_hashlist *oldhashlist;
659 size_t oldhashsize;
660
661 KASSERT(newhashsize > 0);
662
663 newhashlist =
664 xmalloc(sizeof(struct vmem_hashlist *) * newhashsize, flags);
665 if (newhashlist == NULL) {
666 return ENOMEM;
667 }
668 for (i = 0; i < newhashsize; i++) {
669 LIST_INIT(&newhashlist[i]);
670 }
671
672 if (!VMEM_TRYLOCK(vm)) {
673 xfree(newhashlist);
674 return EBUSY;
675 }
676 oldhashlist = vm->vm_hashlist;
677 oldhashsize = vm->vm_hashsize;
678 vm->vm_hashlist = newhashlist;
679 vm->vm_hashsize = newhashsize;
680 if (oldhashlist == NULL) {
681 VMEM_UNLOCK(vm);
682 return 0;
683 }
684 for (i = 0; i < oldhashsize; i++) {
685 while ((bt = LIST_FIRST(&oldhashlist[i])) != NULL) {
686 bt_rembusy(vm, bt); /* XXX */
687 bt_insbusy(vm, bt);
688 }
689 }
690 VMEM_UNLOCK(vm);
691
692 xfree(oldhashlist);
693
694 return 0;
695 }
696
697 /*
698 * vmem_fit: check if a bt can satisfy the given restrictions.
699 */
700
701 static vmem_addr_t
702 vmem_fit(const bt_t *bt, vmem_size_t size, vmem_size_t align, vmem_size_t phase,
703 vmem_size_t nocross, vmem_addr_t minaddr, vmem_addr_t maxaddr)
704 {
705 vmem_addr_t start;
706 vmem_addr_t end;
707
708 KASSERT(bt->bt_size >= size);
709
710 /*
711 * XXX assumption: vmem_addr_t and vmem_size_t are
712 * unsigned integer of the same size.
713 */
714
715 start = bt->bt_start;
716 if (start < minaddr) {
717 start = minaddr;
718 }
719 end = BT_END(bt);
720 if (end > maxaddr - 1) {
721 end = maxaddr - 1;
722 }
723 if (start >= end) {
724 return VMEM_ADDR_NULL;
725 }
726
727 start = VMEM_ALIGNUP(start - phase, align) + phase;
728 if (start < bt->bt_start) {
729 start += align;
730 }
731 if (VMEM_CROSS_P(start, start + size - 1, nocross)) {
732 KASSERT(align < nocross);
733 start = VMEM_ALIGNUP(start - phase, nocross) + phase;
734 }
735 if (start < end && end - start >= size) {
736 KASSERT((start & (align - 1)) == phase);
737 KASSERT(!VMEM_CROSS_P(start, start + size - 1, nocross));
738 KASSERT(minaddr <= start);
739 KASSERT(maxaddr == 0 || start + size <= maxaddr);
740 KASSERT(bt->bt_start <= start);
741 KASSERT(start + size <= BT_END(bt));
742 return start;
743 }
744 return VMEM_ADDR_NULL;
745 }
746
747 #if !defined(VMEM_DEBUG)
748 #define vmem_check_sanity(vm) true
749 #else
750
751 static bool
752 vmem_check_spanoverlap(const char *func, const vmem_t *vm,
753 const bt_t *bt, const bt_t *bt2)
754 {
755 switch (bt->bt_type) {
756 case BT_TYPE_BUSY:
757 case BT_TYPE_FREE:
758 if (BT_ISSPAN_P(bt2))
759 return true;
760 break;
761 case BT_TYPE_SPAN:
762 case BT_TYPE_SPAN_STATIC:
763 if (bt2->bt_type == BT_TYPE_BUSY
764 || bt2->bt_type == BT_TYPE_FREE)
765 return true;
766 break;
767 }
768
769 if (bt->bt_start > bt2->bt_start) {
770 if (bt->bt_start >= BT_END(bt2))
771 return true;
772
773 printf("%s: overlapping VMEM '%s' span 0x%"
774 PRIx64" - 0x%"PRIx64" %s\n",
775 func, vm->vm_name,
776 (uint64_t)bt->bt_start,
777 (uint64_t)BT_END(bt),
778 (bt->bt_type == BT_TYPE_BUSY) ?
779 "allocated" :
780 (bt->bt_type == BT_TYPE_FREE) ?
781 "free" :
782 (bt->bt_type == BT_TYPE_SPAN) ?
783 "span" : "static span");
784 printf("%s: overlapping VMEM '%s' span 0x%"
785 PRIx64" - 0x%"PRIx64" %s\n",
786 func, vm->vm_name,
787 (uint64_t)bt2->bt_start,
788 (uint64_t)BT_END(bt2),
789 (bt2->bt_type == BT_TYPE_BUSY) ?
790 "allocated" :
791 (bt2->bt_type == BT_TYPE_FREE) ?
792 "free" :
793 (bt2->bt_type == BT_TYPE_SPAN) ?
794 "span" : "static span");
795 return false;
796 }
797 if (BT_END(bt) > bt2->bt_start) {
798 printf("%s: overlapping VMEM '%s' span 0x%"
799 PRIx64" - 0x%"PRIx64" %s\n",
800 func, vm->vm_name,
801 (uint64_t)bt->bt_start,
802 (uint64_t)BT_END(bt),
803 (bt->bt_type == BT_TYPE_BUSY) ?
804 "allocated" :
805 (bt->bt_type == BT_TYPE_FREE) ?
806 "free" :
807 (bt->bt_type == BT_TYPE_SPAN) ?
808 "span" : "static span");
809 printf("%s: overlapping VMEM '%s' span 0x%"
810 PRIx64" - 0x%"PRIx64" %s\n",
811 func, vm->vm_name,
812 (uint64_t)bt2->bt_start,
813 (uint64_t)BT_END(bt2),
814 (bt2->bt_type == BT_TYPE_BUSY) ?
815 "allocated" :
816 (bt2->bt_type == BT_TYPE_FREE) ?
817 "free" :
818 (bt2->bt_type == BT_TYPE_SPAN) ?
819 "span" : "static span");
820 return false;
821 }
822
823 return true;
824 }
825
826 static bool
827 vmem_check_sanity(vmem_t *vm)
828 {
829 const bt_t *bt, *bt2;
830
831 KASSERT(vm != NULL);
832
833 CIRCLEQ_FOREACH(bt, &vm->vm_seglist, bt_seglist) {
834 if (bt->bt_start >= BT_END(bt)) {
835 printf("%s: bogus VMEM '%s' span 0x%"PRIx64
836 " - 0x%"PRIx64" %s\n",
837 __func__, vm->vm_name,
838 (uint64_t)bt->bt_start, (uint64_t)BT_END(bt),
839 (bt->bt_type == BT_TYPE_BUSY) ?
840 "allocated" :
841 (bt->bt_type == BT_TYPE_FREE) ?
842 "free" :
843 (bt->bt_type == BT_TYPE_SPAN) ?
844 "span" : "static span");
845 return false;
846 }
847
848 CIRCLEQ_FOREACH(bt2, &vm->vm_seglist, bt_seglist) {
849 if (bt2->bt_start >= BT_END(bt2)) {
850 printf("%s: bogus VMEM '%s' span 0x%"PRIx64
851 " - 0x%"PRIx64" %s\n",
852 __func__, vm->vm_name,
853 (uint64_t)bt2->bt_start,
854 (uint64_t)BT_END(bt2),
855 (bt2->bt_type == BT_TYPE_BUSY) ?
856 "allocated" :
857 (bt2->bt_type == BT_TYPE_FREE) ?
858 "free" :
859 (bt2->bt_type == BT_TYPE_SPAN) ?
860 "span" : "static span");
861 return false;
862 }
863 if (bt == bt2)
864 continue;
865
866 if (vmem_check_spanoverlap(__func__, vm, bt, bt2)
867 == false)
868 return false;
869 }
870 }
871
872 return true;
873 }
874 #endif /* VMEM_DEBUG */
875
876 /* ---- vmem API */
877
878 /*
879 * vmem_create: create an arena.
880 *
881 * => must not be called from interrupt context.
882 */
883
884 vmem_t *
885 vmem_create(const char *name, vmem_addr_t base, vmem_size_t size,
886 vmem_size_t quantum,
887 vmem_addr_t (*allocfn)(vmem_t *, vmem_size_t, vmem_size_t *, vm_flag_t),
888 void (*freefn)(vmem_t *, vmem_addr_t, vmem_size_t),
889 vmem_t *source, vmem_size_t qcache_max, vm_flag_t flags,
890 int ipl)
891 {
892 vmem_t *vm;
893 int i;
894 #if defined(_KERNEL)
895 static ONCE_DECL(control);
896 #endif /* defined(_KERNEL) */
897
898 KASSERT((flags & (VM_SLEEP|VM_NOSLEEP)) != 0);
899 KASSERT((~flags & (VM_SLEEP|VM_NOSLEEP)) != 0);
900
901 #if defined(_KERNEL)
902 if (RUN_ONCE(&control, vmem_init)) {
903 return NULL;
904 }
905 #endif /* defined(_KERNEL) */
906 vm = xmalloc(sizeof(*vm), flags);
907 if (vm == NULL) {
908 return NULL;
909 }
910
911 VMEM_LOCK_INIT(vm, ipl);
912 vm->vm_name = name;
913 vm->vm_quantum_mask = quantum - 1;
914 vm->vm_quantum_shift = calc_order(quantum);
915 KASSERT(ORDER2SIZE(vm->vm_quantum_shift) == quantum);
916 vm->vm_allocfn = allocfn;
917 vm->vm_freefn = freefn;
918 vm->vm_source = source;
919 vm->vm_nbusytag = 0;
920 #if defined(QCACHE)
921 qc_init(vm, qcache_max, ipl);
922 #endif /* defined(QCACHE) */
923
924 CIRCLEQ_INIT(&vm->vm_seglist);
925 for (i = 0; i < VMEM_MAXORDER; i++) {
926 LIST_INIT(&vm->vm_freelist[i]);
927 }
928 vm->vm_hashlist = NULL;
929 if (vmem_rehash(vm, VMEM_HASHSIZE_INIT, flags)) {
930 vmem_destroy1(vm);
931 return NULL;
932 }
933
934 if (size != 0) {
935 if (vmem_add(vm, base, size, flags) == 0) {
936 vmem_destroy1(vm);
937 return NULL;
938 }
939 }
940
941 #if defined(_KERNEL)
942 mutex_enter(&vmem_list_lock);
943 LIST_INSERT_HEAD(&vmem_list, vm, vm_alllist);
944 mutex_exit(&vmem_list_lock);
945 #endif /* defined(_KERNEL) */
946
947 return vm;
948 }
949
950 void
951 vmem_destroy(vmem_t *vm)
952 {
953
954 #if defined(_KERNEL)
955 mutex_enter(&vmem_list_lock);
956 LIST_REMOVE(vm, vm_alllist);
957 mutex_exit(&vmem_list_lock);
958 #endif /* defined(_KERNEL) */
959
960 vmem_destroy1(vm);
961 }
962
963 vmem_size_t
964 vmem_roundup_size(vmem_t *vm, vmem_size_t size)
965 {
966
967 return (size + vm->vm_quantum_mask) & ~vm->vm_quantum_mask;
968 }
969
970 /*
971 * vmem_alloc:
972 *
973 * => caller must ensure appropriate spl,
974 * if the arena can be accessed from interrupt context.
975 */
976
977 vmem_addr_t
978 vmem_alloc(vmem_t *vm, vmem_size_t size, vm_flag_t flags)
979 {
980 const vm_flag_t strat __unused = flags & VM_FITMASK;
981
982 KASSERT((flags & (VM_SLEEP|VM_NOSLEEP)) != 0);
983 KASSERT((~flags & (VM_SLEEP|VM_NOSLEEP)) != 0);
984
985 KASSERT(size > 0);
986 KASSERT(strat == VM_BESTFIT || strat == VM_INSTANTFIT);
987 if ((flags & VM_SLEEP) != 0) {
988 ASSERT_SLEEPABLE();
989 }
990
991 #if defined(QCACHE)
992 if (size <= vm->vm_qcache_max) {
993 int qidx = (size + vm->vm_quantum_mask) >> vm->vm_quantum_shift;
994 qcache_t *qc = vm->vm_qcache[qidx - 1];
995
996 return (vmem_addr_t)pool_cache_get(qc->qc_cache,
997 vmf_to_prf(flags));
998 }
999 #endif /* defined(QCACHE) */
1000
1001 return vmem_xalloc(vm, size, 0, 0, 0, 0, 0, flags);
1002 }
1003
1004 vmem_addr_t
1005 vmem_xalloc(vmem_t *vm, vmem_size_t size0, vmem_size_t align, vmem_size_t phase,
1006 vmem_size_t nocross, vmem_addr_t minaddr, vmem_addr_t maxaddr,
1007 vm_flag_t flags)
1008 {
1009 struct vmem_freelist *list;
1010 struct vmem_freelist *first;
1011 struct vmem_freelist *end;
1012 bt_t *bt;
1013 bt_t *btnew;
1014 bt_t *btnew2;
1015 const vmem_size_t size = vmem_roundup_size(vm, size0);
1016 vm_flag_t strat = flags & VM_FITMASK;
1017 vmem_addr_t start;
1018
1019 KASSERT(size0 > 0);
1020 KASSERT(size > 0);
1021 KASSERT(strat == VM_BESTFIT || strat == VM_INSTANTFIT);
1022 if ((flags & VM_SLEEP) != 0) {
1023 ASSERT_SLEEPABLE();
1024 }
1025 KASSERT((align & vm->vm_quantum_mask) == 0);
1026 KASSERT((align & (align - 1)) == 0);
1027 KASSERT((phase & vm->vm_quantum_mask) == 0);
1028 KASSERT((nocross & vm->vm_quantum_mask) == 0);
1029 KASSERT((nocross & (nocross - 1)) == 0);
1030 KASSERT((align == 0 && phase == 0) || phase < align);
1031 KASSERT(nocross == 0 || nocross >= size);
1032 KASSERT(maxaddr == 0 || minaddr < maxaddr);
1033 KASSERT(!VMEM_CROSS_P(phase, phase + size - 1, nocross));
1034
1035 if (align == 0) {
1036 align = vm->vm_quantum_mask + 1;
1037 }
1038 btnew = bt_alloc(vm, flags);
1039 if (btnew == NULL) {
1040 return VMEM_ADDR_NULL;
1041 }
1042 btnew2 = bt_alloc(vm, flags); /* XXX not necessary if no restrictions */
1043 if (btnew2 == NULL) {
1044 bt_free(vm, btnew);
1045 return VMEM_ADDR_NULL;
1046 }
1047
1048 retry_strat:
1049 first = bt_freehead_toalloc(vm, size, strat);
1050 end = &vm->vm_freelist[VMEM_MAXORDER];
1051 retry:
1052 bt = NULL;
1053 VMEM_LOCK(vm);
1054 KASSERT(vmem_check_sanity(vm));
1055 if (strat == VM_INSTANTFIT) {
1056 for (list = first; list < end; list++) {
1057 bt = LIST_FIRST(list);
1058 if (bt != NULL) {
1059 start = vmem_fit(bt, size, align, phase,
1060 nocross, minaddr, maxaddr);
1061 if (start != VMEM_ADDR_NULL) {
1062 goto gotit;
1063 }
1064 }
1065 }
1066 } else { /* VM_BESTFIT */
1067 for (list = first; list < end; list++) {
1068 LIST_FOREACH(bt, list, bt_freelist) {
1069 if (bt->bt_size >= size) {
1070 start = vmem_fit(bt, size, align, phase,
1071 nocross, minaddr, maxaddr);
1072 if (start != VMEM_ADDR_NULL) {
1073 goto gotit;
1074 }
1075 }
1076 }
1077 }
1078 }
1079 VMEM_UNLOCK(vm);
1080 #if 1
1081 if (strat == VM_INSTANTFIT) {
1082 strat = VM_BESTFIT;
1083 goto retry_strat;
1084 }
1085 #endif
1086 if (align != vm->vm_quantum_mask + 1 || phase != 0 ||
1087 nocross != 0 || minaddr != 0 || maxaddr != 0) {
1088
1089 /*
1090 * XXX should try to import a region large enough to
1091 * satisfy restrictions?
1092 */
1093
1094 goto fail;
1095 }
1096 if (vmem_import(vm, size, flags) == 0) {
1097 goto retry;
1098 }
1099 /* XXX */
1100 fail:
1101 bt_free(vm, btnew);
1102 bt_free(vm, btnew2);
1103 return VMEM_ADDR_NULL;
1104
1105 gotit:
1106 KASSERT(bt->bt_type == BT_TYPE_FREE);
1107 KASSERT(bt->bt_size >= size);
1108 bt_remfree(vm, bt);
1109 KASSERT(vmem_check_sanity(vm));
1110 if (bt->bt_start != start) {
1111 btnew2->bt_type = BT_TYPE_FREE;
1112 btnew2->bt_start = bt->bt_start;
1113 btnew2->bt_size = start - bt->bt_start;
1114 bt->bt_start = start;
1115 bt->bt_size -= btnew2->bt_size;
1116 bt_insfree(vm, btnew2);
1117 bt_insseg(vm, btnew2, CIRCLEQ_PREV(bt, bt_seglist));
1118 btnew2 = NULL;
1119 KASSERT(vmem_check_sanity(vm));
1120 }
1121 KASSERT(bt->bt_start == start);
1122 if (bt->bt_size != size && bt->bt_size - size > vm->vm_quantum_mask) {
1123 /* split */
1124 btnew->bt_type = BT_TYPE_BUSY;
1125 btnew->bt_start = bt->bt_start;
1126 btnew->bt_size = size;
1127 bt->bt_start = bt->bt_start + size;
1128 bt->bt_size -= size;
1129 bt_insfree(vm, bt);
1130 bt_insseg(vm, btnew, CIRCLEQ_PREV(bt, bt_seglist));
1131 bt_insbusy(vm, btnew);
1132 KASSERT(vmem_check_sanity(vm));
1133 VMEM_UNLOCK(vm);
1134 } else {
1135 bt->bt_type = BT_TYPE_BUSY;
1136 bt_insbusy(vm, bt);
1137 KASSERT(vmem_check_sanity(vm));
1138 VMEM_UNLOCK(vm);
1139 bt_free(vm, btnew);
1140 btnew = bt;
1141 }
1142 if (btnew2 != NULL) {
1143 bt_free(vm, btnew2);
1144 }
1145 KASSERT(btnew->bt_size >= size);
1146 btnew->bt_type = BT_TYPE_BUSY;
1147
1148 KASSERT(vmem_check_sanity(vm));
1149 return btnew->bt_start;
1150 }
1151
1152 /*
1153 * vmem_free:
1154 *
1155 * => caller must ensure appropriate spl,
1156 * if the arena can be accessed from interrupt context.
1157 */
1158
1159 void
1160 vmem_free(vmem_t *vm, vmem_addr_t addr, vmem_size_t size)
1161 {
1162
1163 KASSERT(addr != VMEM_ADDR_NULL);
1164 KASSERT(size > 0);
1165
1166 #if defined(QCACHE)
1167 if (size <= vm->vm_qcache_max) {
1168 int qidx = (size + vm->vm_quantum_mask) >> vm->vm_quantum_shift;
1169 qcache_t *qc = vm->vm_qcache[qidx - 1];
1170
1171 return pool_cache_put(qc->qc_cache, (void *)addr);
1172 }
1173 #endif /* defined(QCACHE) */
1174
1175 vmem_xfree(vm, addr, size);
1176 }
1177
1178 void
1179 vmem_xfree(vmem_t *vm, vmem_addr_t addr, vmem_size_t size)
1180 {
1181 bt_t *bt;
1182 bt_t *t;
1183
1184 KASSERT(addr != VMEM_ADDR_NULL);
1185 KASSERT(size > 0);
1186
1187 VMEM_LOCK(vm);
1188
1189 bt = bt_lookupbusy(vm, addr);
1190 KASSERT(bt != NULL);
1191 KASSERT(bt->bt_start == addr);
1192 KASSERT(bt->bt_size == vmem_roundup_size(vm, size) ||
1193 bt->bt_size - vmem_roundup_size(vm, size) <= vm->vm_quantum_mask);
1194 KASSERT(bt->bt_type == BT_TYPE_BUSY);
1195 bt_rembusy(vm, bt);
1196 bt->bt_type = BT_TYPE_FREE;
1197
1198 /* coalesce */
1199 t = CIRCLEQ_NEXT(bt, bt_seglist);
1200 if (t != NULL && t->bt_type == BT_TYPE_FREE) {
1201 KASSERT(BT_END(bt) == t->bt_start);
1202 bt_remfree(vm, t);
1203 bt_remseg(vm, t);
1204 bt->bt_size += t->bt_size;
1205 bt_free(vm, t);
1206 }
1207 t = CIRCLEQ_PREV(bt, bt_seglist);
1208 if (t != NULL && t->bt_type == BT_TYPE_FREE) {
1209 KASSERT(BT_END(t) == bt->bt_start);
1210 bt_remfree(vm, t);
1211 bt_remseg(vm, t);
1212 bt->bt_size += t->bt_size;
1213 bt->bt_start = t->bt_start;
1214 bt_free(vm, t);
1215 }
1216
1217 t = CIRCLEQ_PREV(bt, bt_seglist);
1218 KASSERT(t != NULL);
1219 KASSERT(BT_ISSPAN_P(t) || t->bt_type == BT_TYPE_BUSY);
1220 if (vm->vm_freefn != NULL && t->bt_type == BT_TYPE_SPAN &&
1221 t->bt_size == bt->bt_size) {
1222 vmem_addr_t spanaddr;
1223 vmem_size_t spansize;
1224
1225 KASSERT(t->bt_start == bt->bt_start);
1226 spanaddr = bt->bt_start;
1227 spansize = bt->bt_size;
1228 bt_remseg(vm, bt);
1229 bt_free(vm, bt);
1230 bt_remseg(vm, t);
1231 bt_free(vm, t);
1232 VMEM_UNLOCK(vm);
1233 (*vm->vm_freefn)(vm->vm_source, spanaddr, spansize);
1234 } else {
1235 bt_insfree(vm, bt);
1236 VMEM_UNLOCK(vm);
1237 }
1238 }
1239
1240 /*
1241 * vmem_add:
1242 *
1243 * => caller must ensure appropriate spl,
1244 * if the arena can be accessed from interrupt context.
1245 */
1246
1247 vmem_addr_t
1248 vmem_add(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, vm_flag_t flags)
1249 {
1250
1251 return vmem_add1(vm, addr, size, flags, BT_TYPE_SPAN_STATIC);
1252 }
1253
1254 /*
1255 * vmem_reap: reap unused resources.
1256 *
1257 * => return true if we successfully reaped something.
1258 */
1259
1260 bool
1261 vmem_reap(vmem_t *vm)
1262 {
1263 bool didsomething = false;
1264
1265 #if defined(QCACHE)
1266 didsomething = qc_reap(vm);
1267 #endif /* defined(QCACHE) */
1268 return didsomething;
1269 }
1270
1271 /* ---- rehash */
1272
1273 #if defined(_KERNEL)
1274 static struct callout vmem_rehash_ch;
1275 static int vmem_rehash_interval;
1276 static struct workqueue *vmem_rehash_wq;
1277 static struct work vmem_rehash_wk;
1278
1279 static void
1280 vmem_rehash_all(struct work *wk, void *dummy)
1281 {
1282 vmem_t *vm;
1283
1284 KASSERT(wk == &vmem_rehash_wk);
1285 mutex_enter(&vmem_list_lock);
1286 LIST_FOREACH(vm, &vmem_list, vm_alllist) {
1287 size_t desired;
1288 size_t current;
1289
1290 if (!VMEM_TRYLOCK(vm)) {
1291 continue;
1292 }
1293 desired = vm->vm_nbusytag;
1294 current = vm->vm_hashsize;
1295 VMEM_UNLOCK(vm);
1296
1297 if (desired > VMEM_HASHSIZE_MAX) {
1298 desired = VMEM_HASHSIZE_MAX;
1299 } else if (desired < VMEM_HASHSIZE_MIN) {
1300 desired = VMEM_HASHSIZE_MIN;
1301 }
1302 if (desired > current * 2 || desired * 2 < current) {
1303 vmem_rehash(vm, desired, VM_NOSLEEP);
1304 }
1305 }
1306 mutex_exit(&vmem_list_lock);
1307
1308 callout_schedule(&vmem_rehash_ch, vmem_rehash_interval);
1309 }
1310
1311 static void
1312 vmem_rehash_all_kick(void *dummy)
1313 {
1314
1315 workqueue_enqueue(vmem_rehash_wq, &vmem_rehash_wk, NULL);
1316 }
1317
1318 void
1319 vmem_rehash_start(void)
1320 {
1321 int error;
1322
1323 error = workqueue_create(&vmem_rehash_wq, "vmem_rehash",
1324 vmem_rehash_all, NULL, PRI_VM, IPL_SOFTCLOCK, WQ_MPSAFE);
1325 if (error) {
1326 panic("%s: workqueue_create %d\n", __func__, error);
1327 }
1328 callout_init(&vmem_rehash_ch, CALLOUT_MPSAFE);
1329 callout_setfunc(&vmem_rehash_ch, vmem_rehash_all_kick, NULL);
1330
1331 vmem_rehash_interval = hz * 10;
1332 callout_schedule(&vmem_rehash_ch, vmem_rehash_interval);
1333 }
1334 #endif /* defined(_KERNEL) */
1335
1336 /* ---- debug */
1337
1338 #if defined(DDB)
1339 static bt_t *
1340 vmem_whatis_lookup(vmem_t *vm, uintptr_t addr)
1341 {
1342 bt_t *bt;
1343
1344 CIRCLEQ_FOREACH(bt, &vm->vm_seglist, bt_seglist) {
1345 if (BT_ISSPAN_P(bt)) {
1346 continue;
1347 }
1348 if (bt->bt_start <= addr && addr < BT_END(bt)) {
1349 return bt;
1350 }
1351 }
1352
1353 return NULL;
1354 }
1355
1356 void
1357 vmem_whatis(uintptr_t addr, void (*pr)(const char *, ...))
1358 {
1359 vmem_t *vm;
1360
1361 LIST_FOREACH(vm, &vmem_list, vm_alllist) {
1362 bt_t *bt;
1363
1364 bt = vmem_whatis_lookup(vm, addr);
1365 if (bt == NULL) {
1366 continue;
1367 }
1368 (*pr)("%p is %p+%zu in VMEM '%s' (%s)\n",
1369 (void *)addr, (void *)bt->bt_start,
1370 (size_t)(addr - bt->bt_start), vm->vm_name,
1371 (bt->bt_type == BT_TYPE_BUSY) ? "allocated" : "free");
1372 }
1373 }
1374
1375 static void
1376 vmem_showall(void (*pr)(const char *, ...))
1377 {
1378 vmem_t *vm;
1379
1380 LIST_FOREACH(vm, &vmem_list, vm_alllist) {
1381 (*pr)("VMEM '%s' at %p\n", vm->vm_name, vm);
1382 if (vm->vm_source)
1383 (*pr)(" VMEM backend '%s' at %p\n",
1384 vm->vm_source->vm_name, vm->vm_source);
1385 }
1386 }
1387
1388 static void
1389 vmem_show(uintptr_t addr, void (*pr)(const char *, ...))
1390 {
1391 vmem_t *vm;
1392 bt_t *bt = NULL;
1393
1394 LIST_FOREACH(vm, &vmem_list, vm_alllist) {
1395 if ((uintptr_t)vm == addr)
1396 goto found;
1397 }
1398
1399 LIST_FOREACH(vm, &vmem_list, vm_alllist) {
1400 bt = vmem_whatis_lookup(vm, addr);
1401 if (bt != NULL)
1402 goto found;
1403 }
1404
1405 return;
1406 found:
1407
1408 (*pr)("VMEM '%s' spans\n", vm->vm_name);
1409 CIRCLEQ_FOREACH(bt, &vm->vm_seglist, bt_seglist) {
1410 (*pr)(" 0x%"PRIx64" - 0x%"PRIx64" %s\n",
1411 bt->bt_start, BT_END(bt),
1412 (bt->bt_type == BT_TYPE_BUSY) ?
1413 "allocated" :
1414 (bt->bt_type == BT_TYPE_FREE) ?
1415 "free" :
1416 (bt->bt_type == BT_TYPE_SPAN) ?
1417 "span" : "static span");
1418 }
1419 }
1420
1421 void
1422 vmem_print(uintptr_t addr, const char *modif, void (*pr)(const char *, ...))
1423 {
1424 if (modif[0] == 'a') {
1425 vmem_showall(pr);
1426 return;
1427 }
1428
1429 vmem_show(addr, pr);
1430 }
1431 #endif /* defined(DDB) */
1432
1433 #if defined(VMEM_DEBUG)
1434
1435 #if !defined(_KERNEL)
1436 #include <stdio.h>
1437 #endif /* !defined(_KERNEL) */
1438
1439 void bt_dump(const bt_t *);
1440
1441 void
1442 bt_dump(const bt_t *bt)
1443 {
1444
1445 printf("\t%p: %" PRIu64 ", %" PRIu64 ", %d\n",
1446 bt, (uint64_t)bt->bt_start, (uint64_t)bt->bt_size,
1447 bt->bt_type);
1448 }
1449
1450 void
1451 vmem_dump(const vmem_t *vm)
1452 {
1453 const bt_t *bt;
1454 int i;
1455
1456 printf("vmem %p '%s'\n", vm, vm->vm_name);
1457 CIRCLEQ_FOREACH(bt, &vm->vm_seglist, bt_seglist) {
1458 bt_dump(bt);
1459 }
1460
1461 for (i = 0; i < VMEM_MAXORDER; i++) {
1462 const struct vmem_freelist *fl = &vm->vm_freelist[i];
1463
1464 if (LIST_EMPTY(fl)) {
1465 continue;
1466 }
1467
1468 printf("freelist[%d]\n", i);
1469 LIST_FOREACH(bt, fl, bt_freelist) {
1470 bt_dump(bt);
1471 if (bt->bt_size) {
1472 }
1473 }
1474 }
1475 }
1476
1477 #if !defined(_KERNEL)
1478
1479 int
1480 main()
1481 {
1482 vmem_t *vm;
1483 vmem_addr_t p;
1484 struct reg {
1485 vmem_addr_t p;
1486 vmem_size_t sz;
1487 bool x;
1488 } *reg = NULL;
1489 int nreg = 0;
1490 int nalloc = 0;
1491 int nfree = 0;
1492 vmem_size_t total = 0;
1493 #if 1
1494 vm_flag_t strat = VM_INSTANTFIT;
1495 #else
1496 vm_flag_t strat = VM_BESTFIT;
1497 #endif
1498
1499 vm = vmem_create("test", VMEM_ADDR_NULL, 0, 1,
1500 NULL, NULL, NULL, 0, VM_SLEEP);
1501 if (vm == NULL) {
1502 printf("vmem_create\n");
1503 exit(EXIT_FAILURE);
1504 }
1505 vmem_dump(vm);
1506
1507 p = vmem_add(vm, 100, 200, VM_SLEEP);
1508 p = vmem_add(vm, 2000, 1, VM_SLEEP);
1509 p = vmem_add(vm, 40000, 0x10000000>>12, VM_SLEEP);
1510 p = vmem_add(vm, 10000, 10000, VM_SLEEP);
1511 p = vmem_add(vm, 500, 1000, VM_SLEEP);
1512 vmem_dump(vm);
1513 for (;;) {
1514 struct reg *r;
1515 int t = rand() % 100;
1516
1517 if (t > 45) {
1518 /* alloc */
1519 vmem_size_t sz = rand() % 500 + 1;
1520 bool x;
1521 vmem_size_t align, phase, nocross;
1522 vmem_addr_t minaddr, maxaddr;
1523
1524 if (t > 70) {
1525 x = true;
1526 /* XXX */
1527 align = 1 << (rand() % 15);
1528 phase = rand() % 65536;
1529 nocross = 1 << (rand() % 15);
1530 if (align <= phase) {
1531 phase = 0;
1532 }
1533 if (VMEM_CROSS_P(phase, phase + sz - 1,
1534 nocross)) {
1535 nocross = 0;
1536 }
1537 minaddr = rand() % 50000;
1538 maxaddr = rand() % 70000;
1539 if (minaddr > maxaddr) {
1540 minaddr = 0;
1541 maxaddr = 0;
1542 }
1543 printf("=== xalloc %" PRIu64
1544 " align=%" PRIu64 ", phase=%" PRIu64
1545 ", nocross=%" PRIu64 ", min=%" PRIu64
1546 ", max=%" PRIu64 "\n",
1547 (uint64_t)sz,
1548 (uint64_t)align,
1549 (uint64_t)phase,
1550 (uint64_t)nocross,
1551 (uint64_t)minaddr,
1552 (uint64_t)maxaddr);
1553 p = vmem_xalloc(vm, sz, align, phase, nocross,
1554 minaddr, maxaddr, strat|VM_SLEEP);
1555 } else {
1556 x = false;
1557 printf("=== alloc %" PRIu64 "\n", (uint64_t)sz);
1558 p = vmem_alloc(vm, sz, strat|VM_SLEEP);
1559 }
1560 printf("-> %" PRIu64 "\n", (uint64_t)p);
1561 vmem_dump(vm);
1562 if (p == VMEM_ADDR_NULL) {
1563 if (x) {
1564 continue;
1565 }
1566 break;
1567 }
1568 nreg++;
1569 reg = realloc(reg, sizeof(*reg) * nreg);
1570 r = ®[nreg - 1];
1571 r->p = p;
1572 r->sz = sz;
1573 r->x = x;
1574 total += sz;
1575 nalloc++;
1576 } else if (nreg != 0) {
1577 /* free */
1578 r = ®[rand() % nreg];
1579 printf("=== free %" PRIu64 ", %" PRIu64 "\n",
1580 (uint64_t)r->p, (uint64_t)r->sz);
1581 if (r->x) {
1582 vmem_xfree(vm, r->p, r->sz);
1583 } else {
1584 vmem_free(vm, r->p, r->sz);
1585 }
1586 total -= r->sz;
1587 vmem_dump(vm);
1588 *r = reg[nreg - 1];
1589 nreg--;
1590 nfree++;
1591 }
1592 printf("total=%" PRIu64 "\n", (uint64_t)total);
1593 }
1594 fprintf(stderr, "total=%" PRIu64 ", nalloc=%d, nfree=%d\n",
1595 (uint64_t)total, nalloc, nfree);
1596 exit(EXIT_SUCCESS);
1597 }
1598 #endif /* !defined(_KERNEL) */
1599 #endif /* defined(VMEM_DEBUG) */
1600