aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYongqiang Yang <xiaoqiangnk@gmail.com>2012-01-04 17:09:44 -0500
committerTheodore Ts'o <tytso@mit.edu>2012-01-04 17:09:44 -0500
commit19c5246d251640ac76daa4d34165af78c64b1454 (patch)
treecdf4f2250ca6b61d4910a3279d4d991486631d30
parent4bac1f8cef7bfd2c62793f75aba66a5b8357dede (diff)
ext4: add new online resize interface
This patch adds new online resize interface, whose input argument is a 64-bit integer indicating how many blocks there are in the resized fs. In new resize impelmentation, all work like allocating group tables are done by kernel side, so the new resize interface can support flex_bg feature and prepares ground for suppoting resize with features like bigalloc and exclude bitmap. Besides these, user-space tools just passes in the new number of blocks. We delay initializing the bitmaps and inode tables of added groups if possible and add multi groups (a flex groups) each time, so new resize is very fast like mkfs. Signed-off-by: Yongqiang Yang <xiaoqiangnk@gmail.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r--Documentation/filesystems/ext4.txt7
-rw-r--r--fs/ext4/ext4.h2
-rw-r--r--fs/ext4/ioctl.c57
-rw-r--r--fs/ext4/resize.c177
4 files changed, 243 insertions, 0 deletions
diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt
index 4917cf24a5e0..10ec4639f152 100644
--- a/Documentation/filesystems/ext4.txt
+++ b/Documentation/filesystems/ext4.txt
@@ -581,6 +581,13 @@ Table of Ext4 specific ioctls
581 behaviour may change in the future as it is 581 behaviour may change in the future as it is
582 not necessary and has been done this way only 582 not necessary and has been done this way only
583 for sake of simplicity. 583 for sake of simplicity.
584
585 EXT4_IOC_RESIZE_FS Resize the filesystem to a new size. The number
586 of blocks of resized filesystem is passed in via
587 64 bit integer argument. The kernel allocates
588 bitmaps and inode table, the userspace tool thus
589 just passes the new number of blocks.
590
584.............................................................................. 591..............................................................................
585 592
586References 593References
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 05058e2b7f4f..4bc0e82a9054 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -583,6 +583,7 @@ enum {
583 /* note ioctl 11 reserved for filesystem-independent FIEMAP ioctl */ 583 /* note ioctl 11 reserved for filesystem-independent FIEMAP ioctl */
584#define EXT4_IOC_ALLOC_DA_BLKS _IO('f', 12) 584#define EXT4_IOC_ALLOC_DA_BLKS _IO('f', 12)
585#define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent) 585#define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent)
586#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
586 587
587#if defined(__KERNEL__) && defined(CONFIG_COMPAT) 588#if defined(__KERNEL__) && defined(CONFIG_COMPAT)
588/* 589/*
@@ -1929,6 +1930,7 @@ extern int ext4_group_add(struct super_block *sb,
1929extern int ext4_group_extend(struct super_block *sb, 1930extern int ext4_group_extend(struct super_block *sb,
1930 struct ext4_super_block *es, 1931 struct ext4_super_block *es,
1931 ext4_fsblk_t n_blocks_count); 1932 ext4_fsblk_t n_blocks_count);
1933extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);
1932 1934
1933/* super.c */ 1935/* super.c */
1934extern void *ext4_kvmalloc(size_t size, gfp_t flags); 1936extern void *ext4_kvmalloc(size_t size, gfp_t flags);
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index ff1aab7cd6e8..c1a98804a383 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -18,6 +18,8 @@
18#include "ext4_jbd2.h" 18#include "ext4_jbd2.h"
19#include "ext4.h" 19#include "ext4.h"
20 20
21#define MAX_32_NUM ((((unsigned long long) 1) << 32) - 1)
22
21long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 23long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
22{ 24{
23 struct inode *inode = filp->f_dentry->d_inode; 25 struct inode *inode = filp->f_dentry->d_inode;
@@ -329,6 +331,60 @@ mext_out:
329 return err; 331 return err;
330 } 332 }
331 333
334 case EXT4_IOC_RESIZE_FS: {
335 ext4_fsblk_t n_blocks_count;
336 struct super_block *sb = inode->i_sb;
337 int err = 0, err2 = 0;
338
339 if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
340 EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
341 ext4_msg(sb, KERN_ERR,
342 "Online resizing not (yet) supported with bigalloc");
343 return -EOPNOTSUPP;
344 }
345
346 if (EXT4_HAS_INCOMPAT_FEATURE(sb,
347 EXT4_FEATURE_INCOMPAT_META_BG)) {
348 ext4_msg(sb, KERN_ERR,
349 "Online resizing not (yet) supported with meta_bg");
350 return -EOPNOTSUPP;
351 }
352
353 if (copy_from_user(&n_blocks_count, (__u64 __user *)arg,
354 sizeof(__u64))) {
355 return -EFAULT;
356 }
357
358 if (n_blocks_count > MAX_32_NUM &&
359 !EXT4_HAS_INCOMPAT_FEATURE(sb,
360 EXT4_FEATURE_INCOMPAT_64BIT)) {
361 ext4_msg(sb, KERN_ERR,
362 "File system only supports 32-bit block numbers");
363 return -EOPNOTSUPP;
364 }
365
366 err = ext4_resize_begin(sb);
367 if (err)
368 return err;
369
370 err = mnt_want_write(filp->f_path.mnt);
371 if (err)
372 goto resizefs_out;
373
374 err = ext4_resize_fs(sb, n_blocks_count);
375 if (EXT4_SB(sb)->s_journal) {
376 jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
377 err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
378 jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
379 }
380 if (err == 0)
381 err = err2;
382 mnt_drop_write(filp->f_path.mnt);
383resizefs_out:
384 ext4_resize_end(sb);
385 return err;
386 }
387
332 case FITRIM: 388 case FITRIM:
333 { 389 {
334 struct request_queue *q = bdev_get_queue(sb->s_bdev); 390 struct request_queue *q = bdev_get_queue(sb->s_bdev);
@@ -427,6 +483,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
427 } 483 }
428 case EXT4_IOC_MOVE_EXT: 484 case EXT4_IOC_MOVE_EXT:
429 case FITRIM: 485 case FITRIM:
486 case EXT4_IOC_RESIZE_FS:
430 break; 487 break;
431 default: 488 default:
432 return -ENOIOCTLCMD; 489 return -ENOIOCTLCMD;
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
index dac23561f3eb..5fe2a013ee65 100644
--- a/fs/ext4/resize.c
+++ b/fs/ext4/resize.c
@@ -1430,6 +1430,70 @@ exit:
1430 return err; 1430 return err;
1431} 1431}
1432 1432
1433static int ext4_setup_next_flex_gd(struct super_block *sb,
1434 struct ext4_new_flex_group_data *flex_gd,
1435 ext4_fsblk_t n_blocks_count,
1436 unsigned long flexbg_size)
1437{
1438 struct ext4_super_block *es = EXT4_SB(sb)->s_es;
1439 struct ext4_new_group_data *group_data = flex_gd->groups;
1440 ext4_fsblk_t o_blocks_count;
1441 ext4_group_t n_group;
1442 ext4_group_t group;
1443 ext4_group_t last_group;
1444 ext4_grpblk_t last;
1445 ext4_grpblk_t blocks_per_group;
1446 unsigned long i;
1447
1448 blocks_per_group = EXT4_BLOCKS_PER_GROUP(sb);
1449
1450 o_blocks_count = ext4_blocks_count(es);
1451
1452 if (o_blocks_count == n_blocks_count)
1453 return 0;
1454
1455 ext4_get_group_no_and_offset(sb, o_blocks_count, &group, &last);
1456 BUG_ON(last);
1457 ext4_get_group_no_and_offset(sb, n_blocks_count - 1, &n_group, &last);
1458
1459 last_group = group | (flexbg_size - 1);
1460 if (last_group > n_group)
1461 last_group = n_group;
1462
1463 flex_gd->count = last_group - group + 1;
1464
1465 for (i = 0; i < flex_gd->count; i++) {
1466 int overhead;
1467
1468 group_data[i].group = group + i;
1469 group_data[i].blocks_count = blocks_per_group;
1470 overhead = ext4_bg_has_super(sb, group + i) ?
1471 (1 + ext4_bg_num_gdb(sb, group + i) +
1472 le16_to_cpu(es->s_reserved_gdt_blocks)) : 0;
1473 group_data[i].free_blocks_count = blocks_per_group - overhead;
1474 if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
1475 EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
1476 flex_gd->bg_flags[i] = EXT4_BG_BLOCK_UNINIT |
1477 EXT4_BG_INODE_UNINIT;
1478 else
1479 flex_gd->bg_flags[i] = EXT4_BG_INODE_ZEROED;
1480 }
1481
1482 if (last_group == n_group &&
1483 EXT4_HAS_RO_COMPAT_FEATURE(sb,
1484 EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
1485 /* We need to initialize block bitmap of last group. */
1486 flex_gd->bg_flags[i - 1] &= ~EXT4_BG_BLOCK_UNINIT;
1487
1488 if ((last_group == n_group) && (last != blocks_per_group - 1)) {
1489 group_data[i - 1].blocks_count = last + 1;
1490 group_data[i - 1].free_blocks_count -= blocks_per_group-
1491 last - 1;
1492 }
1493
1494 return 1;
1495}
1496
1433/* Add group descriptor data to an existing or new group descriptor block. 1497/* Add group descriptor data to an existing or new group descriptor block.
1434 * Ensure we handle all possible error conditions _before_ we start modifying 1498 * Ensure we handle all possible error conditions _before_ we start modifying
1435 * the filesystem, because we cannot abort the transaction and not have it 1499 * the filesystem, because we cannot abort the transaction and not have it
@@ -1827,3 +1891,116 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es,
1827exit_put: 1891exit_put:
1828 return err; 1892 return err;
1829} /* ext4_group_extend */ 1893} /* ext4_group_extend */
1894
1895/*
1896 * ext4_resize_fs() resizes a fs to new size specified by @n_blocks_count
1897 *
1898 * @sb: super block of the fs to be resized
1899 * @n_blocks_count: the number of blocks resides in the resized fs
1900 */
1901int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
1902{
1903 struct ext4_new_flex_group_data *flex_gd = NULL;
1904 struct ext4_sb_info *sbi = EXT4_SB(sb);
1905 struct ext4_super_block *es = sbi->s_es;
1906 struct buffer_head *bh;
1907 struct inode *resize_inode;
1908 ext4_fsblk_t o_blocks_count;
1909 ext4_group_t o_group;
1910 ext4_group_t n_group;
1911 ext4_grpblk_t offset;
1912 unsigned long n_desc_blocks;
1913 unsigned long o_desc_blocks;
1914 unsigned long desc_blocks;
1915 int err = 0, flexbg_size = 1;
1916
1917 o_blocks_count = ext4_blocks_count(es);
1918
1919 if (test_opt(sb, DEBUG))
1920 printk(KERN_DEBUG "EXT4-fs: resizing filesystem from %llu "
1921 "upto %llu blocks\n", o_blocks_count, n_blocks_count);
1922
1923 if (n_blocks_count < o_blocks_count) {
1924 /* On-line shrinking not supported */
1925 ext4_warning(sb, "can't shrink FS - resize aborted");
1926 return -EINVAL;
1927 }
1928
1929 if (n_blocks_count == o_blocks_count)
1930 /* Nothing need to do */
1931 return 0;
1932
1933 ext4_get_group_no_and_offset(sb, n_blocks_count - 1, &n_group, &offset);
1934 ext4_get_group_no_and_offset(sb, o_blocks_count, &o_group, &offset);
1935
1936 n_desc_blocks = (n_group + EXT4_DESC_PER_BLOCK(sb)) /
1937 EXT4_DESC_PER_BLOCK(sb);
1938 o_desc_blocks = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /
1939 EXT4_DESC_PER_BLOCK(sb);
1940 desc_blocks = n_desc_blocks - o_desc_blocks;
1941
1942 if (desc_blocks &&
1943 (!EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_RESIZE_INODE) ||
1944 le16_to_cpu(es->s_reserved_gdt_blocks) < desc_blocks)) {
1945 ext4_warning(sb, "No reserved GDT blocks, can't resize");
1946 return -EPERM;
1947 }
1948
1949 resize_inode = ext4_iget(sb, EXT4_RESIZE_INO);
1950 if (IS_ERR(resize_inode)) {
1951 ext4_warning(sb, "Error opening resize inode");
1952 return PTR_ERR(resize_inode);
1953 }
1954
1955 /* See if the device is actually as big as what was requested */
1956 bh = sb_bread(sb, n_blocks_count - 1);
1957 if (!bh) {
1958 ext4_warning(sb, "can't read last block, resize aborted");
1959 return -ENOSPC;
1960 }
1961 brelse(bh);
1962
1963 if (offset != 0) {
1964 /* extend the last group */
1965 ext4_grpblk_t add;
1966 add = EXT4_BLOCKS_PER_GROUP(sb) - offset;
1967 err = ext4_group_extend_no_check(sb, o_blocks_count, add);
1968 if (err)
1969 goto out;
1970 }
1971
1972 if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG) &&
1973 es->s_log_groups_per_flex)
1974 flexbg_size = 1 << es->s_log_groups_per_flex;
1975
1976 o_blocks_count = ext4_blocks_count(es);
1977 if (o_blocks_count == n_blocks_count)
1978 goto out;
1979
1980 flex_gd = alloc_flex_gd(flexbg_size);
1981 if (flex_gd == NULL) {
1982 err = -ENOMEM;
1983 goto out;
1984 }
1985
1986 /* Add flex groups. Note that a regular group is a
1987 * flex group with 1 group.
1988 */
1989 while (ext4_setup_next_flex_gd(sb, flex_gd, n_blocks_count,
1990 flexbg_size)) {
1991 ext4_alloc_group_tables(sb, flex_gd, flexbg_size);
1992 err = ext4_flex_group_add(sb, resize_inode, flex_gd);
1993 if (unlikely(err))
1994 break;
1995 }
1996
1997out:
1998 if (flex_gd)
1999 free_flex_gd(flex_gd);
2000
2001 iput(resize_inode);
2002 if (test_opt(sb, DEBUG))
2003 printk(KERN_DEBUG "EXT4-fs: resized filesystem from %llu "
2004 "upto %llu blocks\n", o_blocks_count, n_blocks_count);
2005 return err;
2006}