ext4: grow the s_flex_groups array as needed when resizing
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / fs / ext4 / super.c
index 2d51cd9af22559394e1aa9ce303922dd64ccc083..b8de488889d6b256d82d18c6f057253d4158e6c9 100644 (file)
@@ -331,33 +331,17 @@ static void ext4_put_nojournal(handle_t *handle)
  * journal_end calls result in the superblock being marked dirty, so
  * that sync() will call the filesystem's write_super callback if
  * appropriate.
- *
- * To avoid j_barrier hold in userspace when a user calls freeze(),
- * ext4 prevents a new handle from being started by s_frozen, which
- * is in an upper layer.
  */
 handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
 {
        journal_t *journal;
-       handle_t  *handle;
 
        trace_ext4_journal_start(sb, nblocks, _RET_IP_);
        if (sb->s_flags & MS_RDONLY)
                return ERR_PTR(-EROFS);
 
+       WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE);
        journal = EXT4_SB(sb)->s_journal;
-       handle = ext4_journal_current_handle();
-
-       /*
-        * If a handle has been started, it should be allowed to
-        * finish, otherwise deadlock could happen between freeze
-        * and others(e.g. truncate) due to the restart of the
-        * journal handle if the filesystem is forzen and active
-        * handles are not stopped.
-        */
-       if (!handle)
-               vfs_check_frozen(sb, SB_FREEZE_TRANS);
-
        if (!journal)
                return ext4_get_nojournal();
        /*
@@ -877,7 +861,6 @@ static void ext4_put_super(struct super_block *sb)
        flush_workqueue(sbi->dio_unwritten_wq);
        destroy_workqueue(sbi->dio_unwritten_wq);
 
-       lock_super(sb);
        if (sbi->s_journal) {
                err = jbd2_journal_destroy(sbi->s_journal);
                sbi->s_journal = NULL;
@@ -944,7 +927,6 @@ static void ext4_put_super(struct super_block *sb)
         * Now that we are completely done shutting down the
         * superblock, we need to actually destroy the kobject.
         */
-       unlock_super(sb);
        kobject_put(&sbi->s_kobj);
        wait_for_completion(&sbi->s_kobj_unregister);
        if (sbi->s_chksum_driver)
@@ -975,6 +957,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
        ei->i_reserved_meta_blocks = 0;
        ei->i_allocated_meta_blocks = 0;
        ei->i_da_metadata_calc_len = 0;
+       ei->i_da_metadata_calc_last_lblock = 0;
        spin_lock_init(&(ei->i_block_reservation_lock));
 #ifdef CONFIG_QUOTA
        ei->i_reserved_quota = 0;
@@ -1245,6 +1228,7 @@ enum {
        Opt_inode_readahead_blks, Opt_journal_ioprio,
        Opt_dioread_nolock, Opt_dioread_lock,
        Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable,
+       Opt_max_dir_size_kb,
 };
 
 static const match_table_t tokens = {
@@ -1318,6 +1302,7 @@ static const match_table_t tokens = {
        {Opt_init_itable, "init_itable=%u"},
        {Opt_init_itable, "init_itable"},
        {Opt_noinit_itable, "noinit_itable"},
+       {Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
        {Opt_removed, "check=none"},    /* mount option from ext2/3 */
        {Opt_removed, "nocheck"},       /* mount option from ext2/3 */
        {Opt_removed, "reservation"},   /* mount option from ext2/3 */
@@ -1498,6 +1483,7 @@ static const struct mount_opts {
        {Opt_jqfmt_vfsold, QFMT_VFS_OLD, MOPT_QFMT},
        {Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT},
        {Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
+       {Opt_max_dir_size_kb, 0, MOPT_GTE0},
        {Opt_err, 0, 0}
 };
 
@@ -1613,6 +1599,8 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
                        if (!args->from)
                                arg = EXT4_DEF_LI_WAIT_MULT;
                        sbi->s_li_wait_mult = arg;
+               } else if (token == Opt_max_dir_size_kb) {
+                       sbi->s_max_dir_size_kb = arg;
                } else if (token == Opt_stripe) {
                        sbi->s_stripe = arg;
                } else if (m->flags & MOPT_DATAJ) {
@@ -1685,7 +1673,7 @@ static int parse_options(char *options, struct super_block *sb,
                 * Initialize args struct so we know whether arg was
                 * found; some options take optional arguments.
                 */
-               args[0].to = args[0].from = 0;
+               args[0].to = args[0].from = NULL;
                token = match_token(p, tokens, args);
                if (handle_mount_opt(sb, p, token, args, journal_devnum,
                                     journal_ioprio, is_remount) < 0)
@@ -1844,6 +1832,8 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
        if (nodefs || (test_opt(sb, INIT_INODE_TABLE) &&
                       (sbi->s_li_wait_mult != EXT4_DEF_LI_WAIT_MULT)))
                SEQ_OPTS_PRINT("init_itable=%u", sbi->s_li_wait_mult);
+       if (nodefs || sbi->s_max_dir_size_kb)
+               SEQ_OPTS_PRINT("max_dir_size_kb=%u", sbi->s_max_dir_size_kb);
 
        ext4_show_quota_options(seq, sb);
        return 0;
@@ -1935,15 +1925,45 @@ done:
        return res;
 }
 
+int ext4_alloc_flex_bg_array(struct super_block *sb, ext4_group_t ngroup)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+       struct flex_groups *new_groups;
+       int size;
+
+       if (!sbi->s_log_groups_per_flex)
+               return 0;
+
+       size = ext4_flex_group(sbi, ngroup - 1) + 1;
+       if (size <= sbi->s_flex_groups_allocated)
+               return 0;
+
+       size = roundup_pow_of_two(size * sizeof(struct flex_groups));
+       new_groups = ext4_kvzalloc(size, GFP_KERNEL);
+       if (!new_groups) {
+               ext4_msg(sb, KERN_ERR, "not enough memory for %d flex groups",
+                        size / (int) sizeof(struct flex_groups));
+               return -ENOMEM;
+       }
+
+       if (sbi->s_flex_groups) {
+               memcpy(new_groups, sbi->s_flex_groups,
+                      (sbi->s_flex_groups_allocated *
+                       sizeof(struct flex_groups)));
+               ext4_kvfree(sbi->s_flex_groups);
+       }
+       sbi->s_flex_groups = new_groups;
+       sbi->s_flex_groups_allocated = size / sizeof(struct flex_groups);
+       return 0;
+}
+
 static int ext4_fill_flex_info(struct super_block *sb)
 {
        struct ext4_sb_info *sbi = EXT4_SB(sb);
        struct ext4_group_desc *gdp = NULL;
-       ext4_group_t flex_group_count;
        ext4_group_t flex_group;
        unsigned int groups_per_flex = 0;
-       size_t size;
-       int i;
+       int i, err;
 
        sbi->s_log_groups_per_flex = sbi->s_es->s_log_groups_per_flex;
        if (sbi->s_log_groups_per_flex < 1 || sbi->s_log_groups_per_flex > 31) {
@@ -1952,17 +1972,9 @@ static int ext4_fill_flex_info(struct super_block *sb)
        }
        groups_per_flex = 1 << sbi->s_log_groups_per_flex;
 
-       /* We allocate both existing and potentially added groups */
-       flex_group_count = ((sbi->s_groups_count + groups_per_flex - 1) +
-                       ((le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) + 1) <<
-                             EXT4_DESC_PER_BLOCK_BITS(sb))) / groups_per_flex;
-       size = flex_group_count * sizeof(struct flex_groups);
-       sbi->s_flex_groups = ext4_kvzalloc(size, GFP_KERNEL);
-       if (sbi->s_flex_groups == NULL) {
-               ext4_msg(sb, KERN_ERR, "not enough memory for %u flex groups",
-                        flex_group_count);
+       err = ext4_alloc_flex_bg_array(sb, sbi->s_groups_count);
+       if (err)
                goto failed;
-       }
 
        for (i = 0; i < sbi->s_groups_count; i++) {
                gdp = ext4_get_group_desc(sb, i, NULL);
@@ -2549,6 +2561,7 @@ EXT4_RW_ATTR_SBI_UI(mb_order2_req, s_mb_order2_reqs);
 EXT4_RW_ATTR_SBI_UI(mb_stream_req, s_mb_stream_request);
 EXT4_RW_ATTR_SBI_UI(mb_group_prealloc, s_mb_group_prealloc);
 EXT4_RW_ATTR_SBI_UI(max_writeback_mb_bump, s_max_writeback_mb_bump);
+EXT4_RW_ATTR_SBI_UI(extent_max_zeroout_kb, s_extent_max_zeroout_kb);
 EXT4_ATTR(trigger_fs_error, 0200, NULL, trigger_test_error);
 
 static struct attribute *ext4_attrs[] = {
@@ -2564,6 +2577,7 @@ static struct attribute *ext4_attrs[] = {
        ATTR_LIST(mb_stream_req),
        ATTR_LIST(mb_group_prealloc),
        ATTR_LIST(max_writeback_mb_bump),
+       ATTR_LIST(extent_max_zeroout_kb),
        ATTR_LIST(trigger_fs_error),
        NULL,
 };
@@ -2747,6 +2761,7 @@ static int ext4_run_li_request(struct ext4_li_request *elr)
        sb = elr->lr_super;
        ngroups = EXT4_SB(sb)->s_groups_count;
 
+       sb_start_write(sb);
        for (group = elr->lr_next_group; group < ngroups; group++) {
                gdp = ext4_get_group_desc(sb, group, NULL);
                if (!gdp) {
@@ -2773,6 +2788,7 @@ static int ext4_run_li_request(struct ext4_li_request *elr)
                elr->lr_next_sched = jiffies + elr->lr_timeout;
                elr->lr_next_group = group + 1;
        }
+       sb_end_write(sb);
 
        return ret;
 }
@@ -3133,6 +3149,10 @@ static int count_overhead(struct super_block *sb, ext4_group_t grp,
        ext4_group_t            i, ngroups = ext4_get_groups_count(sb);
        int                     s, j, count = 0;
 
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC))
+               return (ext4_bg_has_super(sb, grp) + ext4_bg_num_gdb(sb, grp) +
+                       sbi->s_itb_per_group + 2);
+
        first_block = le32_to_cpu(sbi->s_es->s_first_data_block) +
                (grp * EXT4_BLOCKS_PER_GROUP(sb));
        last_block = first_block + EXT4_BLOCKS_PER_GROUP(sb) - 1;
@@ -3758,6 +3778,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 
        sbi->s_stripe = ext4_get_stripe_size(sbi);
        sbi->s_max_writeback_mb_bump = 128;
+       sbi->s_extent_max_zeroout_kb = 32;
 
        /*
         * set up enough so that it can read an inode
@@ -4444,6 +4465,7 @@ static void ext4_clear_journal_err(struct super_block *sb,
                ext4_commit_super(sb, 1);
 
                jbd2_journal_clear_err(journal);
+               jbd2_journal_update_sb_errno(journal);
        }
 }
 
@@ -4460,10 +4482,8 @@ int ext4_force_commit(struct super_block *sb)
                return 0;
 
        journal = EXT4_SB(sb)->s_journal;
-       if (journal) {
-               vfs_check_frozen(sb, SB_FREEZE_TRANS);
+       if (journal)
                ret = ext4_journal_force_commit(journal);
-       }
 
        return ret;
 }
@@ -4493,9 +4513,8 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
  * gives us a chance to flush the journal completely and mark the fs clean.
  *
  * Note that only this function cannot bring a filesystem to be in a clean
- * state independently, because ext4 prevents a new handle from being started
- * by @sb->s_frozen, which stays in an upper layer.  It thus needs help from
- * the upper layer.
+ * state independently. It relies on upper layer to stop all data & metadata
+ * modifications.
  */
 static int ext4_freeze(struct super_block *sb)
 {
@@ -4522,7 +4541,7 @@ static int ext4_freeze(struct super_block *sb)
        EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER);
        error = ext4_commit_super(sb, 1);
 out:
-       /* we rely on s_frozen to stop further updates */
+       /* we rely on upper layer to stop further updates */
        jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
        return error;
 }
@@ -4536,11 +4555,9 @@ static int ext4_unfreeze(struct super_block *sb)
        if (sb->s_flags & MS_RDONLY)
                return 0;
 
-       lock_super(sb);
        /* Reset the needs_recovery flag before the fs is unlocked. */
        EXT4_SET_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER);
        ext4_commit_super(sb, 1);
-       unlock_super(sb);
        return 0;
 }
 
@@ -4576,7 +4593,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
        char *orig_data = kstrdup(data, GFP_KERNEL);
 
        /* Store the original options */
-       lock_super(sb);
        old_sb_flags = sb->s_flags;
        old_opts.s_mount_opt = sbi->s_mount_opt;
        old_opts.s_mount_opt2 = sbi->s_mount_opt2;
@@ -4718,7 +4734,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
        if (sbi->s_journal == NULL)
                ext4_commit_super(sb, 1);
 
-       unlock_super(sb);
 #ifdef CONFIG_QUOTA
        /* Release old quota file names */
        for (i = 0; i < MAXQUOTAS; i++)
@@ -4731,10 +4746,8 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
                else if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
                                        EXT4_FEATURE_RO_COMPAT_QUOTA)) {
                        err = ext4_enable_quotas(sb);
-                       if (err) {
-                               lock_super(sb);
+                       if (err)
                                goto restore_opts;
-                       }
                }
        }
 #endif
@@ -4761,7 +4774,6 @@ restore_opts:
                sbi->s_qf_names[i] = old_opts.s_qf_names[i];
        }
 #endif
-       unlock_super(sb);
        kfree(orig_data);
        return err;
 }
@@ -5286,8 +5298,10 @@ static int __init ext4_init_fs(void)
        if (err)
                goto out6;
        ext4_kset = kset_create_and_add("ext4", NULL, fs_kobj);
-       if (!ext4_kset)
+       if (!ext4_kset) {
+               err = -ENOMEM;
                goto out5;
+       }
        ext4_proc_root = proc_mkdir("fs/ext4", NULL);
 
        err = ext4_init_feat_adverts();