1 | /* |
2 | * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) |
3 | * |
4 | * HelenOS: |
5 | * Copyright (c) 2012 Martin Sucha |
6 | * Copyright (c) 2012 Frantisek Princ |
7 | * All rights reserved. |
8 | * |
9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions |
11 | * are met: |
12 | * |
13 | * - Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * - Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * - The name of the author may not be used to endorse or promote products |
19 | * derived from this software without specific prior written permission. |
20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
22 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
23 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
24 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
25 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
26 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
30 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | /** @addtogroup lwext4 |
34 | * @{ |
35 | */ |
36 | /** |
37 | * @file ext4_balloc.c |
38 | * @brief Physical block allocator. |
39 | */ |
40 | |
41 | #include <ext4_config.h> |
42 | #include <ext4_types.h> |
43 | #include <ext4_misc.h> |
44 | #include <ext4_errno.h> |
45 | #include <ext4_debug.h> |
46 | |
47 | #include <ext4_trans.h> |
48 | #include <ext4_balloc.h> |
49 | #include <ext4_super.h> |
50 | #include <ext4_crc32.h> |
51 | #include <ext4_block_group.h> |
52 | #include <ext4_fs.h> |
53 | #include <ext4_bitmap.h> |
54 | #include <ext4_inode.h> |
55 | |
56 | /**@brief Compute number of block group from block address. |
57 | * @param s superblock pointer. |
58 | * @param baddr Absolute address of block. |
59 | * @return Block group index |
60 | */ |
61 | uint32_t ext4_balloc_get_bgid_of_block(struct ext4_sblock *s, |
62 | uint64_t baddr) |
63 | { |
64 | if (ext4_get32(s, first_data_block) && baddr) |
65 | baddr--; |
66 | |
67 | return (uint32_t)(baddr / ext4_get32(s, blocks_per_group)); |
68 | } |
69 | |
70 | /**@brief Compute the starting block address of a block group |
71 | * @param s superblock pointer. |
72 | * @param bgid block group index |
73 | * @return Block address |
74 | */ |
75 | uint64_t ext4_balloc_get_block_of_bgid(struct ext4_sblock *s, |
76 | uint32_t bgid) |
77 | { |
78 | uint64_t baddr = 0; |
79 | if (ext4_get32(s, first_data_block)) |
80 | baddr++; |
81 | |
82 | baddr += bgid * ext4_get32(s, blocks_per_group); |
83 | return baddr; |
84 | } |
85 | |
86 | #if CONFIG_META_CSUM_ENABLE |
87 | static uint32_t ext4_balloc_bitmap_csum(struct ext4_sblock *sb, |
88 | void *bitmap) |
89 | { |
90 | uint32_t checksum = 0; |
91 | if (ext4_sb_feature_ro_com(s: sb, EXT4_FRO_COM_METADATA_CSUM)) { |
92 | uint32_t blocks_per_group = ext4_get32(sb, blocks_per_group); |
93 | |
94 | /* First calculate crc32 checksum against fs uuid */ |
95 | checksum = ext4_crc32c(EXT4_CRC32_INIT, buf: sb->uuid, |
96 | size: sizeof(sb->uuid)); |
97 | /* Then calculate crc32 checksum against block_group_desc */ |
98 | checksum = ext4_crc32c(crc: checksum, buf: bitmap, size: blocks_per_group / 8); |
99 | } |
100 | return checksum; |
101 | } |
102 | #else |
103 | #define ext4_balloc_bitmap_csum(...) 0 |
104 | #endif |
105 | |
106 | void ext4_balloc_set_bitmap_csum(struct ext4_sblock *sb, |
107 | struct ext4_bgroup *bg, |
108 | void *bitmap __unused) |
109 | { |
110 | int desc_size = ext4_sb_get_desc_size(s: sb); |
111 | uint32_t checksum = ext4_balloc_bitmap_csum(sb, bitmap); |
112 | uint16_t lo_checksum = to_le16(checksum & 0xFFFF), |
113 | hi_checksum = to_le16(checksum >> 16); |
114 | |
115 | if (!ext4_sb_feature_ro_com(s: sb, EXT4_FRO_COM_METADATA_CSUM)) |
116 | return; |
117 | |
118 | /* See if we need to assign a 32bit checksum */ |
119 | bg->block_bitmap_csum_lo = lo_checksum; |
120 | if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE) |
121 | bg->block_bitmap_csum_hi = hi_checksum; |
122 | |
123 | } |
124 | |
125 | #if CONFIG_META_CSUM_ENABLE |
126 | static bool |
127 | ext4_balloc_verify_bitmap_csum(struct ext4_sblock *sb, |
128 | struct ext4_bgroup *bg, |
129 | void *bitmap __unused) |
130 | { |
131 | int desc_size = ext4_sb_get_desc_size(s: sb); |
132 | uint32_t checksum = ext4_balloc_bitmap_csum(sb, bitmap); |
133 | uint16_t lo_checksum = to_le16(checksum & 0xFFFF), |
134 | hi_checksum = to_le16(checksum >> 16); |
135 | |
136 | if (!ext4_sb_feature_ro_com(s: sb, EXT4_FRO_COM_METADATA_CSUM)) |
137 | return true; |
138 | |
139 | if (bg->block_bitmap_csum_lo != lo_checksum) |
140 | return false; |
141 | |
142 | if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE) |
143 | if (bg->block_bitmap_csum_hi != hi_checksum) |
144 | return false; |
145 | |
146 | return true; |
147 | } |
148 | #else |
149 | #define ext4_balloc_verify_bitmap_csum(...) true |
150 | #endif |
151 | |
152 | int ext4_balloc_free_block(struct ext4_inode_ref *inode_ref, ext4_fsblk_t baddr) |
153 | { |
154 | struct ext4_fs *fs = inode_ref->fs; |
155 | struct ext4_sblock *sb = &fs->sb; |
156 | |
157 | uint32_t bg_id = ext4_balloc_get_bgid_of_block(s: sb, baddr); |
158 | uint32_t index_in_group = ext4_fs_addr_to_idx_bg(s: sb, baddr); |
159 | |
160 | /* Load block group reference */ |
161 | struct ext4_block_group_ref bg_ref; |
162 | int rc = ext4_fs_get_block_group_ref(fs, bgid: bg_id, ref: &bg_ref); |
163 | if (rc != EOK) |
164 | return rc; |
165 | |
166 | struct ext4_bgroup *bg = bg_ref.block_group; |
167 | |
168 | /* Load block with bitmap */ |
169 | ext4_fsblk_t bitmap_block_addr = |
170 | ext4_bg_get_block_bitmap(bg, s: sb); |
171 | |
172 | struct ext4_block bitmap_block; |
173 | |
174 | rc = ext4_trans_block_get(bdev: fs->bdev, b: &bitmap_block, lba: bitmap_block_addr); |
175 | if (rc != EOK) { |
176 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
177 | return rc; |
178 | } |
179 | |
180 | if (!ext4_balloc_verify_bitmap_csum(sb, bg, bitmap: bitmap_block.data)) { |
181 | ext4_dbg(DEBUG_BALLOC, |
182 | DBG_WARN "Bitmap checksum failed." |
183 | "Group: %" PRIu32"\n" , |
184 | bg_ref.index); |
185 | } |
186 | |
187 | /* Modify bitmap */ |
188 | ext4_bmap_bit_clr(bmap: bitmap_block.data, bit: index_in_group); |
189 | ext4_balloc_set_bitmap_csum(sb, bg, bitmap: bitmap_block.data); |
190 | ext4_trans_set_block_dirty(buf: bitmap_block.buf); |
191 | |
192 | /* Release block with bitmap */ |
193 | rc = ext4_block_set(bdev: fs->bdev, b: &bitmap_block); |
194 | if (rc != EOK) { |
195 | /* Error in saving bitmap */ |
196 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
197 | return rc; |
198 | } |
199 | |
200 | uint32_t block_size = ext4_sb_get_block_size(s: sb); |
201 | |
202 | /* Update superblock free blocks count */ |
203 | uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(s: sb); |
204 | sb_free_blocks++; |
205 | ext4_sb_set_free_blocks_cnt(s: sb, cnt: sb_free_blocks); |
206 | |
207 | /* Update inode blocks count */ |
208 | uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode: inode_ref->inode); |
209 | ino_blocks -= block_size / EXT4_INODE_BLOCK_SIZE; |
210 | ext4_inode_set_blocks_count(sb, inode: inode_ref->inode, cnt: ino_blocks); |
211 | inode_ref->dirty = true; |
212 | |
213 | /* Update block group free blocks count */ |
214 | uint32_t free_blocks = ext4_bg_get_free_blocks_count(bg, s: sb); |
215 | free_blocks++; |
216 | ext4_bg_set_free_blocks_count(bg, s: sb, cnt: free_blocks); |
217 | |
218 | bg_ref.dirty = true; |
219 | |
220 | rc = ext4_trans_try_revoke_block(bdev: fs->bdev, lba: baddr); |
221 | if (rc != EOK) { |
222 | bg_ref.dirty = false; |
223 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
224 | return rc; |
225 | } |
226 | ext4_bcache_invalidate_lba(bc: fs->bdev->bc, from: baddr, cnt: 1); |
227 | /* Release block group reference */ |
228 | rc = ext4_fs_put_block_group_ref(ref: &bg_ref); |
229 | |
230 | return rc; |
231 | } |
232 | |
233 | int ext4_balloc_free_blocks(struct ext4_inode_ref *inode_ref, |
234 | ext4_fsblk_t first, uint32_t count) |
235 | { |
236 | int rc = EOK; |
237 | uint32_t blk_cnt = count; |
238 | ext4_fsblk_t start_block = first; |
239 | struct ext4_fs *fs = inode_ref->fs; |
240 | struct ext4_sblock *sb = &fs->sb; |
241 | |
242 | /* Compute indexes */ |
243 | uint32_t bg_first = ext4_balloc_get_bgid_of_block(s: sb, baddr: first); |
244 | |
245 | /* Compute indexes */ |
246 | uint32_t bg_last = ext4_balloc_get_bgid_of_block(s: sb, baddr: first + count - 1); |
247 | |
248 | if (!ext4_sb_feature_incom(s: sb, EXT4_FINCOM_FLEX_BG)) { |
249 | /*It is not possible without flex_bg that blocks are continuous |
250 | * and and last block belongs to other bg.*/ |
251 | if (bg_last != bg_first) { |
252 | ext4_dbg(DEBUG_BALLOC, DBG_WARN "FLEX_BG: disabled & " |
253 | "bg_last: %" PRIu32" bg_first: %" PRIu32"\n" , |
254 | bg_last, bg_first); |
255 | } |
256 | } |
257 | |
258 | /* Load block group reference */ |
259 | struct ext4_block_group_ref bg_ref; |
260 | while (bg_first <= bg_last) { |
261 | |
262 | rc = ext4_fs_get_block_group_ref(fs, bgid: bg_first, ref: &bg_ref); |
263 | if (rc != EOK) |
264 | return rc; |
265 | |
266 | struct ext4_bgroup *bg = bg_ref.block_group; |
267 | |
268 | uint32_t idx_in_bg_first; |
269 | idx_in_bg_first = ext4_fs_addr_to_idx_bg(s: sb, baddr: first); |
270 | |
271 | /* Load block with bitmap */ |
272 | ext4_fsblk_t bitmap_blk = ext4_bg_get_block_bitmap(bg, s: sb); |
273 | |
274 | struct ext4_block blk; |
275 | rc = ext4_trans_block_get(bdev: fs->bdev, b: &blk, lba: bitmap_blk); |
276 | if (rc != EOK) { |
277 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
278 | return rc; |
279 | } |
280 | |
281 | if (!ext4_balloc_verify_bitmap_csum(sb, bg, bitmap: blk.data)) { |
282 | ext4_dbg(DEBUG_BALLOC, |
283 | DBG_WARN "Bitmap checksum failed." |
284 | "Group: %" PRIu32"\n" , |
285 | bg_ref.index); |
286 | } |
287 | uint32_t free_cnt; |
288 | free_cnt = ext4_sb_get_block_size(s: sb) * 8 - idx_in_bg_first; |
289 | |
290 | /*If last block, free only count blocks*/ |
291 | free_cnt = count > free_cnt ? free_cnt : count; |
292 | |
293 | /* Modify bitmap */ |
294 | ext4_bmap_bits_free(bmap: blk.data, sbit: idx_in_bg_first, bcnt: free_cnt); |
295 | ext4_balloc_set_bitmap_csum(sb, bg, bitmap: blk.data); |
296 | ext4_trans_set_block_dirty(buf: blk.buf); |
297 | |
298 | count -= free_cnt; |
299 | first += free_cnt; |
300 | |
301 | /* Release block with bitmap */ |
302 | rc = ext4_block_set(bdev: fs->bdev, b: &blk); |
303 | if (rc != EOK) { |
304 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
305 | return rc; |
306 | } |
307 | |
308 | uint32_t block_size = ext4_sb_get_block_size(s: sb); |
309 | |
310 | /* Update superblock free blocks count */ |
311 | uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(s: sb); |
312 | sb_free_blocks += free_cnt; |
313 | ext4_sb_set_free_blocks_cnt(s: sb, cnt: sb_free_blocks); |
314 | |
315 | /* Update inode blocks count */ |
316 | uint64_t ino_blocks; |
317 | ino_blocks = ext4_inode_get_blocks_count(sb, inode: inode_ref->inode); |
318 | ino_blocks -= free_cnt * (block_size / EXT4_INODE_BLOCK_SIZE); |
319 | ext4_inode_set_blocks_count(sb, inode: inode_ref->inode, cnt: ino_blocks); |
320 | inode_ref->dirty = true; |
321 | |
322 | /* Update block group free blocks count */ |
323 | uint32_t free_blocks; |
324 | free_blocks = ext4_bg_get_free_blocks_count(bg, s: sb); |
325 | free_blocks += free_cnt; |
326 | ext4_bg_set_free_blocks_count(bg, s: sb, cnt: free_blocks); |
327 | bg_ref.dirty = true; |
328 | |
329 | /* Release block group reference */ |
330 | rc = ext4_fs_put_block_group_ref(ref: &bg_ref); |
331 | if (rc != EOK) |
332 | break; |
333 | |
334 | bg_first++; |
335 | } |
336 | |
337 | uint32_t i; |
338 | for (i = 0;i < blk_cnt;i++) { |
339 | rc = ext4_trans_try_revoke_block(bdev: fs->bdev, lba: start_block + i); |
340 | if (rc != EOK) |
341 | return rc; |
342 | |
343 | } |
344 | |
345 | ext4_bcache_invalidate_lba(bc: fs->bdev->bc, from: start_block, cnt: blk_cnt); |
346 | /*All blocks should be released*/ |
347 | ext4_assert(count == 0); |
348 | |
349 | return rc; |
350 | } |
351 | |
352 | int ext4_balloc_alloc_block(struct ext4_inode_ref *inode_ref, |
353 | ext4_fsblk_t goal, |
354 | ext4_fsblk_t *fblock) |
355 | { |
356 | ext4_fsblk_t alloc = 0; |
357 | ext4_fsblk_t bmp_blk_adr; |
358 | uint32_t rel_blk_idx = 0; |
359 | uint64_t free_blocks; |
360 | int r; |
361 | struct ext4_sblock *sb = &inode_ref->fs->sb; |
362 | |
363 | /* Load block group number for goal and relative index */ |
364 | uint32_t bg_id = ext4_balloc_get_bgid_of_block(s: sb, baddr: goal); |
365 | uint32_t idx_in_bg = ext4_fs_addr_to_idx_bg(s: sb, baddr: goal); |
366 | |
367 | struct ext4_block b; |
368 | struct ext4_block_group_ref bg_ref; |
369 | |
370 | /* Load block group reference */ |
371 | r = ext4_fs_get_block_group_ref(fs: inode_ref->fs, bgid: bg_id, ref: &bg_ref); |
372 | if (r != EOK) |
373 | return r; |
374 | |
375 | struct ext4_bgroup *bg = bg_ref.block_group; |
376 | |
377 | free_blocks = ext4_bg_get_free_blocks_count(bg: bg_ref.block_group, s: sb); |
378 | if (free_blocks == 0) { |
379 | /* This group has no free blocks */ |
380 | goto goal_failed; |
381 | } |
382 | |
383 | /* Compute indexes */ |
384 | ext4_fsblk_t first_in_bg; |
385 | first_in_bg = ext4_balloc_get_block_of_bgid(s: sb, bgid: bg_ref.index); |
386 | |
387 | uint32_t first_in_bg_index; |
388 | first_in_bg_index = ext4_fs_addr_to_idx_bg(s: sb, baddr: first_in_bg); |
389 | |
390 | if (idx_in_bg < first_in_bg_index) |
391 | idx_in_bg = first_in_bg_index; |
392 | |
393 | /* Load block with bitmap */ |
394 | bmp_blk_adr = ext4_bg_get_block_bitmap(bg: bg_ref.block_group, s: sb); |
395 | |
396 | r = ext4_trans_block_get(bdev: inode_ref->fs->bdev, b: &b, lba: bmp_blk_adr); |
397 | if (r != EOK) { |
398 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
399 | return r; |
400 | } |
401 | |
402 | if (!ext4_balloc_verify_bitmap_csum(sb, bg, bitmap: b.data)) { |
403 | ext4_dbg(DEBUG_BALLOC, |
404 | DBG_WARN "Bitmap checksum failed." |
405 | "Group: %" PRIu32"\n" , |
406 | bg_ref.index); |
407 | } |
408 | |
409 | /* Check if goal is free */ |
410 | if (ext4_bmap_is_bit_clr(bmap: b.data, bit: idx_in_bg)) { |
411 | ext4_bmap_bit_set(bmap: b.data, bit: idx_in_bg); |
412 | ext4_balloc_set_bitmap_csum(sb, bg: bg_ref.block_group, |
413 | bitmap: b.data); |
414 | ext4_trans_set_block_dirty(buf: b.buf); |
415 | r = ext4_block_set(bdev: inode_ref->fs->bdev, b: &b); |
416 | if (r != EOK) { |
417 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
418 | return r; |
419 | } |
420 | |
421 | alloc = ext4_fs_bg_idx_to_addr(s: sb, index: idx_in_bg, bgid: bg_id); |
422 | goto success; |
423 | } |
424 | |
425 | uint32_t blk_in_bg = ext4_blocks_in_group_cnt(s: sb, bgid: bg_id); |
426 | |
427 | uint32_t end_idx = (idx_in_bg + 63) & ~63; |
428 | if (end_idx > blk_in_bg) |
429 | end_idx = blk_in_bg; |
430 | |
431 | /* Try to find free block near to goal */ |
432 | uint32_t tmp_idx; |
433 | for (tmp_idx = idx_in_bg + 1; tmp_idx < end_idx; ++tmp_idx) { |
434 | if (ext4_bmap_is_bit_clr(bmap: b.data, bit: tmp_idx)) { |
435 | ext4_bmap_bit_set(bmap: b.data, bit: tmp_idx); |
436 | |
437 | ext4_balloc_set_bitmap_csum(sb, bg, bitmap: b.data); |
438 | ext4_trans_set_block_dirty(buf: b.buf); |
439 | r = ext4_block_set(bdev: inode_ref->fs->bdev, b: &b); |
440 | if (r != EOK) { |
441 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
442 | return r; |
443 | } |
444 | |
445 | alloc = ext4_fs_bg_idx_to_addr(s: sb, index: tmp_idx, bgid: bg_id); |
446 | goto success; |
447 | } |
448 | } |
449 | |
450 | /* Find free bit in bitmap */ |
451 | r = ext4_bmap_bit_find_clr(bmap: b.data, sbit: idx_in_bg, ebit: blk_in_bg, bit_id: &rel_blk_idx); |
452 | if (r == EOK) { |
453 | ext4_bmap_bit_set(bmap: b.data, bit: rel_blk_idx); |
454 | ext4_balloc_set_bitmap_csum(sb, bg: bg_ref.block_group, bitmap: b.data); |
455 | ext4_trans_set_block_dirty(buf: b.buf); |
456 | r = ext4_block_set(bdev: inode_ref->fs->bdev, b: &b); |
457 | if (r != EOK) { |
458 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
459 | return r; |
460 | } |
461 | |
462 | alloc = ext4_fs_bg_idx_to_addr(s: sb, index: rel_blk_idx, bgid: bg_id); |
463 | goto success; |
464 | } |
465 | |
466 | /* No free block found yet */ |
467 | r = ext4_block_set(bdev: inode_ref->fs->bdev, b: &b); |
468 | if (r != EOK) { |
469 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
470 | return r; |
471 | } |
472 | |
473 | goal_failed: |
474 | |
475 | r = ext4_fs_put_block_group_ref(ref: &bg_ref); |
476 | if (r != EOK) |
477 | return r; |
478 | |
479 | /* Try other block groups */ |
480 | uint32_t block_group_count = ext4_block_group_cnt(s: sb); |
481 | uint32_t bgid = (bg_id + 1) % block_group_count; |
482 | uint32_t count = block_group_count; |
483 | |
484 | while (count > 0) { |
485 | r = ext4_fs_get_block_group_ref(fs: inode_ref->fs, bgid, ref: &bg_ref); |
486 | if (r != EOK) |
487 | return r; |
488 | |
489 | struct ext4_bgroup *bg = bg_ref.block_group; |
490 | free_blocks = ext4_bg_get_free_blocks_count(bg, s: sb); |
491 | if (free_blocks == 0) { |
492 | /* This group has no free blocks */ |
493 | goto next_group; |
494 | } |
495 | |
496 | /* Load block with bitmap */ |
497 | bmp_blk_adr = ext4_bg_get_block_bitmap(bg, s: sb); |
498 | r = ext4_trans_block_get(bdev: inode_ref->fs->bdev, b: &b, lba: bmp_blk_adr); |
499 | if (r != EOK) { |
500 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
501 | return r; |
502 | } |
503 | |
504 | if (!ext4_balloc_verify_bitmap_csum(sb, bg, bitmap: b.data)) { |
505 | ext4_dbg(DEBUG_BALLOC, |
506 | DBG_WARN "Bitmap checksum failed." |
507 | "Group: %" PRIu32"\n" , |
508 | bg_ref.index); |
509 | } |
510 | |
511 | /* Compute indexes */ |
512 | first_in_bg = ext4_balloc_get_block_of_bgid(s: sb, bgid); |
513 | idx_in_bg = ext4_fs_addr_to_idx_bg(s: sb, baddr: first_in_bg); |
514 | blk_in_bg = ext4_blocks_in_group_cnt(s: sb, bgid); |
515 | first_in_bg_index = ext4_fs_addr_to_idx_bg(s: sb, baddr: first_in_bg); |
516 | |
517 | if (idx_in_bg < first_in_bg_index) |
518 | idx_in_bg = first_in_bg_index; |
519 | |
520 | r = ext4_bmap_bit_find_clr(bmap: b.data, sbit: idx_in_bg, ebit: blk_in_bg, |
521 | bit_id: &rel_blk_idx); |
522 | if (r == EOK) { |
523 | ext4_bmap_bit_set(bmap: b.data, bit: rel_blk_idx); |
524 | ext4_balloc_set_bitmap_csum(sb, bg, bitmap: b.data); |
525 | ext4_trans_set_block_dirty(buf: b.buf); |
526 | r = ext4_block_set(bdev: inode_ref->fs->bdev, b: &b); |
527 | if (r != EOK) { |
528 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
529 | return r; |
530 | } |
531 | |
532 | alloc = ext4_fs_bg_idx_to_addr(s: sb, index: rel_blk_idx, bgid); |
533 | goto success; |
534 | } |
535 | |
536 | r = ext4_block_set(bdev: inode_ref->fs->bdev, b: &b); |
537 | if (r != EOK) { |
538 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
539 | return r; |
540 | } |
541 | |
542 | next_group: |
543 | r = ext4_fs_put_block_group_ref(ref: &bg_ref); |
544 | if (r != EOK) { |
545 | return r; |
546 | } |
547 | |
548 | /* Goto next group */ |
549 | bgid = (bgid + 1) % block_group_count; |
550 | count--; |
551 | } |
552 | |
553 | return ENOSPC; |
554 | |
555 | success: |
556 | /* Empty command - because of syntax */ |
557 | ; |
558 | |
559 | uint32_t block_size = ext4_sb_get_block_size(s: sb); |
560 | |
561 | /* Update superblock free blocks count */ |
562 | uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(s: sb); |
563 | sb_free_blocks--; |
564 | ext4_sb_set_free_blocks_cnt(s: sb, cnt: sb_free_blocks); |
565 | |
566 | /* Update inode blocks (different block size!) count */ |
567 | uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode: inode_ref->inode); |
568 | ino_blocks += block_size / EXT4_INODE_BLOCK_SIZE; |
569 | ext4_inode_set_blocks_count(sb, inode: inode_ref->inode, cnt: ino_blocks); |
570 | inode_ref->dirty = true; |
571 | |
572 | /* Update block group free blocks count */ |
573 | |
574 | uint32_t fb_cnt = ext4_bg_get_free_blocks_count(bg: bg_ref.block_group, s: sb); |
575 | fb_cnt--; |
576 | ext4_bg_set_free_blocks_count(bg: bg_ref.block_group, s: sb, cnt: fb_cnt); |
577 | |
578 | bg_ref.dirty = true; |
579 | r = ext4_fs_put_block_group_ref(ref: &bg_ref); |
580 | |
581 | *fblock = alloc; |
582 | return r; |
583 | } |
584 | |
585 | int ext4_balloc_try_alloc_block(struct ext4_inode_ref *inode_ref, |
586 | ext4_fsblk_t baddr, bool *free) |
587 | { |
588 | int rc; |
589 | |
590 | struct ext4_fs *fs = inode_ref->fs; |
591 | struct ext4_sblock *sb = &fs->sb; |
592 | |
593 | /* Compute indexes */ |
594 | uint32_t block_group = ext4_balloc_get_bgid_of_block(s: sb, baddr); |
595 | uint32_t index_in_group = ext4_fs_addr_to_idx_bg(s: sb, baddr); |
596 | |
597 | /* Load block group reference */ |
598 | struct ext4_block_group_ref bg_ref; |
599 | rc = ext4_fs_get_block_group_ref(fs, bgid: block_group, ref: &bg_ref); |
600 | if (rc != EOK) |
601 | return rc; |
602 | |
603 | /* Load block with bitmap */ |
604 | ext4_fsblk_t bmp_blk_addr; |
605 | bmp_blk_addr = ext4_bg_get_block_bitmap(bg: bg_ref.block_group, s: sb); |
606 | |
607 | struct ext4_block b; |
608 | rc = ext4_trans_block_get(bdev: fs->bdev, b: &b, lba: bmp_blk_addr); |
609 | if (rc != EOK) { |
610 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
611 | return rc; |
612 | } |
613 | |
614 | if (!ext4_balloc_verify_bitmap_csum(sb, bg: bg_ref.block_group, bitmap: b.data)) { |
615 | ext4_dbg(DEBUG_BALLOC, |
616 | DBG_WARN "Bitmap checksum failed." |
617 | "Group: %" PRIu32"\n" , |
618 | bg_ref.index); |
619 | } |
620 | |
621 | /* Check if block is free */ |
622 | *free = ext4_bmap_is_bit_clr(bmap: b.data, bit: index_in_group); |
623 | |
624 | /* Allocate block if possible */ |
625 | if (*free) { |
626 | ext4_bmap_bit_set(bmap: b.data, bit: index_in_group); |
627 | ext4_balloc_set_bitmap_csum(sb, bg: bg_ref.block_group, bitmap: b.data); |
628 | ext4_trans_set_block_dirty(buf: b.buf); |
629 | } |
630 | |
631 | /* Release block with bitmap */ |
632 | rc = ext4_block_set(bdev: fs->bdev, b: &b); |
633 | if (rc != EOK) { |
634 | /* Error in saving bitmap */ |
635 | ext4_fs_put_block_group_ref(ref: &bg_ref); |
636 | return rc; |
637 | } |
638 | |
639 | /* If block is not free, return */ |
640 | if (!(*free)) |
641 | goto terminate; |
642 | |
643 | uint32_t block_size = ext4_sb_get_block_size(s: sb); |
644 | |
645 | /* Update superblock free blocks count */ |
646 | uint64_t sb_free_blocks = ext4_sb_get_free_blocks_cnt(s: sb); |
647 | sb_free_blocks--; |
648 | ext4_sb_set_free_blocks_cnt(s: sb, cnt: sb_free_blocks); |
649 | |
650 | /* Update inode blocks count */ |
651 | uint64_t ino_blocks = ext4_inode_get_blocks_count(sb, inode: inode_ref->inode); |
652 | ino_blocks += block_size / EXT4_INODE_BLOCK_SIZE; |
653 | ext4_inode_set_blocks_count(sb, inode: inode_ref->inode, cnt: ino_blocks); |
654 | inode_ref->dirty = true; |
655 | |
656 | /* Update block group free blocks count */ |
657 | uint32_t fb_cnt = ext4_bg_get_free_blocks_count(bg: bg_ref.block_group, s: sb); |
658 | fb_cnt--; |
659 | ext4_bg_set_free_blocks_count(bg: bg_ref.block_group, s: sb, cnt: fb_cnt); |
660 | |
661 | bg_ref.dirty = true; |
662 | |
663 | terminate: |
664 | return ext4_fs_put_block_group_ref(ref: &bg_ref); |
665 | } |
666 | |
667 | /** |
668 | * @} |
669 | */ |
670 | |