b77d6b7c0b70d711eabe24eb7d5ec56859cb93b3
[lunaix-os.git] / lunaix-os / kernel / fs / ext2 / group.c
1 #include <lunaix/mm/valloc.h>
2
3 #include "ext2.h"
4
5 bcache_zone_t gdesc_bcache_zone;
6
7 static void
8 __cached_gdesc_evict(struct bcache* bc, void* data)
9 {
10     struct ext2_gdesc* gd;
11     gd = (struct ext2_gdesc*)data;
12
13     llist_delete(&gd->groups);
14     llist_delete(&gd->free_grps_blk);
15     llist_delete(&gd->free_grps_ino);
16
17     fsblock_put(gd->buf);
18
19     vfree(gd);
20 }
21
22 static void
23 __cached_gdesc_sync(struct bcache*, unsigned long tag, void* data)
24 {
25     // since all mods to gdesc goes directly into fs buffer,
26     // we just need to invoke the sync on the underlying.
27
28     struct ext2_gdesc* gd;
29     gd = (struct ext2_gdesc*)data;
30
31     fsblock_sync(gd->buf);
32 }
33
34 static struct bcache_ops gdesc_bc_ops = {
35     .release_on_evict = __cached_gdesc_evict,
36     .sync_cached = __cached_gdesc_sync
37 };
38
39 void
40 ext2gd_prepare_gdt(struct v_superblock* vsb)
41 {
42     struct ext2b_super* sb;
43     unsigned int nr_parts;
44     unsigned int nr_gd_pb, nr_gd;
45     struct ext2_sbinfo* ext2sb;
46
47     ext2sb = EXT2_SB(vsb);
48     sb = ext2sb->raw;
49     
50     nr_gd_pb = ext2sb->block_size / sizeof(struct ext2b_gdesc);
51     nr_gd    = ICEIL(sb->s_blk_cnt, sb->s_blk_per_grp);
52     nr_parts = ICEIL(nr_gd, nr_gd_pb);
53
54     ext2sb->gdt_frag    = (bbuf_t*)vcalloc(sizeof(bbuf_t), nr_parts);
55     ext2sb->nr_gdesc_pb = nr_gd_pb;
56     ext2sb->nr_gdesc    = nr_gd;
57
58     bcache_init_zone(&ext2sb->gd_caches, gdesc_bcache_zone, 
59                 ilog2(64), 0, sizeof(struct ext2b_gdesc), &gdesc_bc_ops);
60
61     llist_init_head(&ext2sb->gds);
62     llist_init_head(&ext2sb->free_grps_blk);
63     llist_init_head(&ext2sb->free_grps_ino);
64 }
65
66 void
67 ext2gd_release_gdt(struct v_superblock* vsb)
68 {
69     unsigned int parts_cnt;
70     struct ext2_sbinfo* ext2sb;
71
72     ext2sb = EXT2_SB(vsb);
73     parts_cnt = ICEIL(ext2sb->nr_gdesc, ext2sb->nr_gdesc_pb);
74     for (size_t i = 0; i < parts_cnt; i++)
75     {
76         if (!ext2sb->gdt_frag[i]) {
77             continue;
78         }
79
80         fsblock_put(ext2sb->gdt_frag[i]);
81         ext2sb->gdt_frag[i] = NULL;
82     }
83 }
84
85 static inline bool
86 __try_load_bitmap(struct v_superblock* vsb, 
87                   struct ext2_gdesc* gd, int type)
88 {
89     struct ext2_sbinfo* ext2sb;
90     struct ext2_bmp* bmp;
91     struct llist_header* flist, *flist_entry;
92     bbuf_t buf;
93     unsigned int blk_id, bmp_blk_id, bmp_size;
94
95     ext2sb = EXT2_SB(vsb);
96
97     if (type == GDESC_INO_SEL) {
98         bmp_blk_id = gd->info->bg_ino_map;
99         bmp_size = ext2sb->raw->s_ino_per_grp;
100         bmp = &gd->ino_bmp;
101     }
102     else if (type == GDESC_BLK_SEL) {
103         bmp_blk_id = gd->info->bg_blk_map;
104         bmp_size = ext2sb->raw->s_blk_per_grp;
105         bmp = &gd->blk_bmp;
106     }
107     else {
108         fail_fs("unknown bitmap type");
109     }
110
111     flist = &ext2sb->free_list_sel[type];
112     flist_entry = &gd->free_list_sel[type];
113
114     blk_id = ext2_datablock(vsb, bmp_blk_id);
115     buf    = fsblock_get(vsb, blk_id);
116     if (blkbuf_errbuf(buf)) {
117         return false;
118     }
119
120     ext2bmp_init(bmp, buf, bmp_size);
121
122     if (ext2bmp_check_free(bmp)) {
123         llist_append(flist, flist_entry);
124     }
125
126     return true;
127 }
128
129 int
130 ext2gd_take(struct v_superblock* vsb, 
131                unsigned int index, struct ext2_gdesc** out)
132 {
133     bbuf_t part, buf;
134     struct ext2_sbinfo* ext2sb;
135     unsigned int blk_id, blk_off;
136
137     ext2sb = EXT2_SB(vsb);
138
139     if (index >= ext2sb->nr_gdesc) {
140         return ENOENT;
141     }
142     
143     bcobj_t cached;
144     if (bcache_tryget(&ext2sb->gd_caches, index, &cached)) {
145         *out = (struct ext2_gdesc*)bcached_data(cached);
146         return 0;
147     }
148
149     blk_id  = index / ext2sb->nr_gdesc_pb;
150     blk_off = index % ext2sb->nr_gdesc_pb;
151     
152     part = ext2sb->gdt_frag[blk_id];
153     if (!part) {
154         blk_id = ext2_datablock(vsb, blk_id + 1);
155         part   = fsblock_get(vsb, blk_id);
156         if (!part) {
157             return EIO;
158         }
159         
160         ext2sb->gdt_frag[blk_id] = part;
161     }
162
163     struct ext2_gdesc* gd;
164     
165     gd = valloc(sizeof(struct ext2_gdesc));
166     *gd = (struct ext2_gdesc) {
167         .info = &block_buffer(part, struct ext2b_gdesc)[blk_off],
168         .buf  = part,
169         .base = index * ext2sb->raw->s_blk_per_grp,
170         .ino_base = index * ext2sb->raw->s_ino_per_grp
171     };
172
173     *out = gd;
174     
175     if (!ext2sb->read_only) {
176         if (!__try_load_bitmap(vsb, gd, GDESC_INO_SEL)) {
177             goto cleanup;
178         }
179
180         if (!__try_load_bitmap(vsb, gd, GDESC_BLK_SEL)) {
181             llist_delete(&gd->free_grps_ino);
182             goto cleanup;
183         }
184     }
185     
186     llist_append(&ext2sb->gds, &gd->groups);
187
188     cached = bcache_put_and_ref(&ext2sb->gd_caches, index, gd);
189     gd->cache_ref = cached;
190     gd->sb = ext2sb;
191
192     return 0;
193
194 cleanup:
195     *out = NULL;
196
197     vfree(gd);
198     return EIO;
199 }
200
201 static void
202 __ext2bmp_update_next_free_cell(struct ext2_bmp* e_bmp)
203 {
204     unsigned int i;
205     unsigned int end;
206
207     i = valid_bmp_slot(e_bmp->next_free) ? e_bmp->next_free : 0;
208     end = i;
209     
210     // next fit, try to maximize our locality without going after
211     //  some crazy algorithm
212     do {
213         if (e_bmp->bmp[i] != 0xff) {
214             e_bmp->next_free = i;
215             return;
216         }
217
218         if (++i == e_bmp->nr_bytes) {
219             i = 0;
220         }
221     } 
222     while (i != end);
223
224     e_bmp->next_free = ALLOC_FAIL;
225 }
226
227 void
228 ext2bmp_init(struct ext2_bmp* e_bmp, bbuf_t bmp_buf, unsigned int nr_bits)
229 {
230     assert(nr_bits % 8 == 0);
231
232     e_bmp->bmp = blkbuf_data(bmp_buf);
233     e_bmp->raw = bmp_buf;
234     e_bmp->nr_bytes = nr_bits / 8;
235     
236     __ext2bmp_update_next_free_cell(e_bmp);
237 }
238
239 bool
240 ext2bmp_check_free(struct ext2_bmp* e_bmp)
241 {
242     assert(e_bmp->raw);
243
244     return valid_bmp_slot(e_bmp->next_free);
245 }
246
247 int
248 ext2bmp_alloc_one(struct ext2_bmp* e_bmp)
249 {
250     assert(e_bmp->raw);
251     
252     u8_t cell;
253     int slot, next_free;
254
255     if (!valid_bmp_slot(e_bmp->next_free)) {
256         return ALLOC_FAIL;
257     }
258     
259     slot = 0;
260     next_free = e_bmp->next_free;
261     cell = e_bmp->bmp[next_free];
262     assert(cell != 0xff);
263
264     while ((cell & (1 << slot++)));
265
266     cell |= (1 << --slot);
267     slot += (next_free * 8);
268     e_bmp->bmp[next_free] = cell;
269
270     if (cell == 0xff) {
271         __ext2bmp_update_next_free_cell(e_bmp);
272     }
273
274     fsblock_dirty(e_bmp->raw);
275     return slot;
276 }
277
278 void
279 ext2bmp_free_one(struct ext2_bmp* e_bmp, unsigned int pos)
280 {
281     assert(e_bmp->raw);
282
283     int cell_idx = pos / 8;
284     u8_t cell_mask = 1 << (pos % 8);
285     e_bmp->bmp[cell_idx] &= ~cell_mask;
286
287     if (!valid_bmp_slot(e_bmp->next_free)) {
288         e_bmp->next_free = cell_idx;
289     }
290
291     fsblock_dirty(e_bmp->raw);
292 }
293
294 void
295 ext2bmp_discard(struct ext2_bmp* e_bmp)
296 {
297     assert(e_bmp->raw);
298
299     fsblock_put(e_bmp->raw);
300     e_bmp->raw = NULL;
301 }