f2fs: remove request_list check in is_idle()
[linux-2.6-block.git] / fs / f2fs / super.c
index 32402f0d9d4032595bb8bd33be37e68ab29405ac..262a27744eb1f1debdaf38d7ff2d60544ac3fd6d 100644 (file)
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * fs/f2fs/super.c
  *
  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
  */
 #include <linux/module.h>
 #include <linux/init.h>
@@ -53,9 +50,10 @@ char *f2fs_fault_name[FAULT_MAX] = {
        [FAULT_DIR_DEPTH]       = "too big dir depth",
        [FAULT_EVICT_INODE]     = "evict_inode fail",
        [FAULT_TRUNCATE]        = "truncate fail",
-       [FAULT_IO]              = "IO error",
+       [FAULT_READ_IO]         = "read IO error",
        [FAULT_CHECKPOINT]      = "checkpoint error",
        [FAULT_DISCARD]         = "discard error",
+       [FAULT_WRITE_IO]        = "write IO error",
 };
 
 void f2fs_build_fault_attr(struct f2fs_sb_info *sbi, unsigned int rate,
@@ -138,6 +136,7 @@ enum {
        Opt_alloc,
        Opt_fsync,
        Opt_test_dummy_encryption,
+       Opt_checkpoint,
        Opt_err,
 };
 
@@ -196,6 +195,7 @@ static match_table_t f2fs_tokens = {
        {Opt_alloc, "alloc_mode=%s"},
        {Opt_fsync, "fsync_mode=%s"},
        {Opt_test_dummy_encryption, "test_dummy_encryption"},
+       {Opt_checkpoint, "checkpoint=%s"},
        {Opt_err, NULL},
 };
 
@@ -594,28 +594,31 @@ static int parse_options(struct super_block *sb, char *options)
                        }
                        F2FS_OPTION(sbi).write_io_size_bits = arg;
                        break;
+#ifdef CONFIG_F2FS_FAULT_INJECTION
                case Opt_fault_injection:
                        if (args->from && match_int(args, &arg))
                                return -EINVAL;
-#ifdef CONFIG_F2FS_FAULT_INJECTION
                        f2fs_build_fault_attr(sbi, arg, F2FS_ALL_FAULT_TYPE);
                        set_opt(sbi, FAULT_INJECTION);
-#else
-                       f2fs_msg(sb, KERN_INFO,
-                               "FAULT_INJECTION was not selected");
-#endif
                        break;
+
                case Opt_fault_type:
                        if (args->from && match_int(args, &arg))
                                return -EINVAL;
-#ifdef CONFIG_F2FS_FAULT_INJECTION
                        f2fs_build_fault_attr(sbi, 0, arg);
                        set_opt(sbi, FAULT_INJECTION);
+                       break;
 #else
+               case Opt_fault_injection:
                        f2fs_msg(sb, KERN_INFO,
-                               "FAULT_INJECTION was not selected");
-#endif
+                               "fault_injection options not supported");
                        break;
+
+               case Opt_fault_type:
+                       f2fs_msg(sb, KERN_INFO,
+                               "fault_type options not supported");
+                       break;
+#endif
                case Opt_lazytime:
                        sb->s_flags |= SB_LAZYTIME;
                        break;
@@ -768,6 +771,23 @@ static int parse_options(struct super_block *sb, char *options)
                                        "Test dummy encryption mount option ignored");
 #endif
                        break;
+               case Opt_checkpoint:
+                       name = match_strdup(&args[0]);
+                       if (!name)
+                               return -ENOMEM;
+
+                       if (strlen(name) == 6 &&
+                                       !strncmp(name, "enable", 6)) {
+                               clear_opt(sbi, DISABLE_CHECKPOINT);
+                       } else if (strlen(name) == 7 &&
+                                       !strncmp(name, "disable", 7)) {
+                               set_opt(sbi, DISABLE_CHECKPOINT);
+                       } else {
+                               kfree(name);
+                               return -EINVAL;
+                       }
+                       kfree(name);
+                       break;
                default:
                        f2fs_msg(sb, KERN_ERR,
                                "Unrecognized mount option \"%s\" or missing value",
@@ -826,6 +846,12 @@ static int parse_options(struct super_block *sb, char *options)
                }
        }
 
+       if (test_opt(sbi, DISABLE_CHECKPOINT) && test_opt(sbi, LFS)) {
+               f2fs_msg(sb, KERN_ERR,
+                               "LFS not compatible with checkpoint=disable\n");
+               return -EINVAL;
+       }
+
        /* Not pass down write hints if the number of active logs is lesser
         * than NR_CURSEG_TYPE.
         */
@@ -1013,8 +1039,8 @@ static void f2fs_put_super(struct super_block *sb)
         * But, the previous checkpoint was not done by umount, it needs to do
         * clean checkpoint again.
         */
-       if (is_sbi_flag_set(sbi, SBI_IS_DIRTY) ||
-                       !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
+       if ((is_sbi_flag_set(sbi, SBI_IS_DIRTY) ||
+                       !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG))) {
                struct cp_control cpc = {
                        .reason = CP_UMOUNT,
                };
@@ -1086,6 +1112,8 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
 
        if (unlikely(f2fs_cp_error(sbi)))
                return 0;
+       if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
+               return 0;
 
        trace_f2fs_sync_fs(sb, sync);
 
@@ -1185,6 +1213,11 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
        buf->f_blocks = total_count - start_count;
        buf->f_bfree = user_block_count - valid_user_blocks(sbi) -
                                                sbi->current_reserved_blocks;
+       if (unlikely(buf->f_bfree <= sbi->unusable_block_count))
+               buf->f_bfree = 0;
+       else
+               buf->f_bfree -= sbi->unusable_block_count;
+
        if (buf->f_bfree > F2FS_OPTION(sbi).root_reserved_blocks)
                buf->f_bavail = buf->f_bfree -
                                F2FS_OPTION(sbi).root_reserved_blocks;
@@ -1329,7 +1362,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
                                from_kgid_munged(&init_user_ns,
                                        F2FS_OPTION(sbi).s_resgid));
        if (F2FS_IO_SIZE_BITS(sbi))
-               seq_printf(seq, ",io_size=%uKB", F2FS_IO_SIZE_KB(sbi));
+               seq_printf(seq, ",io_bits=%u",
+                               F2FS_OPTION(sbi).write_io_size_bits);
 #ifdef CONFIG_F2FS_FAULT_INJECTION
        if (test_opt(sbi, FAULT_INJECTION)) {
                seq_printf(seq, ",fault_injection=%u",
@@ -1363,6 +1397,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
        else if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_REUSE)
                seq_printf(seq, ",alloc_mode=%s", "reuse");
 
+       if (test_opt(sbi, DISABLE_CHECKPOINT))
+               seq_puts(seq, ",checkpoint=disable");
+
        if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_POSIX)
                seq_printf(seq, ",fsync_mode=%s", "posix");
        else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT)
@@ -1390,6 +1427,7 @@ static void default_options(struct f2fs_sb_info *sbi)
        set_opt(sbi, INLINE_DENTRY);
        set_opt(sbi, EXTENT_CACHE);
        set_opt(sbi, NOHEAP);
+       clear_opt(sbi, DISABLE_CHECKPOINT);
        sbi->sb->s_flags |= SB_LAZYTIME;
        set_opt(sbi, FLUSH_MERGE);
        set_opt(sbi, DISCARD);
@@ -1411,6 +1449,57 @@ static void default_options(struct f2fs_sb_info *sbi)
 #ifdef CONFIG_QUOTA
 static int f2fs_enable_quotas(struct super_block *sb);
 #endif
+
+static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
+{
+       struct cp_control cpc;
+       int err;
+
+       sbi->sb->s_flags |= SB_ACTIVE;
+
+       mutex_lock(&sbi->gc_mutex);
+       f2fs_update_time(sbi, DISABLE_TIME);
+
+       while (!f2fs_time_over(sbi, DISABLE_TIME)) {
+               err = f2fs_gc(sbi, true, false, NULL_SEGNO);
+               if (err == -ENODATA)
+                       break;
+               if (err && err != -EAGAIN) {
+                       mutex_unlock(&sbi->gc_mutex);
+                       return err;
+               }
+       }
+       mutex_unlock(&sbi->gc_mutex);
+
+       err = sync_filesystem(sbi->sb);
+       if (err)
+               return err;
+
+       if (f2fs_disable_cp_again(sbi))
+               return -EAGAIN;
+
+       mutex_lock(&sbi->gc_mutex);
+       cpc.reason = CP_PAUSE;
+       set_sbi_flag(sbi, SBI_CP_DISABLED);
+       f2fs_write_checkpoint(sbi, &cpc);
+
+       sbi->unusable_block_count = 0;
+       mutex_unlock(&sbi->gc_mutex);
+       return 0;
+}
+
+static void f2fs_enable_checkpoint(struct f2fs_sb_info *sbi)
+{
+       mutex_lock(&sbi->gc_mutex);
+       f2fs_dirty_to_prefree(sbi);
+
+       clear_sbi_flag(sbi, SBI_CP_DISABLED);
+       set_sbi_flag(sbi, SBI_IS_DIRTY);
+       mutex_unlock(&sbi->gc_mutex);
+
+       f2fs_sync_fs(sbi->sb, 1);
+}
+
 static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 {
        struct f2fs_sb_info *sbi = F2FS_SB(sb);
@@ -1420,6 +1509,8 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
        bool need_restart_gc = false;
        bool need_stop_gc = false;
        bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE);
+       bool disable_checkpoint = test_opt(sbi, DISABLE_CHECKPOINT);
+       bool checkpoint_changed;
 #ifdef CONFIG_QUOTA
        int i, j;
 #endif
@@ -1464,6 +1555,8 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
        err = parse_options(sb, data);
        if (err)
                goto restore_opts;
+       checkpoint_changed =
+                       disable_checkpoint != test_opt(sbi, DISABLE_CHECKPOINT);
 
        /*
         * Previous and new state of filesystem is RO,
@@ -1477,7 +1570,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
                err = dquot_suspend(sb, -1);
                if (err < 0)
                        goto restore_opts;
-       } else if (f2fs_readonly(sb) && !(*flags & MS_RDONLY)) {
+       } else if (f2fs_readonly(sb) && !(*flags & SB_RDONLY)) {
                /* dquot_resume needs RW */
                sb->s_flags &= ~SB_RDONLY;
                if (sb_any_quota_suspended(sb)) {
@@ -1497,6 +1590,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
                goto restore_opts;
        }
 
+       if ((*flags & SB_RDONLY) && test_opt(sbi, DISABLE_CHECKPOINT)) {
+               err = -EINVAL;
+               f2fs_msg(sbi->sb, KERN_WARNING,
+                       "disabling checkpoint not compatible with read-only");
+               goto restore_opts;
+       }
+
        /*
         * We stop the GC thread if FS is mounted as RO
         * or if background_gc = off is passed in mount
@@ -1525,6 +1625,16 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
                clear_sbi_flag(sbi, SBI_IS_CLOSE);
        }
 
+       if (checkpoint_changed) {
+               if (test_opt(sbi, DISABLE_CHECKPOINT)) {
+                       err = f2fs_disable_checkpoint(sbi);
+                       if (err)
+                               goto restore_gc;
+               } else {
+                       f2fs_enable_checkpoint(sbi);
+               }
+       }
+
        /*
         * We stop issue flush thread if FS is mounted as RO
         * or if flush_merge is not passed in mount option.
@@ -1548,6 +1658,7 @@ skip:
                (test_opt(sbi, POSIX_ACL) ? SB_POSIXACL : 0);
 
        limit_reserve_root(sbi);
+       *flags = (*flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME);
        return 0;
 restore_gc:
        if (need_restart_gc) {
@@ -2175,6 +2286,26 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi,
                                        (bh->b_data + F2FS_SUPER_OFFSET);
        struct super_block *sb = sbi->sb;
        unsigned int blocksize;
+       size_t crc_offset = 0;
+       __u32 crc = 0;
+
+       /* Check checksum_offset and crc in superblock */
+       if (le32_to_cpu(raw_super->feature) & F2FS_FEATURE_SB_CHKSUM) {
+               crc_offset = le32_to_cpu(raw_super->checksum_offset);
+               if (crc_offset !=
+                       offsetof(struct f2fs_super_block, crc)) {
+                       f2fs_msg(sb, KERN_INFO,
+                               "Invalid SB checksum offset: %zu",
+                               crc_offset);
+                       return 1;
+               }
+               crc = le32_to_cpu(raw_super->crc);
+               if (!f2fs_crc_valid(sbi, crc, raw_super, crc_offset)) {
+                       f2fs_msg(sb, KERN_INFO,
+                               "Invalid SB checksum value: %u", crc);
+                       return 1;
+               }
+       }
 
        if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) {
                f2fs_msg(sb, KERN_INFO,
@@ -2460,6 +2591,9 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
        sbi->dir_level = DEF_DIR_LEVEL;
        sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL;
        sbi->interval_time[REQ_TIME] = DEF_IDLE_INTERVAL;
+       sbi->interval_time[DISCARD_TIME] = DEF_IDLE_INTERVAL;
+       sbi->interval_time[GC_TIME] = DEF_IDLE_INTERVAL;
+       sbi->interval_time[DISABLE_TIME] = DEF_DISABLE_INTERVAL;
        clear_sbi_flag(sbi, SBI_NEED_FSCK);
 
        for (i = 0; i < NR_COUNT_TYPE; i++)
@@ -2630,6 +2764,7 @@ static int read_raw_super_block(struct f2fs_sb_info *sbi,
 int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover)
 {
        struct buffer_head *bh;
+       __u32 crc = 0;
        int err;
 
        if ((recover && f2fs_readonly(sbi->sb)) ||
@@ -2638,6 +2773,13 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover)
                return -EROFS;
        }
 
+       /* we should update superblock crc here */
+       if (!recover && f2fs_sb_has_sb_chksum(sbi->sb)) {
+               crc = f2fs_crc32(sbi, F2FS_RAW_SUPER(sbi),
+                               offsetof(struct f2fs_super_block, crc));
+               F2FS_RAW_SUPER(sbi)->crc = cpu_to_le32(crc);
+       }
+
        /* write back-up superblock first */
        bh = sb_bread(sbi->sb, sbi->valid_super_block ? 0 : 1);
        if (!bh)
@@ -3048,11 +3190,9 @@ try_onemore:
        /* Enable quota usage during mount */
        if (f2fs_sb_has_quota_ino(sb) && !f2fs_readonly(sb)) {
                err = f2fs_enable_quotas(sb);
-               if (err) {
+               if (err)
                        f2fs_msg(sb, KERN_ERR,
                                "Cannot turn on quotas: error %d", err);
-                       goto free_sysfs;
-               }
        }
 #endif
        /* if there are nt orphan nodes free them */
@@ -3060,6 +3200,9 @@ try_onemore:
        if (err)
                goto free_meta;
 
+       if (unlikely(is_set_ckpt_flags(sbi, CP_DISABLED_FLAG)))
+               goto skip_recovery;
+
        /* recover fsynced data */
        if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
                /*
@@ -3099,6 +3242,14 @@ skip_recovery:
        /* f2fs_recover_fsync_data() cleared this already */
        clear_sbi_flag(sbi, SBI_POR_DOING);
 
+       if (test_opt(sbi, DISABLE_CHECKPOINT)) {
+               err = f2fs_disable_checkpoint(sbi);
+               if (err)
+                       goto free_meta;
+       } else if (is_set_ckpt_flags(sbi, CP_DISABLED_FLAG)) {
+               f2fs_enable_checkpoint(sbi);
+       }
+
        /*
         * If filesystem is not mounted as read-only then
         * do start the gc_thread.
@@ -3142,9 +3293,6 @@ free_meta:
         * falls into an infinite loop in f2fs_sync_meta_pages().
         */
        truncate_inode_pages_final(META_MAPPING(sbi));
-#ifdef CONFIG_QUOTA
-free_sysfs:
-#endif
        f2fs_unregister_sysfs(sbi);
 free_root_inode:
        dput(sb->s_root);