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