f2fs: introduce mode=lfs mount option
authorJaegeuk Kim <jaegeuk@kernel.org>
Sat, 4 Jun 2016 02:29:38 +0000 (19:29 -0700)
committerJaegeuk Kim <jaegeuk@kernel.org>
Mon, 13 Jun 2016 18:55:21 +0000 (11:55 -0700)
This mount option is to enable original log-structured filesystem forcefully.
So, there should be no random writes for main area.

Especially, this supports host-managed SMR device.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Documentation/filesystems/f2fs.txt
fs/f2fs/checkpoint.c
fs/f2fs/data.c
fs/f2fs/f2fs.h
fs/f2fs/file.c
fs/f2fs/recovery.c
fs/f2fs/segment.c
fs/f2fs/segment.h
fs/f2fs/super.c

index e1c9f0849da6d99ebcf03b6d6fec56f09c205e67..3a5ce24021d940286b54a903c0f3c675b5126753 100644 (file)
@@ -151,6 +151,9 @@ noinline_data          Disable the inline data feature, inline data feature is
                        enabled by default.
 data_flush             Enable data flushing before checkpoint in order to
                        persist data of regular and symlink.
+mode=%s                Control block allocation mode which supports "adaptive"
+                       and "lfs". In "lfs" mode, there should be no random
+                       writes towards main area.
 
 ================================================================================
 DEBUGFS ENTRIES
index 4179c7b971fc62c8e66194e97f040f9bcf03824b..837e6bcad5cea425b76581c26c849bb4f8c99871 100644 (file)
@@ -981,7 +981,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
         * This avoids to conduct wrong roll-forward operations and uses
         * metapages, so should be called prior to sync_meta_pages below.
         */
-       if (discard_next_dnode(sbi, discard_blk))
+       if (!test_opt(sbi, LFS) && discard_next_dnode(sbi, discard_blk))
                invalidate = true;
 
        /* Flush all the NAT/SIT pages */
index 5f655d0c5b1f9db4cc8d578d7b1f9a9b8b5afd5a..607ef43973307bb46dbb6fe66d8fe056d1971bd3 100644 (file)
@@ -1710,6 +1710,8 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
 
        if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
                return 0;
+       if (test_opt(F2FS_I_SB(inode), LFS))
+               return 0;
 
        trace_f2fs_direct_IO_enter(inode, offset, count, iov_iter_rw(iter));
 
index 24c7cde8490591897390393021c575d6c308c513..82acdec022ef9bc8a9dfeec6e25135a67a6054dd 100644 (file)
@@ -111,6 +111,8 @@ static inline bool time_to_inject(int type)
 #define F2FS_MOUNT_FORCE_FG_GC         0x00004000
 #define F2FS_MOUNT_DATA_FLUSH          0x00008000
 #define F2FS_MOUNT_FAULT_INJECTION     0x00010000
+#define F2FS_MOUNT_ADAPTIVE            0x00020000
+#define F2FS_MOUNT_LFS                 0x00040000
 
 #define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option)
 #define set_opt(sbi, option)   (sbi->mount_opt.opt |= F2FS_MOUNT_##option)
index 7a8d262bc488293dfeaed2d24e0ad9c390eef2d1..b9d745ef5b08c636afbe031b7bafad2e80130c26 100644 (file)
@@ -878,9 +878,15 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src,
                return full ? truncate_hole(inode, dst, dst + 1) : 0;
 
        if (do_replace) {
-               struct page *ipage = get_node_page(sbi, inode->i_ino);
+               struct page *ipage;
                struct node_info ni;
 
+               if (test_opt(sbi, LFS)) {
+                       ret = -ENOTSUPP;
+                       goto err_out;
+               }
+
+               ipage = get_node_page(sbi, inode->i_ino);
                if (IS_ERR(ipage)) {
                        ret = PTR_ERR(ipage);
                        goto err_out;
index b568b28c74f2a677d800a8a102018d7b3909212e..a39d84ab66b2c2e777e7c0433733f1b62eadcb77 100644 (file)
@@ -624,8 +624,12 @@ out:
        if (err) {
                bool invalidate = false;
 
-               if (discard_next_dnode(sbi, blkaddr))
+               if (test_opt(sbi, LFS)) {
+                       update_meta_page(sbi, NULL, blkaddr);
                        invalidate = true;
+               } else if (discard_next_dnode(sbi, blkaddr)) {
+                       invalidate = true;
+               }
 
                /* Flush all the NAT/SIT pages */
                while (get_pages(sbi, F2FS_DIRTY_META))
index eff046a792adcf8f50e66f26a7c47025e0ac6932..4792f94089f74e38d0ea0630f836e18aa1894399 100644 (file)
@@ -707,6 +707,7 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
        unsigned long *prefree_map = dirty_i->dirty_segmap[PRE];
        unsigned int start = 0, end = -1;
+       unsigned int secno, start_segno;
 
        mutex_lock(&dirty_i->seglist_lock);
 
@@ -726,8 +727,22 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc)
                if (!test_opt(sbi, DISCARD))
                        continue;
 
-               f2fs_issue_discard(sbi, START_BLOCK(sbi, start),
+               if (!test_opt(sbi, LFS) || sbi->segs_per_sec == 1) {
+                       f2fs_issue_discard(sbi, START_BLOCK(sbi, start),
                                (end - start) << sbi->log_blocks_per_seg);
+                       continue;
+               }
+next:
+               secno = GET_SECNO(sbi, start);
+               start_segno = secno * sbi->segs_per_sec;
+               if (!IS_CURSEC(sbi, secno) &&
+                       !get_valid_blocks(sbi, start, sbi->segs_per_sec))
+                       f2fs_issue_discard(sbi, START_BLOCK(sbi, start_segno),
+                               sbi->segs_per_sec << sbi->log_blocks_per_seg);
+
+               start = start_segno + sbi->segs_per_sec;
+               if (start < end)
+                       goto next;
        }
        mutex_unlock(&dirty_i->seglist_lock);
 
@@ -1221,6 +1236,9 @@ void allocate_new_segments(struct f2fs_sb_info *sbi)
 {
        int i;
 
+       if (test_opt(sbi, LFS))
+               return;
+
        for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++)
                __allocate_new_segments(sbi, i);
 }
index 890bb28d2082bcad6e4a4d2afb3aa1d378f95cb4..d74cc330ae13742eaba0edde0bc2a289ac5009d5 100644 (file)
@@ -470,6 +470,10 @@ static inline bool need_SSR(struct f2fs_sb_info *sbi)
 {
        int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES);
        int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS);
+
+       if (test_opt(sbi, LFS))
+               return false;
+
        return free_sections(sbi) <= (node_secs + 2 * dent_secs +
                                                reserved_sections(sbi) + 1);
 }
@@ -533,6 +537,9 @@ static inline bool need_inplace_update(struct inode *inode)
        if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
                return false;
 
+       if (test_opt(sbi, LFS))
+               return false;
+
        if (policy & (0x1 << F2FS_IPU_FORCE))
                return true;
        if (policy & (0x1 << F2FS_IPU_SSR) && need_SSR(sbi))
index dc66f1623e06a1e3bef9656f102cec72017b3b2c..edc736de8ee950460e9fb0eeb83a8c44c674ca5e 100644 (file)
@@ -94,6 +94,7 @@ enum {
        Opt_noextent_cache,
        Opt_noinline_data,
        Opt_data_flush,
+       Opt_mode,
        Opt_fault_injection,
        Opt_lazytime,
        Opt_nolazytime,
@@ -123,6 +124,7 @@ static match_table_t f2fs_tokens = {
        {Opt_noextent_cache, "noextent_cache"},
        {Opt_noinline_data, "noinline_data"},
        {Opt_data_flush, "data_flush"},
+       {Opt_mode, "mode=%s"},
        {Opt_fault_injection, "fault_injection=%u"},
        {Opt_lazytime, "lazytime"},
        {Opt_nolazytime, "nolazytime"},
@@ -506,6 +508,25 @@ static int parse_options(struct super_block *sb, char *options)
                case Opt_data_flush:
                        set_opt(sbi, DATA_FLUSH);
                        break;
+               case Opt_mode:
+                       name = match_strdup(&args[0]);
+
+                       if (!name)
+                               return -ENOMEM;
+                       if (strlen(name) == 8 &&
+                                       !strncmp(name, "adaptive", 8)) {
+                               set_opt(sbi, ADAPTIVE);
+                               clear_opt(sbi, LFS);
+                       } else if (strlen(name) == 3 &&
+                                       !strncmp(name, "lfs", 3)) {
+                               clear_opt(sbi, ADAPTIVE);
+                               set_opt(sbi, LFS);
+                       } else {
+                               kfree(name);
+                               return -EINVAL;
+                       }
+                       kfree(name);
+                       break;
                case Opt_fault_injection:
                        if (args->from && match_int(args, &arg))
                                return -EINVAL;
@@ -870,6 +891,12 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
                seq_puts(seq, ",noextent_cache");
        if (test_opt(sbi, DATA_FLUSH))
                seq_puts(seq, ",data_flush");
+
+       seq_puts(seq, ",mode=");
+       if (test_opt(sbi, ADAPTIVE))
+               seq_puts(seq, "adaptive");
+       else if (test_opt(sbi, LFS))
+               seq_puts(seq, "lfs");
        seq_printf(seq, ",active_logs=%u", sbi->active_logs);
 
        return 0;
@@ -953,6 +980,7 @@ static void default_options(struct f2fs_sb_info *sbi)
        set_opt(sbi, EXTENT_CACHE);
        sbi->sb->s_flags |= MS_LAZYTIME;
        set_opt(sbi, FLUSH_MERGE);
+       set_opt(sbi, ADAPTIVE);
 
 #ifdef CONFIG_F2FS_FS_XATTR
        set_opt(sbi, XATTR_USER);