Bigalloc compatibility -- OK, it works now!

master
Vitaliy Filippov 2014-01-11 20:39:05 +00:00
parent 8ad456b992
commit af8311fa63
3 changed files with 56 additions and 27 deletions

20
bmove.c
View File

@ -23,6 +23,7 @@ struct process_block_struct {
char *buf;
int add_dir;
int flags;
blk64_t last, last_new;
};
static int process_block(ext2_filsys fs, blk64_t *block_nr,
@ -42,6 +43,16 @@ static int process_block(ext2_filsys fs, blk64_t *block_nr,
* Let's see if this is one which we need to relocate
*/
if (ext2fs_test_block_bitmap2(pb->reserve, block)) {
if (block & EXT2FS_CLUSTER_MASK(fs)) {
// This means we've already moved the cluster and just need
// to rewrite numbers of a non-first block in a cluster
if (pb->last == block-1) {
*block_nr = pb->last_new+1;
pb->last++;
return BLOCK_CHANGED;
}
return 0;
}
if (blockcnt >= 0 && (pb->ino == EXT2_BAD_INO || pb->ino == EXT2_RESIZE_INO)) {
// We obviously can't move bad blocks; and also the resize inode, because it must be in a predefined location
// But we allow to move extent blocks (blockcnt == -1) and directory index blocks (blockcnt == -2)
@ -58,17 +69,19 @@ static int process_block(ext2_filsys fs, blk64_t *block_nr,
} while (ext2fs_test_block_bitmap2(pb->reserve, block) ||
ext2fs_test_block_bitmap2(pb->alloc_map, block));
retval = io_channel_read_blk64(fs->io, orig, 1, pb->buf);
retval = io_channel_read_blk64(fs->io, orig, EXT2FS_CLUSTER_RATIO(fs), pb->buf);
if (retval) {
pb->error = retval;
return BLOCK_ABORT;
}
retval = io_channel_write_blk64(fs->io, block, 1, pb->buf);
retval = io_channel_write_blk64(fs->io, block, EXT2FS_CLUSTER_RATIO(fs), pb->buf);
if (retval) {
pb->error = retval;
return BLOCK_ABORT;
}
*block_nr = block;
pb->last = orig;
pb->last_new = block;
ext2fs_block_alloc_stats2(fs, orig, -1);
ext2fs_block_alloc_stats2(fs, block, +1);
ret = BLOCK_CHANGED;
@ -109,10 +122,11 @@ errcode_t ext2fs_move_blocks(ext2_filsys fs,
pb.alloc_map = alloc_map ? alloc_map : fs->block_map;
pb.flags = flags;
retval = ext2fs_get_array(4, fs->blocksize, &block_buf);
retval = ext2fs_get_array(3 + EXT2FS_CLUSTER_RATIO(fs), fs->blocksize, &block_buf);
if (retval)
return retval;
pb.buf = block_buf + fs->blocksize * 3;
pb.last = -1;
/*
* If GET_DBLIST is set in the flags field, then we should

View File

@ -3,12 +3,13 @@
# To test shrinking: INODES=8192, then ./realloc-inodes test-clean.img 2048
# To test growing: INODES=2048, then ./realloc-inodes test-clean.img 8192
INODES=2048
FILE=test-ext4-grow.img
FS=ext4
FILE=${FILE:-test-ext4-grow1.img}
FS=${FS:-ext4}
SIZE=${SIZE:-32768}
OPTS=${OPTS:--b 1024 -g 2048 -N 2048}
dd if=/dev/zero of=$FILE bs=1k seek=32767 count=1
/sbin/mkfs.$FS -F -b 1024 -m 0 -g 2048 -N $INODES $FILE
dd if=/dev/zero of=$FILE bs=1k seek=$((SIZE-1)) count=1
/sbin/mkfs.$FS -F -m 0 $OPTS $FILE
mkdir -p dir
mount -o loop $FILE dir
# For block moving test: create a sparse file with many extents

View File

@ -21,7 +21,6 @@
*/
/**
* TODO bigalloc compatibility
* TODO write some tests
*
* The theory isn't that hard:
@ -71,6 +70,7 @@
* - reallocation (both shrink and grow) in case of big flex_bg when inode
* tables don't fit into single block group
* - non-optimal reallocation, i.e. when inode block count doesn't change
* - reallocation on a bigalloc filesystem
*/
#include <stdio.h>
@ -184,7 +184,6 @@ errcode_t shrink_move_inodes(realloc_data *rd)
__u32 new_group;
ext2_ino_t ino, new_ino;
struct ext2_inode *inode = NULL;
ext2fs_read_inode_bitmap(rd->fs);
if (retval)
{
return retval;
@ -268,10 +267,6 @@ errcode_t extend_move_blocks(realloc_data *rd)
{
return retval;
}
if (!rd->fs->block_map)
{
ext2fs_read_block_bitmap(rd->fs);
}
// Mark blocks we want to free as "reserved"
// Don't care about which blocks are already used by inode tables,
// because ext2fs_move_blocks only moves blocks that belong to inodes.
@ -326,17 +321,14 @@ errcode_t change_inode_numbers(realloc_data *rd)
*/
errcode_t change_super_and_bgd(realloc_data *rd)
{
blk64_t blk;
blk64_t blk, end;
dgrp_t grp, n_grp, flex_grp;
__u32 used_ig, used_ibg, i, unus;
errcode_t retval = 0;
int has_gdt_csum = EXT2_HAS_RO_COMPAT_FEATURE(rd->fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
int cl = EXT2FS_CLUSTER_RATIO(rd->fs);
void *buf = NULL;
ext2fs_flush(rd->fs);
if (!rd->fs->block_map)
{
ext2fs_read_block_bitmap(rd->fs);
}
retval = ext2fs_get_mem(EXT2_BLOCK_SIZE(rd->fs->super) * rd->ibg_new * rd->flexbg_size, &buf);
if (retval)
{
@ -345,14 +337,28 @@ errcode_t change_super_and_bgd(realloc_data *rd)
// First mark/unmark inode table blocks (mark and unmark in separate passes)
for (grp = 0; grp < rd->fs->group_desc_count; grp++)
{
for (i = 0, blk = ext2fs_inode_table_loc(rd->fs, grp); i < rd->ibg_old; i++, blk++)
// In case of bigalloc, last cluster of previous group inode table
// may often overlap with the first cluster of next group one
blk = ext2fs_inode_table_loc(rd->fs, grp) & ~(cl-1);
if (!ext2fs_test_block_bitmap2(rd->fs->block_map, blk))
{
blk += cl;
}
end = (ext2fs_inode_table_loc(rd->fs, grp) + rd->ibg_old + (cl-1)) & ~(cl-1);
for (; blk < end; blk += cl)
{
ext2fs_block_alloc_stats2(rd->fs, blk, -1);
}
}
for (grp = 0; grp < rd->fs->group_desc_count; grp++)
{
for (i = 0, blk = rd->new_itable_loc[grp]; i < rd->ibg_new; i++, blk++)
blk = rd->new_itable_loc[grp] & ~(cl-1);
if (grp > 0 && blk == end-cl)
{
blk += cl;
}
end = (rd->new_itable_loc[grp] + rd->ibg_new + (cl-1)) & ~(cl-1);
for (; blk < end; blk += cl)
{
if (ext2fs_test_block_bitmap2(rd->fs->block_map, blk))
{
@ -427,7 +433,6 @@ errcode_t change_super_and_bgd(realloc_data *rd)
unus = 0;
}
ext2fs_bg_itable_unused_set(rd->fs, grp, unus);
ext2fs_bg_flags_clear(rd->fs, grp, EXT2_BG_BLOCK_UNINIT);
ext2fs_group_desc_csum_set(rd->fs, grp);
}
}
@ -488,8 +493,8 @@ errcode_t alloc_itables(realloc_data *rd)
errcode_t retval = 0;
ext2fs_block_bitmap nonmovable = NULL;
dgrp_t grp, flex_grp;
int n_grp, i;
blk64_t blk, end;
int n_grp, i, mod;
blk64_t blk, start, end;
retval = ext2fs_get_mem(sizeof(blk64_t) * rd->fs->group_desc_count, &rd->new_itable_loc);
if (retval)
goto out;
@ -528,15 +533,23 @@ errcode_t alloc_itables(realloc_data *rd)
// TODO We could use a better algorithm that would always try to find
// the biggest free sequence of blocks if it can't allocate all inode
// tables in sequence
blk = ext2fs_group_first_block2(rd->fs, grp);
start = ext2fs_group_first_block2(rd->fs, grp);
end = ext2fs_group_last_block2(rd->fs, grp+n_grp-1);
mod = 0;
for (i = 0; i < n_grp; i++, grp++)
{
retval = ext2fs_get_free_blocks2(rd->fs, blk, end, rd->ibg_new, nonmovable, &blk);
retval = ext2fs_get_free_blocks2(rd->fs, start, end, rd->ibg_new, nonmovable, &blk);
if (retval)
goto out;
if (start == blk)
blk += mod;
rd->new_itable_loc[grp] = blk;
blk += rd->ibg_new;
start = blk + rd->ibg_new;
// Allow adjacent inode tables to share one cluster
// ext2fs_get_free_blocks2 operates on cluster boundaries,
// so we handle the division residue separately
mod = start & EXT2FS_CLUSTER_MASK(rd->fs);
start = start - mod;
}
}
out:
@ -579,6 +592,7 @@ errcode_t do_realloc(realloc_data *rd)
" - there will be wasted space in inode tables. Optimal inode count would be %u.\n",
rd->new_inode_count, rd->ig_new, EXT2_BLOCK_SIZE(rd->fs->super) / EXT2_INODE_SIZE(rd->fs->super), ig_round);
}
ext2fs_read_bitmaps(rd->fs);
// Find where to put the new inode tables
retval = alloc_itables(rd);
if (retval)