diff options
author | Theodore Ts'o <tytso@mit.edu> | 2012-09-13 10:19:24 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2012-09-13 10:19:24 -0400 |
commit | 1c6bd7173d66b3dfdefcedb38cabc1fb03997509 (patch) | |
tree | 676479aeed00d4c63e1a29ef96df90b06d17f199 | |
parent | 93f9052643409c13b3b5f76833865087351f55b8 (diff) |
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>
-rw-r--r-- | fs/ext4/resize.c | 150 |
1 files changed, 133 insertions, 17 deletions
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index a5be589c85b..5932ab5ca53 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c | |||
@@ -1756,6 +1756,99 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es, | |||
1756 | return err; | 1756 | return err; |
1757 | } /* ext4_group_extend */ | 1757 | } /* ext4_group_extend */ |
1758 | 1758 | ||
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 | |||
1759 | /* | 1852 | /* |
1760 | * ext4_resize_fs() resizes a fs to new size specified by @n_blocks_count | 1853 | * ext4_resize_fs() resizes a fs to new size specified by @n_blocks_count |
1761 | * | 1854 | * |
@@ -1772,13 +1865,14 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count) | |||
1772 | ext4_grpblk_t add, offset; | 1865 | ext4_grpblk_t add, offset; |
1773 | unsigned long n_desc_blocks; | 1866 | unsigned long n_desc_blocks; |
1774 | unsigned long o_desc_blocks; | 1867 | unsigned long o_desc_blocks; |
1775 | unsigned long desc_blocks; | ||
1776 | ext4_group_t o_group; | 1868 | ext4_group_t o_group; |
1777 | ext4_group_t n_group; | 1869 | ext4_group_t n_group; |
1778 | ext4_fsblk_t o_blocks_count; | 1870 | ext4_fsblk_t o_blocks_count; |
1871 | ext4_fsblk_t n_blocks_count_retry = 0; | ||
1779 | int err = 0, flexbg_size = 1 << sbi->s_log_groups_per_flex; | 1872 | int err = 0, flexbg_size = 1 << sbi->s_log_groups_per_flex; |
1780 | int meta_bg; | 1873 | int meta_bg; |
1781 | 1874 | ||
1875 | retry: | ||
1782 | o_blocks_count = ext4_blocks_count(es); | 1876 | o_blocks_count = ext4_blocks_count(es); |
1783 | 1877 | ||
1784 | if (test_opt(sb, DEBUG)) | 1878 | if (test_opt(sb, DEBUG)) |
@@ -1798,11 +1892,8 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count) | |||
1798 | ext4_get_group_no_and_offset(sb, n_blocks_count - 1, &n_group, &offset); | 1892 | ext4_get_group_no_and_offset(sb, n_blocks_count - 1, &n_group, &offset); |
1799 | ext4_get_group_no_and_offset(sb, o_blocks_count - 1, &o_group, &offset); | 1893 | ext4_get_group_no_and_offset(sb, o_blocks_count - 1, &o_group, &offset); |
1800 | 1894 | ||
1801 | n_desc_blocks = (n_group + EXT4_DESC_PER_BLOCK(sb)) / | 1895 | n_desc_blocks = num_desc_blocks(sb, n_group + 1); |
1802 | EXT4_DESC_PER_BLOCK(sb); | 1896 | o_desc_blocks = num_desc_blocks(sb, sbi->s_groups_count); |
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; | ||
1806 | 1897 | ||
1807 | meta_bg = EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG); | 1898 | meta_bg = EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG); |
1808 | 1899 | ||
@@ -1812,20 +1903,37 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count) | |||
1812 | "simultaneously"); | 1903 | "simultaneously"); |
1813 | return -EINVAL; | 1904 | return -EINVAL; |
1814 | } | 1905 | } |
1815 | if (le16_to_cpu(es->s_reserved_gdt_blocks) < desc_blocks) { | 1906 | if (n_desc_blocks > o_desc_blocks + |
1816 | ext4_warning(sb, | 1907 | le16_to_cpu(es->s_reserved_gdt_blocks)) { |
1817 | "No reserved GDT blocks, can't resize"); | 1908 | n_blocks_count_retry = n_blocks_count; |
1818 | return -EPERM; | 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 */ | ||
1819 | } | 1914 | } |
1820 | resize_inode = ext4_iget(sb, EXT4_RESIZE_INO); | 1915 | |
1916 | if (!resize_inode) | ||
1917 | resize_inode = ext4_iget(sb, EXT4_RESIZE_INO); | ||
1821 | if (IS_ERR(resize_inode)) { | 1918 | if (IS_ERR(resize_inode)) { |
1822 | ext4_warning(sb, "Error opening resize inode"); | 1919 | ext4_warning(sb, "Error opening resize inode"); |
1823 | return PTR_ERR(resize_inode); | 1920 | return PTR_ERR(resize_inode); |
1824 | } | 1921 | } |
1825 | } else if (!meta_bg) { | 1922 | } |
1826 | ext4_warning(sb, "File system features do not permit " | 1923 | |
1827 | "online resize"); | 1924 | if ((!resize_inode && !meta_bg) || n_group == o_group) { |
1828 | return -EPERM; | 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 | } | ||
1829 | } | 1937 | } |
1830 | 1938 | ||
1831 | /* See if the device is actually as big as what was requested */ | 1939 | /* 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) | |||
1876 | break; | 1984 | break; |
1877 | } | 1985 | } |
1878 | 1986 | ||
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 | |||
1879 | out: | 1995 | out: |
1880 | if (flex_gd) | 1996 | if (flex_gd) |
1881 | free_flex_gd(flex_gd); | 1997 | free_flex_gd(flex_gd); |
1882 | if (resize_inode != NULL) | 1998 | if (resize_inode != NULL) |
1883 | iput(resize_inode); | 1999 | iput(resize_inode); |
1884 | if (test_opt(sb, DEBUG)) | 2000 | if (test_opt(sb, DEBUG)) |
1885 | ext4_msg(sb, KERN_DEBUG, "resized filesystem from %llu " | 2001 | ext4_msg(sb, KERN_DEBUG, "resized filesystem to %llu", |
1886 | "upto %llu blocks", o_blocks_count, n_blocks_count); | 2002 | n_blocks_count); |
1887 | return err; | 2003 | return err; |
1888 | } | 2004 | } |