diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 0caf7da0a532..0409c47584ea 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -180,6 +180,8 @@ whint_mode=%s Control which write hints are passed down to block down hints. In "user-based" mode, f2fs tries to pass down hints given by users. And in "fs-based" mode, f2fs passes down hints with its policy. +alloc_mode=%s Adjust block allocation policy, which supports "reuse" + and "default". ================================================================================ DEBUGFS ENTRIES diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3bb4e943454a..833c9bc06d03 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1043,6 +1043,11 @@ enum { WHINT_MODE_FS, /* pass down hints with F2FS policy */ }; +enum { + ALLOC_MODE_DEFAULT, /* stay default */ + ALLOC_MODE_REUSE, /* reuse segments as much as possible */ +}; + struct f2fs_sb_info { struct super_block *sb; /* pointer to VFS super block */ struct proc_dir_entry *s_proc; /* proc entry */ @@ -1229,6 +1234,9 @@ struct f2fs_sb_info { #endif /* For which write hints are passed down to block layer */ int whint_mode; + + /* segment allocation policy */ + int alloc_mode; }; #ifdef CONFIG_F2FS_FAULT_INJECTION diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0646d5de8cd0..5e5e2936a26a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2170,6 +2170,11 @@ static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type) if (SIT_I(sbi)->last_victim[ALLOC_NEXT]) return SIT_I(sbi)->last_victim[ALLOC_NEXT]; + + /* find segments from 0 to reuse freed segments */ + if (sbi->alloc_mode == ALLOC_MODE_REUSE) + return 0; + return CURSEG_I(sbi, type)->segno; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 06da158d4263..16d157e44184 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -130,6 +130,7 @@ enum { Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_whint, + Opt_alloc, Opt_err, }; @@ -184,6 +185,7 @@ static match_table_t f2fs_tokens = { {Opt_jqfmt_vfsv0, "jqfmt=vfsv0"}, {Opt_jqfmt_vfsv1, "jqfmt=vfsv1"}, {Opt_whint, "whint_mode=%s"}, + {Opt_alloc, "alloc_mode=%s"}, {Opt_err, NULL}, }; @@ -700,6 +702,23 @@ static int parse_options(struct super_block *sb, char *options) } kfree(name); break; + case Opt_alloc: + name = match_strdup(&args[0]); + if (!name) + return -ENOMEM; + + if (strlen(name) == 7 && + !strncmp(name, "default", 7)) { + sbi->alloc_mode = ALLOC_MODE_DEFAULT; + } else if (strlen(name) == 5 && + !strncmp(name, "reuse", 5)) { + sbi->alloc_mode = ALLOC_MODE_REUSE; + } else { + kfree(name); + return -EINVAL; + } + kfree(name); + break; default: f2fs_msg(sb, KERN_ERR, "Unrecognized mount option \"%s\" or missing value", @@ -1264,6 +1283,10 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) else if (sbi->whint_mode == WHINT_MODE_FS) seq_printf(seq, ",whint_mode=%s", "fs-based"); + if (sbi->alloc_mode == ALLOC_MODE_DEFAULT) + seq_printf(seq, ",alloc_mode=%s", "default"); + else if (sbi->alloc_mode == ALLOC_MODE_REUSE) + seq_printf(seq, ",alloc_mode=%s", "reuse"); return 0; } @@ -1273,6 +1296,7 @@ static void default_options(struct f2fs_sb_info *sbi) sbi->active_logs = NR_CURSEG_TYPE; sbi->inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS; sbi->whint_mode = WHINT_MODE_OFF; + sbi->alloc_mode = ALLOC_MODE_DEFAULT; set_opt(sbi, BG_GC); set_opt(sbi, INLINE_XATTR); @@ -1314,6 +1338,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) bool need_stop_gc = false; bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE); int old_whint_mode = sbi->whint_mode; + int old_alloc_mode = sbi->alloc_mode; #ifdef CONFIG_F2FS_FAULT_INJECTION struct f2fs_fault_info ffi = sbi->fault_info; #endif @@ -1463,6 +1488,7 @@ restore_opts: sbi->s_qf_names[i] = s_qf_names[i]; } #endif + sbi->alloc_mode = old_alloc_mode; sbi->whint_mode = old_whint_mode; sbi->mount_opt = org_mount_opt; sbi->active_logs = active_logs;