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