1#include <math/bit.h>
2#include <math/range.h>
3#include <mem/domain.h>
4#include <mem/pmm.h>
5
6#include "internal.h"
7
8bool slab_can_resize_to(struct slab *slab, size_t new_size_pages) {
9 size_t cap = next_pow2(x: slab->page_count);
10 size_t min = (cap >> 1) + 1;
11 return IN_RANGE(new_size_pages, min, cap);
12}
13
14static void slab_shrink(struct slab *slab, size_t start, size_t end,
15 bool assert_nonnull) {
16 for (size_t i = start; i < end; i++) {
17 struct page *page = atomic_load_rlx(&slab->backing_pages[i]);
18 if (assert_nonnull)
19 enforce(page);
20
21 if (!page)
22 return;
23
24 paddr_t phys = page_get_paddr(bp: page);
25 vaddr_t virt = (vaddr_t) slab + i * PAGE_SIZE;
26 vmm_unmap_page(virt);
27 pmm_free_page(addr: phys);
28 atomic_store_rlx(&slab->backing_pages[i], NULL);
29 }
30
31 slab->page_count = start;
32}
33
34/* returns if the resize was successful or not needed */
35bool slab_resize(struct slab *slab, size_t new_size_pages) {
36 enforce(slab_can_resize_to(slab, new_size_pages));
37 size_t old = slab->page_count;
38
39 if (old == new_size_pages)
40 return true;
41
42 struct slab_domain *parent = slab->parent_cache->parent_domain;
43
44 /* Grow */
45 if (old < new_size_pages) {
46 for (size_t i = old; i < new_size_pages; i++)
47 atomic_store_rlx(&slab->backing_pages[i], NULL);
48
49 for (size_t i = old; i < new_size_pages; i++) {
50 vaddr_t virt = (vaddr_t) slab + i * PAGE_SIZE;
51
52 /* TODO: we can handle in the PAGEABLE type that isn't zero
53 * with stuff once we get the infra for that to exist */
54 if (slab->type == SLAB_TYPE_PAGEABLE_ZERO) {
55 /* TODO: memory commit limits here */
56
57 /* TODO: grow_err needs to handle pages that were just
58 * marked as being demand paged, i.e. vmm_unmap_page should
59 * either have its behavior changed or another function should
60 * get introduced for the sake of this little thing */
61 if (cc_unlikely(vmm_mark_demand_page(
62 virt, DEMAND_PAGE_FLAG_WRITABLE |
63 DEMAND_PAGE_FLAG_ZERO_MEMORY) <
64 0)) {
65 goto grow_err;
66 }
67 } else {
68 paddr_t phys = domain_alloc_from_domain(cd: parent->domain, pages: 1);
69 if (!phys)
70 goto grow_err;
71
72 uint64_t flags = slab_page_flags(type: slab->type);
73 if (cc_unlikely(vmm_map_page(virt, phys, .page_flags = flags) <
74 0)) {
75 pmm_free_page(addr: phys); /* not yet recorded, free directly */
76 goto grow_err;
77 }
78
79 atomic_store_rlx(&slab->backing_pages[i], page_for_paddr(phys));
80 if (slab->type == SLAB_TYPE_NONPAGEABLE_ZERO)
81 memset(hhdm_paddr_to_ptr(phys), 0, PAGE_SIZE);
82 }
83 }
84
85 slab->page_count = new_size_pages;
86 return true;
87
88 grow_err:
89 slab_shrink(slab, start: old, end: new_size_pages, /* assert_nonnull = */ false);
90 return false;
91 }
92
93 slab_shrink(slab, start: new_size_pages, end: old, /* assert_nonnull = */ true);
94 return true;
95}
96