From: Maxim V. Patlasov <mpatla...@parallels.com> This is a port of da0fae4 ext4: Teach the fs where the balloon inode is
This adds the balloon_ino mount option and stores the inode pointer on the in-memory super block object. This is not good solution - in a perfect world the balloon inode should be hidden (like the journalling one), but this requires a) reserve its number in the mainline sources;) b) teach e2fsprogs not to treat one as orphaned Until (if) we do this it's better to keep this as a regular file on the disk. (cherry picked from vz7 commit 54ac06cf671c68a3778e9f939ba3794fd6a51470) Signed-off-by: Konstantin Khorenko <khore...@virtuozzo.com> Signed-off-by: Kirill Tkhai <ktk...@virtuozzo.com> --- fs/ext4/ext4.h | 2 + fs/ext4/super.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 3c51e243450d..9b655a94eb16 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1579,6 +1579,8 @@ struct ext4_sb_info { atomic_t s_mb_discarded; atomic_t s_lock_busy; + struct inode *s_balloon_ino; + /* locality groups */ struct ext4_locality_group __percpu *s_locality_groups; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index befbb0892fdd..3bc2cfb04518 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1682,6 +1682,7 @@ enum { #ifdef CONFIG_EXT4_DEBUG Opt_fc_debug_max_replay, Opt_fc_debug_force #endif + Opt_balloon_ino, }; static const match_table_t tokens = { @@ -1786,6 +1787,7 @@ static const match_table_t tokens = { {Opt_removed, "reservation"}, /* mount option from ext2/3 */ {Opt_removed, "noreservation"}, /* mount option from ext2/3 */ {Opt_removed, "journal=%u"}, /* mount option from ext2/3 */ + {Opt_balloon_ino, "balloon_ino=%u"}, {Opt_err, NULL}, }; @@ -2009,6 +2011,7 @@ static const struct mount_opts { MOPT_SET | MOPT_2 | MOPT_EXT4_ONLY}, {Opt_fc_debug_max_replay, 0, MOPT_GTE0}, #endif + {Opt_balloon_ino, 0, 0}, {Opt_err, 0, 0} }; @@ -2093,7 +2096,8 @@ struct ext4_parsed_options { static int handle_mount_opt(struct super_block *sb, char *opt, int token, substring_t *args, struct ext4_parsed_options *parsed_opts, - int is_remount) + + unsigned long *balloon_ino, int is_remount) { struct ext4_sb_info *sbi = EXT4_SB(sb); const struct mount_opts *m; @@ -2300,6 +2304,8 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, } else if (token == Opt_test_dummy_encryption) { return ext4_set_test_dummy_encryption(sb, opt, &args[0], is_remount); + } else if (token == Opt_balloon_ino) { + *balloon_ino = arg; } else if (m->flags & MOPT_DATAJ) { if (is_remount) { if (!sbi->s_journal) @@ -2420,6 +2426,7 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, static int parse_options(char *options, struct super_block *sb, struct ext4_parsed_options *ret_opts, + unsigned long *balloon_ino, int is_remount) { struct ext4_sb_info __maybe_unused *sbi = EXT4_SB(sb); @@ -2440,7 +2447,7 @@ static int parse_options(char *options, struct super_block *sb, args[0].to = args[0].from = NULL; token = match_token(p, tokens, args); if (handle_mount_opt(sb, p, token, args, ret_opts, - is_remount) < 0) + balloon_ino, is_remount) < 0) return 0; } #ifdef CONFIG_QUOTA @@ -2628,6 +2635,10 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb, } else if (test_opt2(sb, DAX_INODE)) { SEQ_OPTS_PUTS("dax=inode"); } + + if (sbi->s_balloon_ino) + SEQ_OPTS_PRINT("balloon_ino=%ld", sbi->s_balloon_ino->i_ino); + ext4_show_quota_options(seq, sb); return 0; } @@ -4014,6 +4025,54 @@ static const char *ext4_quota_mode(struct super_block *sb) #endif } +static void ext4_load_balloon(struct super_block *sb, unsigned long ino) +{ + struct inode *inode; + struct ext4_sb_info *sbi; + + sbi = EXT4_SB(sb); + + if (!ino) { + /* FIXME locking */ + if (sbi->s_balloon_ino) { + iput(sbi->s_balloon_ino); + sbi->s_balloon_ino = NULL; + } + + return; + } + + if (ino < EXT4_FIRST_INO(sb)) { + ext4_msg(sb, KERN_WARNING, "bad balloon inode specified"); + return; + } + + inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL); + if (IS_ERR(inode)) { + ext4_msg(sb, KERN_WARNING, "can't load balloon inode (%ld)", PTR_ERR(inode)); + return; + } + + if (!S_ISREG(inode->i_mode)) { + iput(inode); + ext4_msg(sb, KERN_WARNING, "balloon should be regular"); + return; + } + + if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)) { + iput(inode); + ext4_msg(sb, KERN_WARNING, "balloon should support extents"); + return; + } + + /* FIXME - locking */ + if (sbi->s_balloon_ino) + iput(sbi->s_balloon_ino); + sbi->s_balloon_ino = inode; + ext4_msg(sb, KERN_INFO, "loaded balloon from %ld (%llu blocks)", + inode->i_ino, inode->i_blocks); +} + static int ext4_fill_super(struct super_block *sb, void *data, int silent) { struct dax_device *dax_dev = fs_dax_get_by_bdev(sb->s_bdev); @@ -4036,6 +4095,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) int needs_recovery, has_huge_files; __u64 blocks_count; int err = 0; + unsigned long balloon_ino = 0; ext4_group_t first_not_zeroed; struct ext4_parsed_options parsed_opts; @@ -4288,7 +4348,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) GFP_KERNEL); if (!s_mount_opts) goto failed_mount; - if (!parse_options(s_mount_opts, sb, &parsed_opts, 0)) { + if (!parse_options(s_mount_opts, sb, &parsed_opts, + &balloon_ino, 0)) { ext4_msg(sb, KERN_WARNING, "failed to parse options in superblock: %s", s_mount_opts); @@ -4296,7 +4357,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) kfree(s_mount_opts); } sbi->s_def_mount_opt = sbi->s_mount_opt; - if (!parse_options((char *) data, sb, &parsed_opts, 0)) + if (!parse_options((char *) data, sb, &parsed_opts, + &balloon_ino, 0)) goto failed_mount; #ifdef CONFIG_UNICODE @@ -5115,6 +5177,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) "the device does not support discard"); } + ext4_load_balloon(sb, balloon_ino); + if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount")) ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. " "Opts: %.*s%s%s. Quota mode: %s.", descr, @@ -5854,6 +5918,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) #endif char *orig_data = kstrdup(data, GFP_KERNEL); struct ext4_parsed_options parsed_opts; + unsigned long balloon_ino = -1; parsed_opts.journal_ioprio = DEFAULT_JOURNAL_IOPRIO; parsed_opts.journal_devnum = 0; @@ -5898,7 +5963,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) vfs_flags = SB_LAZYTIME | SB_I_VERSION; sb->s_flags = (sb->s_flags & ~vfs_flags) | (*flags & vfs_flags); - if (!parse_options(data, sb, &parsed_opts, 1)) { + if (!parse_options(data, sb, &parsed_opts, &balloon_ino, 1)) { err = -EINVAL; goto restore_opts; } @@ -6086,6 +6151,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) goto restore_opts; } + if (balloon_ino != -1) + ext4_load_balloon(sb, balloon_ino); + #ifdef CONFIG_QUOTA /* Release old quota file names */ for (i = 0; i < EXT4_MAXQUOTAS; i++) @@ -6699,11 +6767,22 @@ static inline int ext3_feature_set_ok(struct super_block *sb) return 1; } +static void ext4_kill_sb(struct super_block *sb) +{ + struct ext4_sb_info *sbi; + + sbi = EXT4_SB(sb); + if (sbi && sbi->s_balloon_ino) + iput(sbi->s_balloon_ino); + + kill_block_super(sb); +} + static struct file_system_type ext4_fs_type = { .owner = THIS_MODULE, .name = "ext4", .mount = ext4_mount, - .kill_sb = kill_block_super, + .kill_sb = ext4_kill_sb, .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP | FS_VIRTUALIZED, }; MODULE_ALIAS_FS("ext4"); _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel