Support to multi-threading and pthread interface (POSIX.1-2008) (#23)
[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     ptr_t pa = (ptr_t)pmm_alloc_cpage(cake_pg, 0);
32     if (!pa) {
33         return NULL;
34     }
35     return vmap(pa, cake_pg * PG_SIZE, PG_PREM_RW, 0);
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     u32_t max_piece = pile->pieces_per_cake;
48
49     assert(max_piece);
50
51     cake->first_piece = (void*)((ptr_t)cake + pile->offset);
52     cake->next_free = 0;
53     pile->cakes_count++;
54
55     piece_index_t* free_list = cake->free_list;
56     for (size_t i = 0; i < max_piece - 1; i++) {
57         free_list[i] = i + 1;
58     }
59     free_list[max_piece - 1] = EO_FREE_PIECE;
60
61     llist_append(&pile->free, &cake->cakes);
62
63     return cake;
64 }
65
66 void
67 __init_pile(struct cake_pile* pile,
68             char* name,
69             unsigned int piece_size,
70             unsigned int pg_per_cake,
71             int options)
72 {
73     if (!pile) {
74         return;
75     }
76
77     unsigned int offset = sizeof(long);
78
79     // 默认每块儿蛋糕对齐到地址总线宽度
80     if ((options & PILE_ALIGN_CACHE)) {
81         // 对齐到128字节缓存行大小,主要用于DMA
82         offset = CACHE_LINE_SIZE;
83     }
84
85     piece_size = ROUNDUP(piece_size, offset);
86     *pile = (struct cake_pile){ .piece_size = piece_size,
87                                 .cakes_count = 0,
88                                 .pieces_per_cake =
89                                   (pg_per_cake * PG_SIZE) /
90                                   (piece_size + sizeof(piece_index_t)),
91                                 .pg_per_cake = pg_per_cake };
92
93     unsigned int free_list_size = pile->pieces_per_cake * sizeof(piece_index_t);
94
95     pile->offset = ROUNDUP(sizeof(struct cake_s) + free_list_size, offset);
96     pile->pieces_per_cake -= ICEIL((pile->offset - free_list_size), piece_size);
97
98     strncpy(pile->pile_name, name, PILE_NAME_MAXLEN);
99
100     llist_init_head(&pile->free);
101     llist_init_head(&pile->full);
102     llist_init_head(&pile->partial);
103     llist_append(&piles, &pile->piles);
104 }
105
106 void
107 cake_init()
108 {
109     __init_pile(&master_pile, "pinkamina", sizeof(master_pile), 1, 0);
110 }
111
112 struct cake_pile*
113 cake_new_pile(char* name,
114               unsigned int piece_size,
115               unsigned int pg_per_cake,
116               int options)
117 {
118     struct cake_pile* pile = (struct cake_pile*)cake_grab(&master_pile);
119
120     __init_pile(pile, name, piece_size, pg_per_cake, options);
121
122     return pile;
123 }
124
125 void
126 cake_set_constructor(struct cake_pile* pile, pile_cb ctor)
127 {
128     pile->ctor = ctor;
129 }
130
131 void*
132 cake_grab(struct cake_pile* pile)
133 {
134     struct cake_s *pos, *n;
135     if (!llist_empty(&pile->partial)) {
136         pos = list_entry(pile->partial.next, typeof(*pos), cakes);
137     } else if (llist_empty(&pile->free)) {
138         pos = __new_cake(pile);
139     } else {
140         pos = list_entry(pile->free.next, typeof(*pos), cakes);
141     }
142
143     if (!pos)
144         return NULL;
145
146     piece_index_t found_index = pos->next_free;
147     pos->next_free = pos->free_list[found_index];
148     pos->used_pieces++;
149     pile->alloced_pieces++;
150
151     llist_delete(&pos->cakes);
152     if (pos->free_list[pos->next_free] == EO_FREE_PIECE) {
153         llist_append(&pile->full, &pos->cakes);
154     } else {
155         llist_append(&pile->partial, &pos->cakes);
156     }
157
158     void* ptr =
159       (void*)((ptr_t)pos->first_piece + found_index * pile->piece_size);
160
161     if (pile->ctor) {
162         pile->ctor(pile, ptr);
163     }
164
165     return ptr;
166 }
167
168 int
169 cake_release(struct cake_pile* pile, void* area)
170 {
171     piece_index_t piece_index;
172     size_t dsize = 0;
173     struct cake_s *pos, *n;
174     struct llist_header* hdrs[2] = { &pile->full, &pile->partial };
175
176     for (size_t i = 0; i < 2; i++) {
177         llist_for_each(pos, n, hdrs[i], cakes)
178         {
179             if (pos->first_piece > area) {
180                 continue;
181             }
182             dsize = (ptr_t)(area - pos->first_piece);
183             piece_index = dsize / pile->piece_size;
184             if (piece_index < pile->pieces_per_cake) {
185                 goto found;
186             }
187         }
188     }
189
190     return 0;
191
192 found:
193     assert(!(dsize % pile->piece_size));
194     pos->free_list[piece_index] = pos->next_free;
195     pos->next_free = piece_index;
196
197     assert_msg(pos->free_list[piece_index] != pos->next_free, "double free");
198
199     pos->used_pieces--;
200     pile->alloced_pieces--;
201
202     llist_delete(&pos->cakes);
203     if (!pos->used_pieces) {
204         llist_append(&pile->free, &pos->cakes);
205     } else {
206         llist_append(&pile->partial, &pos->cakes);
207     }
208
209     *((unsigned int*)area) = DEADCAKE_MARK;
210
211     return 1;
212 }
213
214 void
215 cake_ctor_zeroing(struct cake_pile* pile, void* piece)
216 {
217     memset(piece, 0, pile->piece_size);
218 }