Merge branch 'master' into sata-ahci-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 overhead_size =
80       sizeof(struct cake_s) + pile->pieces_per_cake * sizeof(piece_index_t);
81
82     pile->offset = ROUNDUP(overhead_size, offset);
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 found:
124     piece_index_t found_index = pos->next_free;
125     pos->next_free = pos->free_list[found_index];
126     pos->used_pieces++;
127     pile->alloced_pieces++;
128
129     llist_delete(&pos->cakes);
130     if (pos->next_free == EO_FREE_PIECE) {
131         llist_append(&pile->full, &pos->cakes);
132     } else {
133         llist_append(&pile->partial, &pos->cakes);
134     }
135
136     return (void*)((uintptr_t)pos->first_piece +
137                    found_index * pile->piece_size);
138 }
139
140 int
141 cake_release(struct cake_pile* pile, void* area)
142 {
143     piece_index_t area_index;
144     struct cake_s *pos, *n;
145     struct llist_header* hdrs[2] = { &pile->full, &pile->partial };
146
147     for (size_t i = 0; i < 2; i++) {
148         llist_for_each(pos, n, hdrs[i], cakes)
149         {
150             if (pos->first_piece > area) {
151                 continue;
152             }
153             area_index =
154               (uintptr_t)(area - pos->first_piece) / pile->piece_size;
155             if (area_index < pile->pieces_per_cake) {
156                 goto found;
157             }
158         }
159     }
160
161     return 0;
162
163 found:
164     pos->free_list[area_index] = pos->next_free;
165     pos->next_free = area_index;
166     pos->used_pieces--;
167     pile->alloced_pieces--;
168
169     llist_delete(pos);
170     if (!pos->used_pieces) {
171         llist_append(&pile->free, &pos->cakes);
172     } else {
173         llist_append(&pile->partial, &pos->cakes);
174     }
175
176     return 1;
177 }
178
179 void
180 cake_stats()
181 {
182     kprintf(KDEBUG "<name> <cake> <pg/c> <p/c> <alloced>\n");
183
184     struct cake_pile *pos, *n;
185     llist_for_each(pos, n, &piles, piles)
186     {
187         kprintf("%s %d %d %d %d\n",
188                 pos->pile_name,
189                 pos->cakes_count,
190                 pos->pg_per_cake,
191                 pos->pieces_per_cake,
192                 pos->alloced_pieces);
193     }
194 }