feat: cake (slab) allocator, for dynamic continuous physical page allocation
[lunaix-os.git] / lunaix-os / kernel / mm / cake.c
1 /**
2  * @file valloc.c
3  * @author Lunaixsky (zelong56@gmail.com)
4  * @brief A simplified cake(slab) allocator.
5  *          P.s. I call it cake as slab sounds more 'ridge' to me. :)
6  * @version 0.1
7  * @date 2022-07-02
8  *
9  * @copyright Copyright (c) 2022
10  *
11  */
12
13 #include <klibc/string.h>
14 #include <lunaix/mm/cake.h>
15 #include <lunaix/mm/pmm.h>
16 #include <lunaix/mm/vmm.h>
17 #include <lunaix/spike.h>
18 #include <lunaix/syslog.h>
19
20 LOG_MODULE("CAKE")
21
22 #define SLAB_SIZE PG_SIZE
23
24 struct cake_pile master_pile;
25
26 struct llist_header piles = { .next = &piles, .prev = &piles };
27
28 void*
29 __alloc_cake(unsigned int cake_pg)
30 {
31     uintptr_t pa = pmm_alloc_cpage(KERNEL_PID, cake_pg, 0);
32     return vmm_vmap(pa, cake_pg * PG_SIZE, PG_PREM_RW);
33 }
34
35 struct cake_s*
36 __new_cake(struct cake_pile* pile)
37 {
38     struct cake_s* cake = __alloc_cake(pile->pg_per_cake);
39
40     int max_piece = pile->pieces_per_cake;
41
42     cake->first_piece =
43       (void*)((uintptr_t)(cake + 1) + max_piece * sizeof(piece_index_t));
44     cake->next_free = 0;
45
46     piece_index_t* free_list = &cake->free_list;
47     for (size_t i = 0; i < max_piece - 1; i++) {
48         free_list[i] = i + 1;
49     }
50     free_list[max_piece - 1] = EO_FREE_PIECE;
51
52     llist_append(&pile->free, &cake->cakes);
53
54     return cake;
55 }
56
57 void
58 __init_pile(struct cake_pile* pile,
59             char* name,
60             unsigned int piece_size,
61             unsigned int pg_per_cake)
62 {
63     *pile = (struct cake_pile){ .piece_size = piece_size,
64                                 .cakes_count = 1,
65                                 .pieces_per_cake =
66                                   (pg_per_cake * PG_SIZE) /
67                                   (piece_size + sizeof(piece_index_t)),
68                                 .pg_per_cake = pg_per_cake };
69
70     strncpy(&pile->pile_name, name, PILE_NAME_MAXLEN);
71
72     llist_init_head(&pile->free);
73     llist_init_head(&pile->full);
74     llist_init_head(&pile->partial);
75     llist_append(&piles, &pile->piles);
76 }
77
78 void
79 cake_init()
80 {
81     __init_pile(&master_pile, "master", sizeof(master_pile), 1);
82 }
83
84 struct cake_pile*
85 cake_new_pile(char* name, unsigned int piece_size, unsigned int pg_per_cake)
86 {
87     struct cake_pile* pile = (struct cake_pile*)cake_grab(&master_pile);
88
89     __init_pile(pile, name, piece_size, pg_per_cake);
90
91     return pile;
92 }
93
94 void*
95 cake_grab(struct cake_pile* pile)
96 {
97     struct cake_s *pos, *n;
98     llist_for_each(pos, n, &pile->partial, cakes)
99     {
100         if (pos->next_free != EO_FREE_PIECE) {
101             goto found;
102         }
103     }
104
105     if (llist_empty(&pile->free)) {
106         pos = __new_cake(pile);
107     } else {
108         pos = list_entry(pile->free.next, typeof(*pos), cakes);
109     }
110
111 found:
112     piece_index_t found_index = pos->next_free;
113     pos->next_free = pos->free_list[found_index];
114     pos->used_pieces++;
115     pile->alloced_pieces++;
116
117     llist_delete(&pos->cakes);
118     if (pos->next_free == EO_FREE_PIECE) {
119         llist_append(&pile->full, &pos->cakes);
120     } else {
121         llist_append(&pile->partial, &pos->cakes);
122     }
123
124     return (void*)((uintptr_t)pos->first_piece +
125                    found_index * pile->piece_size);
126 }
127
128 int
129 cake_release(struct cake_pile* pile, void* area)
130 {
131     piece_index_t area_index;
132     struct cake_s *pos, *n;
133     struct llist_header* hdrs[2] = { &pile->full, &pile->partial };
134
135     for (size_t i = 0; i < 2; i++) {
136         llist_for_each(pos, n, hdrs[i], cakes)
137         {
138             if (pos->first_piece > area) {
139                 continue;
140             }
141             area_index =
142               (uintptr_t)(area - pos->first_piece) / pile->piece_size;
143             if (area_index < pile->pieces_per_cake) {
144                 goto found;
145             }
146         }
147     }
148
149     return 0;
150
151 found:
152     pos->free_list[area_index] = pos->next_free;
153     pos->next_free = area_index;
154     pos->used_pieces--;
155     pile->alloced_pieces--;
156
157     llist_delete(pos);
158     if (!pos->used_pieces) {
159         llist_append(&pile->free, &pos->cakes);
160     } else {
161         llist_append(&pile->partial, &pos->cakes);
162     }
163
164     return 1;
165 }
166
167 void
168 cake_stats()
169 {
170     kprintf(KDEBUG "<name> <cake> <pg/c> <p/c> <alloced>\n");
171
172     struct cake_pile *pos, *n;
173     llist_for_each(pos, n, &piles, piles)
174     {
175         kprintf("%s %d %d %d %d\n",
176                 pos->pile_name,
177                 pos->cakes_count,
178                 pos->pg_per_cake,
179                 pos->pieces_per_cake,
180                 pos->alloced_pieces);
181     }
182 }