BACKPORT: FROMGIT: f2fs: add lookup_mode mount option
For casefolded directories, f2fs may fall back to a linear search if a hash-based lookup fails. This can cause severe performance regressions. While this behavior can be controlled by userspace tools (e.g. mkfs, fsck) by setting an on-disk flag, a kernel-level solution is needed to guarantee the lookup behavior regardless of the on-disk state. This commit introduces the 'lookup_mode' mount option to provide this kernel-side control. The option accepts three values: - perf: (Default) Enforces a hash-only lookup. The linear fallback is always disabled. - compat: Enables the linear search fallback for compatibility with directory entries from older kernels. - auto: Determines the mode based on the on-disk flag, preserving the userspace-based behavior. Bug: 432807936 (cherry picked from commit 632f0b6c3e32758e5c93d4e3c2860a3708b9853e https: //git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git dev) Link: https://lore.kernel.org/linux-f2fs-devel/20250805065228.1473089-1-chullee@google.com/ Change-Id: I51c4cb6eb40c8753c48f6e5de76e2edf24d20422 [chullee: adapted the mount option parsing to an older API] Signed-off-by: Daniel Lee <chullee@google.com> Reviewed-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
committed by
Paul Lawrence
parent
1ee9b67d67
commit
34d1a222d4
@@ -309,7 +309,25 @@ age_extent_cache Enable an age extent cache based on rb-tree. It records
|
||||
data block update frequency of the extent per inode, in
|
||||
order to provide better temperature hints for data block
|
||||
allocation.
|
||||
======================== ============================================================
|
||||
lookup_mode=%s Control the directory lookup behavior for casefolded
|
||||
directories. This option has no effect on directories
|
||||
that do not have the casefold feature enabled.
|
||||
|
||||
================== ========================================
|
||||
Value Description
|
||||
================== ========================================
|
||||
perf (Default) Enforces a hash-only lookup.
|
||||
The linear search fallback is always
|
||||
disabled, ignoring the on-disk flag.
|
||||
compat Enables the linear search fallback for
|
||||
compatibility with directory entries
|
||||
created by older kernel that used a
|
||||
different case-folding algorithm.
|
||||
This mode ignores the on-disk flag.
|
||||
auto F2FS determines the mode based on the
|
||||
on-disk `SB_ENC_NO_COMPAT_FALLBACK_FL`
|
||||
flag.
|
||||
================== ========================================
|
||||
|
||||
Debugfs Entries
|
||||
===============
|
||||
|
||||
@@ -16,6 +16,21 @@
|
||||
#include "xattr.h"
|
||||
#include <trace/events/f2fs.h>
|
||||
|
||||
static inline bool f2fs_should_fallback_to_linear(struct inode *dir)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
|
||||
switch (f2fs_get_lookup_mode(sbi)) {
|
||||
case LOOKUP_PERF:
|
||||
return false;
|
||||
case LOOKUP_COMPAT:
|
||||
return true;
|
||||
case LOOKUP_AUTO:
|
||||
return !sb_no_casefold_compat_fallback(sbi->sb);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
extern struct kmem_cache *f2fs_cf_name_slab;
|
||||
#endif
|
||||
@@ -438,7 +453,7 @@ start_find_entry:
|
||||
|
||||
out:
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
if (!sb_no_casefold_compat_fallback(dir->i_sb) &&
|
||||
if (f2fs_should_fallback_to_linear(dir) &&
|
||||
IS_CASEFOLDED(dir) && !de && use_hash) {
|
||||
use_hash = false;
|
||||
goto start_find_entry;
|
||||
|
||||
@@ -4553,6 +4553,47 @@ static inline bool is_journalled_quota(struct f2fs_sb_info *sbi)
|
||||
return false;
|
||||
}
|
||||
|
||||
enum f2fs_lookup_mode {
|
||||
LOOKUP_PERF,
|
||||
LOOKUP_COMPAT,
|
||||
LOOKUP_AUTO,
|
||||
};
|
||||
|
||||
/*
|
||||
* For bit-packing in f2fs_mount_info->alloc_mode
|
||||
*/
|
||||
#define ALLOC_MODE_BITS 1
|
||||
#define LOOKUP_MODE_BITS 2
|
||||
|
||||
#define ALLOC_MODE_SHIFT 0
|
||||
#define LOOKUP_MODE_SHIFT (ALLOC_MODE_SHIFT + ALLOC_MODE_BITS)
|
||||
|
||||
#define ALLOC_MODE_MASK (((1 << ALLOC_MODE_BITS) - 1) << ALLOC_MODE_SHIFT)
|
||||
#define LOOKUP_MODE_MASK (((1 << LOOKUP_MODE_BITS) - 1) << LOOKUP_MODE_SHIFT)
|
||||
|
||||
static inline int f2fs_get_alloc_mode(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return (F2FS_OPTION(sbi).alloc_mode & ALLOC_MODE_MASK) >> ALLOC_MODE_SHIFT;
|
||||
}
|
||||
|
||||
static inline void f2fs_set_alloc_mode(struct f2fs_sb_info *sbi, int mode)
|
||||
{
|
||||
F2FS_OPTION(sbi).alloc_mode &= ~ALLOC_MODE_MASK;
|
||||
F2FS_OPTION(sbi).alloc_mode |= (mode << ALLOC_MODE_SHIFT);
|
||||
}
|
||||
|
||||
static inline enum f2fs_lookup_mode f2fs_get_lookup_mode(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return (F2FS_OPTION(sbi).alloc_mode & LOOKUP_MODE_MASK) >> LOOKUP_MODE_SHIFT;
|
||||
}
|
||||
|
||||
static inline void f2fs_set_lookup_mode(struct f2fs_sb_info *sbi,
|
||||
enum f2fs_lookup_mode mode)
|
||||
{
|
||||
F2FS_OPTION(sbi).alloc_mode &= ~LOOKUP_MODE_MASK;
|
||||
F2FS_OPTION(sbi).alloc_mode |= (mode << LOOKUP_MODE_SHIFT);
|
||||
}
|
||||
|
||||
#define EFSBADCRC EBADMSG /* Bad CRC detected */
|
||||
#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */
|
||||
|
||||
|
||||
@@ -2621,7 +2621,7 @@ static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type)
|
||||
return SIT_I(sbi)->last_victim[ALLOC_NEXT];
|
||||
|
||||
/* find segments from 0 to reuse freed segments */
|
||||
if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_REUSE)
|
||||
if (f2fs_get_alloc_mode(sbi) == ALLOC_MODE_REUSE)
|
||||
return 0;
|
||||
|
||||
return curseg->segno;
|
||||
|
||||
@@ -156,6 +156,7 @@ enum {
|
||||
Opt_nogc_merge,
|
||||
Opt_memory_mode,
|
||||
Opt_age_extent_cache,
|
||||
Opt_lookup_mode,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
@@ -233,6 +234,7 @@ static match_table_t f2fs_tokens = {
|
||||
{Opt_nogc_merge, "nogc_merge"},
|
||||
{Opt_memory_mode, "memory=%s"},
|
||||
{Opt_age_extent_cache, "age_extent_cache"},
|
||||
{Opt_lookup_mode, "lookup_mode=%s"},
|
||||
{Opt_err, NULL},
|
||||
};
|
||||
|
||||
@@ -954,9 +956,9 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!strcmp(name, "default")) {
|
||||
F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
|
||||
f2fs_set_alloc_mode(sbi, ALLOC_MODE_DEFAULT);
|
||||
} else if (!strcmp(name, "reuse")) {
|
||||
F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_REUSE;
|
||||
f2fs_set_alloc_mode(sbi, ALLOC_MODE_REUSE);
|
||||
} else {
|
||||
kfree(name);
|
||||
return -EINVAL;
|
||||
@@ -1171,6 +1173,23 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
|
||||
}
|
||||
kfree(name);
|
||||
break;
|
||||
|
||||
case Opt_lookup_mode:
|
||||
name = match_strdup(&args[0]);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
if (!strcmp(name, "perf")) {
|
||||
f2fs_set_lookup_mode(sbi, LOOKUP_PERF);
|
||||
} else if (!strcmp(name, "compat")) {
|
||||
f2fs_set_lookup_mode(sbi, LOOKUP_COMPAT);
|
||||
} else if (!strcmp(name, "auto")) {
|
||||
f2fs_set_lookup_mode(sbi, LOOKUP_AUTO);
|
||||
} else {
|
||||
kfree(name);
|
||||
return -EINVAL;
|
||||
}
|
||||
kfree(name);
|
||||
break;
|
||||
default:
|
||||
f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
|
||||
p);
|
||||
@@ -1893,9 +1912,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
|
||||
if (sbi->sb->s_flags & SB_INLINECRYPT)
|
||||
seq_puts(seq, ",inlinecrypt");
|
||||
|
||||
if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_DEFAULT)
|
||||
if (f2fs_get_alloc_mode(sbi) == ALLOC_MODE_DEFAULT)
|
||||
seq_printf(seq, ",alloc_mode=%s", "default");
|
||||
else if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_REUSE)
|
||||
else if (f2fs_get_alloc_mode(sbi) == ALLOC_MODE_REUSE)
|
||||
seq_printf(seq, ",alloc_mode=%s", "reuse");
|
||||
|
||||
if (test_opt(sbi, DISABLE_CHECKPOINT))
|
||||
@@ -1924,6 +1943,13 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
|
||||
else if (F2FS_OPTION(sbi).memory_mode == MEMORY_MODE_LOW)
|
||||
seq_printf(seq, ",memory=%s", "low");
|
||||
|
||||
if (f2fs_get_lookup_mode(sbi) == LOOKUP_PERF)
|
||||
seq_show_option(seq, "lookup_mode", "perf");
|
||||
else if (f2fs_get_lookup_mode(sbi) == LOOKUP_COMPAT)
|
||||
seq_show_option(seq, "lookup_mode", "compat");
|
||||
else if (f2fs_get_lookup_mode(sbi) == LOOKUP_AUTO)
|
||||
seq_show_option(seq, "lookup_mode", "auto");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1946,6 +1972,11 @@ static void default_options(struct f2fs_sb_info *sbi, bool remount)
|
||||
F2FS_OPTION(sbi).inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS;
|
||||
F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF;
|
||||
F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
|
||||
if (le32_to_cpu(F2FS_RAW_SUPER(sbi)->segment_count_main) <=
|
||||
SMALL_VOLUME_SEGMENTS)
|
||||
f2fs_set_alloc_mode(sbi, ALLOC_MODE_REUSE);
|
||||
else
|
||||
f2fs_set_alloc_mode(sbi, ALLOC_MODE_DEFAULT);
|
||||
F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_POSIX;
|
||||
F2FS_OPTION(sbi).s_resuid = make_kuid(&init_user_ns, F2FS_DEF_RESUID);
|
||||
F2FS_OPTION(sbi).s_resgid = make_kgid(&init_user_ns, F2FS_DEF_RESGID);
|
||||
@@ -1977,6 +2008,8 @@ static void default_options(struct f2fs_sb_info *sbi, bool remount)
|
||||
#endif
|
||||
|
||||
f2fs_build_fault_attr(sbi, 0, 0);
|
||||
|
||||
f2fs_set_lookup_mode(sbi, LOOKUP_PERF);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
|
||||
Reference in New Issue
Block a user