1#include <math/bit_ops.h>
2#include <math/pow.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 new_size_pages >= min && new_size_pages <= 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 = slab->backing_pages[i];
18 if (assert_nonnull)
19 kassert(page);
20
21 if (!page)
22 return;
23
24 printf(format: "SHRINK!\n");
25 paddr_t phys = page_get_paddr(bp: page);
26 vaddr_t virt = (vaddr_t) slab + i * PAGE_SIZE;
27 vmm_unmap_page(virt);
28 pmm_free_page(addr: phys);
29 slab->backing_pages[i] = NULL;
30 }
31
32 slab->page_count = start;
33}
34
35bool slab_resize(struct slab *slab, size_t new_size_pages) {
36 kassert(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 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 (unlikely(vmm_mark_demand_page(
62 virt, DEMAND_PAGE_FLAG_WRITABLE |
63 DEMAND_PAGE_FLAG_ZERO_MEMORY) < 0)) {
64 goto grow_err;
65 }
66 } else {
67 paddr_t phys = domain_alloc_from_domain(cd: parent->domain, pages: 1);
68 if (!phys)
69 goto grow_err;
70
71 uint64_t flags = slab_page_flags(type: slab->type);
72 if (unlikely(vmm_map_page(virt, phys, flags) < 0)) {
73 pmm_free_page(addr: phys); /* not yet recorded, free directly */
74 goto grow_err;
75 }
76
77 slab->backing_pages[i] = page_for_paddr(paddr: phys);
78 if (slab->type == SLAB_TYPE_NONPAGEABLE_ZERO)
79 memset(hhdm_paddr_to_ptr(phys), 0, PAGE_SIZE);
80 }
81 }
82
83 slab->page_count = new_size_pages;
84 return true;
85
86 grow_err:
87 slab_shrink(slab, start: old, end: new_size_pages, /* assert_nonnull = */ false);
88 return false;
89 }
90
91 slab_shrink(slab, start: new_size_pages, end: old, /* assert_nonnull = */ true);
92 return true;
93}
94