Skip to content

Commit 1c6bd71

Browse files
committed
ext4: convert file system to meta_bg if needed during resizing
If we have run out of reserved gdt blocks, then clear the resize_inode feature and enable the meta_bg feature, so that we can continue resizing the file system seamlessly. Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
1 parent 93f9052 commit 1c6bd71

File tree

1 file changed

+133
-17
lines changed

1 file changed

+133
-17
lines changed

fs/ext4/resize.c

Lines changed: 133 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1756,6 +1756,99 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es,
17561756
return err;
17571757
} /* ext4_group_extend */
17581758

1759+
1760+
static int num_desc_blocks(struct super_block *sb, ext4_group_t groups)
1761+
{
1762+
return (groups + EXT4_DESC_PER_BLOCK(sb) - 1) / EXT4_DESC_PER_BLOCK(sb);
1763+
}
1764+
1765+
/*
1766+
* Release the resize inode and drop the resize_inode feature if there
1767+
* are no more reserved gdt blocks, and then convert the file system
1768+
* to enable meta_bg
1769+
*/
1770+
static int ext4_convert_meta_bg(struct super_block *sb, struct inode *inode)
1771+
{
1772+
handle_t *handle;
1773+
struct ext4_sb_info *sbi = EXT4_SB(sb);
1774+
struct ext4_super_block *es = sbi->s_es;
1775+
struct ext4_inode_info *ei = 0;
1776+
ext4_fsblk_t nr;
1777+
int i, ret, err = 0;
1778+
int credits = 1;
1779+
1780+
ext4_msg(sb, KERN_INFO, "Converting file system to meta_bg");
1781+
if (EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_RESIZE_INODE)) {
1782+
if (es->s_reserved_gdt_blocks) {
1783+
ext4_error(sb, "Unexpected non-zero "
1784+
"s_reserved_gdt_blocks");
1785+
return -EPERM;
1786+
}
1787+
if (!inode) {
1788+
ext4_error(sb, "Unexpected NULL resize_inode");
1789+
return -EPERM;
1790+
}
1791+
ei = EXT4_I(inode);
1792+
1793+
/* Do a quick sanity check of the resize inode */
1794+
if (inode->i_blocks != 1 << (inode->i_blkbits - 9))
1795+
goto invalid_resize_inode;
1796+
for (i = 0; i < EXT4_N_BLOCKS; i++) {
1797+
if (i == EXT4_DIND_BLOCK) {
1798+
if (ei->i_data[i])
1799+
continue;
1800+
else
1801+
goto invalid_resize_inode;
1802+
}
1803+
if (ei->i_data[i])
1804+
goto invalid_resize_inode;
1805+
}
1806+
credits += 3; /* block bitmap, bg descriptor, resize inode */
1807+
}
1808+
1809+
handle = ext4_journal_start_sb(sb, credits);
1810+
if (IS_ERR(handle))
1811+
return PTR_ERR(handle);
1812+
1813+
err = ext4_journal_get_write_access(handle, sbi->s_sbh);
1814+
if (err)
1815+
goto errout;
1816+
1817+
EXT4_CLEAR_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_RESIZE_INODE);
1818+
EXT4_SET_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG);
1819+
sbi->s_es->s_first_meta_bg =
1820+
cpu_to_le32(num_desc_blocks(sb, sbi->s_groups_count));
1821+
1822+
err = ext4_handle_dirty_super(handle, sb);
1823+
if (err) {
1824+
ext4_std_error(sb, err);
1825+
goto errout;
1826+
}
1827+
1828+
if (inode) {
1829+
nr = le32_to_cpu(ei->i_data[EXT4_DIND_BLOCK]);
1830+
ext4_free_blocks(handle, inode, NULL, nr, 1,
1831+
EXT4_FREE_BLOCKS_METADATA |
1832+
EXT4_FREE_BLOCKS_FORGET);
1833+
ei->i_data[EXT4_DIND_BLOCK] = 0;
1834+
inode->i_blocks = 0;
1835+
1836+
err = ext4_mark_inode_dirty(handle, inode);
1837+
if (err)
1838+
ext4_std_error(sb, err);
1839+
}
1840+
1841+
errout:
1842+
ret = ext4_journal_stop(handle);
1843+
if (!err)
1844+
err = ret;
1845+
return ret;
1846+
1847+
invalid_resize_inode:
1848+
ext4_error(sb, "corrupted/inconsistent resize inode");
1849+
return -EINVAL;
1850+
}
1851+
17591852
/*
17601853
* ext4_resize_fs() resizes a fs to new size specified by @n_blocks_count
17611854
*
@@ -1772,13 +1865,14 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
17721865
ext4_grpblk_t add, offset;
17731866
unsigned long n_desc_blocks;
17741867
unsigned long o_desc_blocks;
1775-
unsigned long desc_blocks;
17761868
ext4_group_t o_group;
17771869
ext4_group_t n_group;
17781870
ext4_fsblk_t o_blocks_count;
1871+
ext4_fsblk_t n_blocks_count_retry = 0;
17791872
int err = 0, flexbg_size = 1 << sbi->s_log_groups_per_flex;
17801873
int meta_bg;
17811874

1875+
retry:
17821876
o_blocks_count = ext4_blocks_count(es);
17831877

17841878
if (test_opt(sb, DEBUG))
@@ -1798,11 +1892,8 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
17981892
ext4_get_group_no_and_offset(sb, n_blocks_count - 1, &n_group, &offset);
17991893
ext4_get_group_no_and_offset(sb, o_blocks_count - 1, &o_group, &offset);
18001894

1801-
n_desc_blocks = (n_group + EXT4_DESC_PER_BLOCK(sb)) /
1802-
EXT4_DESC_PER_BLOCK(sb);
1803-
o_desc_blocks = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /
1804-
EXT4_DESC_PER_BLOCK(sb);
1805-
desc_blocks = n_desc_blocks - o_desc_blocks;
1895+
n_desc_blocks = num_desc_blocks(sb, n_group + 1);
1896+
o_desc_blocks = num_desc_blocks(sb, sbi->s_groups_count);
18061897

18071898
meta_bg = EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG);
18081899

@@ -1812,20 +1903,37 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
18121903
"simultaneously");
18131904
return -EINVAL;
18141905
}
1815-
if (le16_to_cpu(es->s_reserved_gdt_blocks) < desc_blocks) {
1816-
ext4_warning(sb,
1817-
"No reserved GDT blocks, can't resize");
1818-
return -EPERM;
1906+
if (n_desc_blocks > o_desc_blocks +
1907+
le16_to_cpu(es->s_reserved_gdt_blocks)) {
1908+
n_blocks_count_retry = n_blocks_count;
1909+
n_desc_blocks = o_desc_blocks +
1910+
le16_to_cpu(es->s_reserved_gdt_blocks);
1911+
n_group = n_desc_blocks * EXT4_DESC_PER_BLOCK(sb);
1912+
n_blocks_count = n_group * EXT4_BLOCKS_PER_GROUP(sb);
1913+
n_group--; /* set to last group number */
18191914
}
1820-
resize_inode = ext4_iget(sb, EXT4_RESIZE_INO);
1915+
1916+
if (!resize_inode)
1917+
resize_inode = ext4_iget(sb, EXT4_RESIZE_INO);
18211918
if (IS_ERR(resize_inode)) {
18221919
ext4_warning(sb, "Error opening resize inode");
18231920
return PTR_ERR(resize_inode);
18241921
}
1825-
} else if (!meta_bg) {
1826-
ext4_warning(sb, "File system features do not permit "
1827-
"online resize");
1828-
return -EPERM;
1922+
}
1923+
1924+
if ((!resize_inode && !meta_bg) || n_group == o_group) {
1925+
err = ext4_convert_meta_bg(sb, resize_inode);
1926+
if (err)
1927+
goto out;
1928+
if (resize_inode) {
1929+
iput(resize_inode);
1930+
resize_inode = NULL;
1931+
}
1932+
if (n_blocks_count_retry) {
1933+
n_blocks_count = n_blocks_count_retry;
1934+
n_blocks_count_retry = 0;
1935+
goto retry;
1936+
}
18291937
}
18301938

18311939
/* See if the device is actually as big as what was requested */
@@ -1876,13 +1984,21 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
18761984
break;
18771985
}
18781986

1987+
if (!err && n_blocks_count_retry) {
1988+
n_blocks_count = n_blocks_count_retry;
1989+
n_blocks_count_retry = 0;
1990+
free_flex_gd(flex_gd);
1991+
flex_gd = NULL;
1992+
goto retry;
1993+
}
1994+
18791995
out:
18801996
if (flex_gd)
18811997
free_flex_gd(flex_gd);
18821998
if (resize_inode != NULL)
18831999
iput(resize_inode);
18842000
if (test_opt(sb, DEBUG))
1885-
ext4_msg(sb, KERN_DEBUG, "resized filesystem from %llu "
1886-
"upto %llu blocks", o_blocks_count, n_blocks_count);
2001+
ext4_msg(sb, KERN_DEBUG, "resized filesystem to %llu",
2002+
n_blocks_count);
18872003
return err;
18882004
}

0 commit comments

Comments
 (0)