Merge branch 'vfs-dev'
[lunaix-os.git] / lunaix-os / kernel / mm / cake.c
1 /**
2  * @file cake.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 CACHE_LINE_SIZE 128
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     if (!pa) {
33         return NULL;
34     }
35     return vmm_vmap(pa, cake_pg * PG_SIZE, PG_PREM_RW);
36 }
37
38 struct cake_s*
39 __new_cake(struct cake_pile* pile)
40 {
41     struct cake_s* cake = __alloc_cake(pile->pg_per_cake);
42
43     if (!cake) {
44         return NULL;
45     }
46
47     int max_piece = pile->pieces_per_cake;
48
49     cake->first_piece = (void*)((uintptr_t)cake + pile->offset);
50     cake->next_free = 0;
51
52     piece_index_t* free_list = cake->free_list;
53     for (size_t i = 0; i < max_piece - 1; i++) {
54         free_list[i] = i + 1;
55     }
56     free_list[max_piece - 1] = EO_FREE_PIECE;
57
58     llist_append(&pile->free, &cake->cakes);
59
60     return cake;
61 }
62
63 void
64 __init_pile(struct cake_pile* pile,
65             char* name,
66             unsigned int piece_size,
67             unsigned int pg_per_cake,
68             int options)
69 {
70     if (!pile) {
71         return;
72     }
73
74     unsigned int offset = sizeof(long);
75
76     // 默认每块儿蛋糕对齐到地址总线宽度
77     if ((options & PILE_CACHELINE)) {
78         // 对齐到128字节缓存行大小,主要用于DMA
79         offset = CACHE_LINE_SIZE;
80     }
81
82     piece_size = ROUNDUP(piece_size, offset);
83     *pile = (struct cake_pile){ .piece_size = piece_size,
84                                 .cakes_count = 1,
85                                 .pieces_per_cake =
86                                   (pg_per_cake * PG_SIZE) /
87                                   (piece_size + sizeof(piece_index_t)),
88                                 .pg_per_cake = pg_per_cake };
89
90     unsigned int free_list_size = pile->pieces_per_cake * sizeof(piece_index_t);
91
92     pile->offset = ROUNDUP(sizeof(struct cake_s) + free_list_size, offset);
93     pile->pieces_per_cake -= ICEIL((pile->offset - free_list_size), piece_size);
94
95     strncpy(pile->pile_name, name, PILE_NAME_MAXLEN);
96
97     llist_init_head(&pile->free);
98     llist_init_head(&pile->full);
99     llist_init_head(&pile->partial);
100     llist_append(&piles, &pile->piles);
101 }
102
103 void
104 cake_init()
105 {
106     __init_pile(&master_pile, "pinkamina", sizeof(master_pile), 1, 0);
107 }
108
109 struct cake_pile*
110 cake_new_pile(char* name,
111               unsigned int piece_size,
112               unsigned int pg_per_cake,
113               int options)
114 {
115     struct cake_pile* pile = (struct cake_pile*)cake_grab(&master_pile);
116
117     __init_pile(pile, name, piece_size, pg_per_cake, options);
118
119     return pile;
120 }
121
122 void*
123 cake_grab(struct cake_pile* pile)
124 {
125     struct cake_s *pos, *n;
126     if (!llist_empty(&pile->partial)) {
127         pos = list_entry(pile->partial.next, typeof(*pos), cakes);
128     } else if (llist_empty(&pile->free)) {
129         pos = __new_cake(pile);
130     } else {
131         pos = list_entry(pile->free.next, typeof(*pos), cakes);
132     }
133
134     if (!pos)
135         return NULL;
136
137     piece_index_t found_index = pos->next_free;
138     pos->next_free = pos->free_list[found_index];
139     pos->used_pieces++;
140     pile->alloced_pieces++;
141
142     llist_delete(&pos->cakes);
143     if (pos->next_free == EO_FREE_PIECE) {
144         llist_append(&pile->full, &pos->cakes);
145     } else {
146         llist_append(&pile->partial, &pos->cakes);
147     }
148
149     return (void*)((uintptr_t)pos->first_piece +
150                    found_index * pile->piece_size);
151 }
152
153 int
154 cake_release(struct cake_pile* pile, void* area)
155 {
156     piece_index_t piece_index;
157     struct cake_s *pos, *n;
158     struct llist_header* hdrs[2] = { &pile->full, &pile->partial };
159
160     for (size_t i = 0; i < 2; i++) {
161         llist_for_each(pos, n, hdrs[i], cakes)
162         {
163             if (pos->first_piece > area) {
164                 continue;
165             }
166             piece_index =
167               (uintptr_t)(area - pos->first_piece) / pile->piece_size;
168             if (piece_index < pile->pieces_per_cake) {
169                 goto found;
170             }
171         }
172     }
173
174     return 0;
175
176 found:
177     pos->free_list[piece_index] = pos->next_free;
178     pos->next_free = piece_index;
179     pos->used_pieces--;
180     pile->alloced_pieces--;
181
182     llist_delete(&pos->cakes);
183     if (!pos->used_pieces) {
184         llist_append(&pile->free, &pos->cakes);
185     } else {
186         llist_append(&pile->partial, &pos->cakes);
187     }
188
189     return 1;
190 }
191
192 void
193 cake_stats()
194 {
195     kprintf(KDEBUG "<name> <cake> <pg/c> <p/c> <alloced>\n");
196
197     struct cake_pile *pos, *n;
198     llist_for_each(pos, n, &piles, piles)
199     {
200         kprintf("%s %d %d %d %d\n",
201                 pos->pile_name,
202                 pos->cakes_count,
203                 pos->pg_per_cake,
204                 pos->pieces_per_cake,
205                 pos->alloced_pieces);
206     }
207 }