Merge tag 'f2fs-for-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeu...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 13 Nov 2021 19:20:22 +0000 (11:20 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 13 Nov 2021 19:20:22 +0000 (11:20 -0800)
Pull f2fs updates from Jaegeuk Kim:
 "In this cycle, we've applied relatively small number of patches which
  fix subtle corner cases mainly, while introducing a new mount option
  to be able to fragment the disk intentionally for performance tests.

  Enhancements:

   - add a mount option to fragmente on-disk layout to understand the
     performance

   - support direct IO for multi-partitions

   - add a fault injection of dquot_initialize

  Bug fixes:

   - address some lockdep complaints

   - fix a deadlock issue with quota

   - fix a memory tuning condition

   - fix compression condition to improve the ratio

   - fix disabling compression on the non-empty compressed file

   - invalidate cached pages before IPU/DIO writes

  And, we've added some minor clean-ups as usual"

* tag 'f2fs-for-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs:
  f2fs: fix UAF in f2fs_available_free_memory
  f2fs: invalidate META_MAPPING before IPU/DIO write
  f2fs: support fault injection for dquot_initialize()
  f2fs: fix incorrect return value in f2fs_sanity_check_ckpt()
  f2fs: compress: disallow disabling compress on non-empty compressed file
  f2fs: compress: fix overwrite may reduce compress ratio unproperly
  f2fs: multidevice: support direct IO
  f2fs: introduce fragment allocation mode mount option
  f2fs: replace snprintf in show functions with sysfs_emit
  f2fs: include non-compressed blocks in compr_written_block
  f2fs: fix wrong condition to trigger background checkpoint correctly
  f2fs: fix to use WHINT_MODE
  f2fs: fix up f2fs_lookup tracepoints
  f2fs: set SBI_NEED_FSCK flag when inconsistent node block found
  f2fs: introduce excess_dirty_threshold()
  f2fs: avoid attaching SB_ACTIVE flag during mount
  f2fs: quota: fix potential deadlock
  f2fs: should use GFP_NOFS for directory inodes

1  2 
Documentation/filesystems/f2fs.rst
fs/f2fs/compress.c
fs/f2fs/file.c
fs/f2fs/super.c

@@@ -197,10 -197,29 +197,29 @@@ fault_type=%d            Support configuring fau
                         FAULT_DISCARD            0x000002000
                         FAULT_WRITE_IO           0x000004000
                         FAULT_SLAB_ALLOC         0x000008000
+                        FAULT_DQUOT_INIT         0x000010000
                         ===================      ===========
  mode=%s                        Control block allocation mode which supports "adaptive"
                         and "lfs". In "lfs" mode, there should be no random
                         writes towards main area.
+                        "fragment:segment" and "fragment:block" are newly added here.
+                        These are developer options for experiments to simulate filesystem
+                        fragmentation/after-GC situation itself. The developers use these
+                        modes to understand filesystem fragmentation/after-GC condition well,
+                        and eventually get some insights to handle them better.
+                        In "fragment:segment", f2fs allocates a new segment in ramdom
+                        position. With this, we can simulate the after-GC condition.
+                        In "fragment:block", we can scatter block allocation with
+                        "max_fragment_chunk" and "max_fragment_hole" sysfs nodes.
+                        We added some randomness to both chunk and hole size to make
+                        it close to realistic IO pattern. So, in this mode, f2fs will allocate
+                        1..<max_fragment_chunk> blocks in a chunk and make a hole in the
+                        length of 1..<max_fragment_hole> by turns. With this, the newly
+                        allocated blocks will be scattered throughout the whole partition.
+                        Note that "fragment:block" implicitly enables "fragment:segment"
+                        option for more randomness.
+                        Please, use these options for your experiments and we strongly
+                        recommend to re-format the filesystem after using these options.
  io_bits=%u             Set the bit size of write IO requests. It should be set
                         with "mode=lfs".
  usrquota               Enable plain user disk quota accounting.
@@@ -283,7 -302,7 +302,7 @@@ compress_extension=%s       Support adding s
                         For other files, we can still enable compression via ioctl.
                         Note that, there is one reserved special extension '*', it
                         can be set to enable compression for all files.
 -nocompress_extension=%s          Support adding specified extension, so that f2fs can disable
 +nocompress_extension=%s        Support adding specified extension, so that f2fs can disable
                         compression on those corresponding files, just contrary to compression extension.
                         If you know exactly which files cannot be compressed, you can use this.
                         The same extension name can't appear in both compress and nocompress
diff --combined fs/f2fs/compress.c
@@@ -7,7 -7,6 +7,7 @@@
  
  #include <linux/fs.h>
  #include <linux/f2fs_fs.h>
 +#include <linux/moduleparam.h>
  #include <linux/writeback.h>
  #include <linux/backing-dev.h>
  #include <linux/lzo.h>
@@@ -882,6 -881,25 +882,25 @@@ bool f2fs_cluster_can_merge_page(struc
        return is_page_in_cluster(cc, index);
  }
  
+ bool f2fs_all_cluster_page_loaded(struct compress_ctx *cc, struct pagevec *pvec,
+                               int index, int nr_pages)
+ {
+       unsigned long pgidx;
+       int i;
+       if (nr_pages - index < cc->cluster_size)
+               return false;
+       pgidx = pvec->pages[index]->index;
+       for (i = 1; i < cc->cluster_size; i++) {
+               if (pvec->pages[index + i]->index != pgidx + i)
+                       return false;
+       }
+       return true;
+ }
  static bool cluster_has_invalid_data(struct compress_ctx *cc)
  {
        loff_t i_size = i_size_read(cc->inode);
@@@ -1531,6 -1549,7 +1550,7 @@@ int f2fs_write_multi_pages(struct compr
        if (cluster_may_compress(cc)) {
                err = f2fs_compress_pages(cc);
                if (err == -EAGAIN) {
+                       add_compr_block_stat(cc->inode, cc->cluster_size);
                        goto write;
                } else if (err) {
                        f2fs_put_rpages_wbc(cc, wbc, true, 1);
diff --combined fs/f2fs/file.c
@@@ -786,7 -786,7 +786,7 @@@ int f2fs_truncate(struct inode *inode
                return -EIO;
        }
  
-       err = dquot_initialize(inode);
+       err = f2fs_dquot_initialize(inode);
        if (err)
                return err;
  
@@@ -916,7 -916,7 +916,7 @@@ int f2fs_setattr(struct user_namespace 
                return err;
  
        if (is_quota_modification(inode, attr)) {
-               err = dquot_initialize(inode);
+               err = f2fs_dquot_initialize(inode);
                if (err)
                        return err;
        }
@@@ -3020,7 -3020,7 +3020,7 @@@ static int f2fs_ioc_setproject(struct i
        }
        f2fs_put_page(ipage, 1);
  
-       err = dquot_initialize(inode);
+       err = f2fs_dquot_initialize(inode);
        if (err)
                return err;
  
@@@ -4276,7 -4276,7 +4276,7 @@@ static ssize_t f2fs_file_write_iter(str
                size_t target_size = 0;
                int err;
  
 -              if (iov_iter_fault_in_readable(from, iov_iter_count(from)))
 +              if (fault_in_iov_iter_readable(from, iov_iter_count(from)))
                        set_inode_flag(inode, FI_NO_PREALLOC);
  
                if ((iocb->ki_flags & IOCB_NOWAIT)) {
diff --combined fs/f2fs/super.c
@@@ -58,6 -58,7 +58,7 @@@ const char *f2fs_fault_name[FAULT_MAX] 
        [FAULT_DISCARD]         = "discard error",
        [FAULT_WRITE_IO]        = "write IO error",
        [FAULT_SLAB_ALLOC]      = "slab alloc",
+       [FAULT_DQUOT_INIT]      = "dquot initialize",
  };
  
  void f2fs_build_fault_attr(struct f2fs_sb_info *sbi, unsigned int rate,
@@@ -817,6 -818,10 +818,10 @@@ static int parse_options(struct super_b
                                F2FS_OPTION(sbi).fs_mode = FS_MODE_ADAPTIVE;
                        } else if (!strcmp(name, "lfs")) {
                                F2FS_OPTION(sbi).fs_mode = FS_MODE_LFS;
+                       } else if (!strcmp(name, "fragment:segment")) {
+                               F2FS_OPTION(sbi).fs_mode = FS_MODE_FRAGMENT_SEG;
+                       } else if (!strcmp(name, "fragment:block")) {
+                               F2FS_OPTION(sbi).fs_mode = FS_MODE_FRAGMENT_BLK;
                        } else {
                                kfree(name);
                                return -EINVAL;
@@@ -1292,7 -1297,7 +1297,7 @@@ default_check
        /* Not pass down write hints if the number of active logs is lesser
         * than NR_CURSEG_PERSIST_TYPE.
         */
-       if (F2FS_OPTION(sbi).active_logs != NR_CURSEG_TYPE)
+       if (F2FS_OPTION(sbi).active_logs != NR_CURSEG_PERSIST_TYPE)
                F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF;
  
        if (f2fs_sb_has_readonly(sbi) && !f2fs_readonly(sbi->sb)) {
@@@ -1896,6 -1901,10 +1901,10 @@@ static int f2fs_show_options(struct seq
                seq_puts(seq, "adaptive");
        else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_LFS)
                seq_puts(seq, "lfs");
+       else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_SEG)
+               seq_puts(seq, "fragment:segment");
+       else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK)
+               seq_puts(seq, "fragment:block");
        seq_printf(seq, ",active_logs=%u", F2FS_OPTION(sbi).active_logs);
        if (test_opt(sbi, RESERVE_ROOT))
                seq_printf(seq, ",reserve_root=%u,resuid=%u,resgid=%u",
@@@ -2491,6 -2500,16 +2500,16 @@@ retry
        return len - towrite;
  }
  
+ int f2fs_dquot_initialize(struct inode *inode)
+ {
+       if (time_to_inject(F2FS_I_SB(inode), FAULT_DQUOT_INIT)) {
+               f2fs_show_injection_info(F2FS_I_SB(inode), FAULT_DQUOT_INIT);
+               return -ESRCH;
+       }
+       return dquot_initialize(inode);
+ }
  static struct dquot **f2fs_get_dquots(struct inode *inode)
  {
        return F2FS_I(inode)->i_dquot;
@@@ -2875,6 -2894,11 +2894,11 @@@ static const struct quotactl_ops f2fs_q
        .get_nextdqblk  = dquot_get_next_dqblk,
  };
  #else
+ int f2fs_dquot_initialize(struct inode *inode)
+ {
+       return 0;
+ }
  int f2fs_quota_sync(struct super_block *sb, int type)
  {
        return 0;
@@@ -2976,6 -3000,7 +3000,6 @@@ static const struct fscrypt_operations 
        .set_context            = f2fs_set_context,
        .get_dummy_policy       = f2fs_get_dummy_policy,
        .empty_dir              = f2fs_empty_dir,
 -      .max_namelen            = F2FS_NAME_LEN,
        .has_stable_inodes      = f2fs_has_stable_inodes,
        .get_ino_and_lblk_bits  = f2fs_get_ino_and_lblk_bits,
        .get_num_devices        = f2fs_get_num_devices,
@@@ -3486,7 -3511,7 +3510,7 @@@ skip_cross
                NR_CURSEG_PERSIST_TYPE + nat_bits_blocks >= blocks_per_seg)) {
                f2fs_warn(sbi, "Insane cp_payload: %u, nat_bits_blocks: %u)",
                          cp_payload, nat_bits_blocks);
-               return -EFSCORRUPTED;
+               return 1;
        }
  
        if (unlikely(f2fs_cp_error(sbi))) {
@@@ -3522,6 -3547,8 +3546,8 @@@ static void init_sb_info(struct f2fs_sb
        sbi->max_victim_search = DEF_MAX_VICTIM_SEARCH;
        sbi->migration_granularity = sbi->segs_per_sec;
        sbi->seq_file_ra_mul = MIN_RA_MUL;
+       sbi->max_fragment_chunk = DEF_FRAGMENT_SIZE;
+       sbi->max_fragment_hole = DEF_FRAGMENT_SIZE;
  
        sbi->dir_level = DEF_DIR_LEVEL;
        sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL;
@@@ -3746,6 -3773,7 +3772,7 @@@ static int f2fs_scan_devices(struct f2f
  {
        struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
        unsigned int max_devices = MAX_DEVICES;
+       unsigned int logical_blksize;
        int i;
  
        /* Initialize single device information */
        if (!sbi->devs)
                return -ENOMEM;
  
+       logical_blksize = bdev_logical_block_size(sbi->sb->s_bdev);
+       sbi->aligned_blksize = true;
        for (i = 0; i < max_devices; i++) {
  
                if (i > 0 && !RDEV(i).path[0])
                /* to release errored devices */
                sbi->s_ndevs = i + 1;
  
+               if (logical_blksize != bdev_logical_block_size(FDEV(i).bdev))
+                       sbi->aligned_blksize = false;
  #ifdef CONFIG_BLK_DEV_ZONED
                if (bdev_zoned_model(FDEV(i).bdev) == BLK_ZONED_HM &&
                                !f2fs_sb_has_blkzoned(sbi)) {
@@@ -4351,6 -4385,8 +4384,8 @@@ free_node_inode
  free_stats:
        f2fs_destroy_stats(sbi);
  free_nm:
+       /* stop discard thread before destroying node manager */
+       f2fs_stop_discard_thread(sbi);
        f2fs_destroy_node_manager(sbi);
  free_sm:
        f2fs_destroy_segment_manager(sbi);