aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChao Yu <yuchao0@huawei.com>2021-03-31 17:46:48 +0800
committerChao Yu <yuchao0@huawei.com>2021-04-03 00:57:43 +0800
commitb03f6152e147ea188a45501b8de7eed54b997dc9 (patch)
treee6dc64648563642586a55eb459c57728fda2c427
parent268b38f2676d79b974693876c82c982b85eb371e (diff)
downloadlinux-dax-wip.tar.gz
f2fs: dax optionsdax-wip
Signed-off-by: Chao Yu <yuchao0@huawei.com>
-rw-r--r--fs/f2fs/f2fs.h25
-rw-r--r--fs/f2fs/file.c77
-rw-r--r--fs/f2fs/inode.c12
-rw-r--r--fs/f2fs/namei.c2
-rw-r--r--fs/f2fs/recovery.c3
-rw-r--r--fs/f2fs/super.c69
-rw-r--r--fs/f2fs/verity.c2
7 files changed, 173 insertions, 17 deletions
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 1fc7839a7f925..6ca41b222107a 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -152,6 +152,9 @@ struct f2fs_mount_info {
unsigned char compress_ext_cnt; /* extension count */
int compress_mode; /* compression mode */
unsigned char extensions[COMPRESS_EXT_NUM][F2FS_EXTENSION_LEN]; /* extensions */
+
+ /* For dax */
+ int dax_mode; /* dax mode */
};
#define F2FS_FEATURE_ENCRYPT 0x0001
@@ -1292,6 +1295,19 @@ enum {
*/
};
+enum {
+ DAX_MODE_NONE, /* no dax option */
+ DAX_MODE_ALWAYS, /* always set S_DAX ignore FS_XFLAG_DAX */
+ DAX_MODE_NEVER, /* never set S_DAX, ignore FS_XFLAG_DAX */
+ DAX_MODE_INODE, /* follow FS_XFLAG_DAX" and is the default */
+ DAX_MODE_LAGECY,
+ /*
+ * is a legacy option which is an alias for "dax=always".
+ * This may be removed in the future so "-o dax=always" is
+ * the preferred method for specifying this behavior.
+ */
+};
+
/*
* this value is set in page as a private data which indicate that
* the page is atomically written, and it is in inmem_pages list.
@@ -2655,11 +2671,13 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
#define F2FS_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */
#define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
#define F2FS_CASEFOLD_FL 0x40000000 /* Casefolded file */
+#define F2FS_DAX_FL 0x80000000 /* Dax file */
/* Flags that should be inherited by new inodes from their parent. */
#define F2FS_FL_INHERITED (F2FS_SYNC_FL | F2FS_NODUMP_FL | F2FS_NOATIME_FL | \
F2FS_DIRSYNC_FL | F2FS_PROJINHERIT_FL | \
- F2FS_CASEFOLD_FL | F2FS_COMPR_FL | F2FS_NOCOMP_FL)
+ F2FS_CASEFOLD_FL | F2FS_COMPR_FL | F2FS_NOCOMP_FL | \
+ F2FS_DAX_FL)
/* Flags that are appropriate for regular files (all but dir-specific ones). */
#define F2FS_REG_FLMASK (~(F2FS_DIRSYNC_FL | F2FS_PROJINHERIT_FL | \
@@ -3210,6 +3228,7 @@ int f2fs_getattr(struct user_namespace *mnt_userns, const struct path *path,
int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
struct iattr *attr);
int f2fs_truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
+bool f2fs_should_enable_dax(struct inode *inode);
void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count);
int f2fs_precache_extents(struct inode *inode);
long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
@@ -3220,7 +3239,7 @@ int f2fs_pin_file_control(struct inode *inode, bool inc);
/*
* inode.c
*/
-void f2fs_set_inode_flags(struct inode *inode);
+void f2fs_set_inode_flags(struct inode *inode, bool init);
bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page);
void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page);
struct inode *f2fs_iget(struct super_block *sb, unsigned long ino);
@@ -3923,7 +3942,7 @@ static inline void f2fs_set_encrypted_inode(struct inode *inode)
{
#ifdef CONFIG_FS_ENCRYPTION
file_set_encrypt(inode);
- f2fs_set_inode_flags(inode);
+ f2fs_set_inode_flags(inode, false);
#endif
}
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index bb87b9fafb790..6993a2a7a3b03 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1900,6 +1900,61 @@ static int f2fs_file_flush(struct file *file, fl_owner_t id)
return 0;
}
+static bool dax_mutually_exclusive(struct inode *inode)
+{
+ if (file_is_encrypt(inode))
+ return false;
+ if (file_is_verity(inode))
+ return false;
+ if (f2fs_has_inline_data(inode))
+ return true;
+ return false;
+}
+
+static bool dax_compatible(struct inode *inode, unsigned int iflags)
+{
+ if (!(iflags & F2FS_DAX_FL))
+ return true;
+ if (dax_mutually_exclusive(inode))
+ return false;
+ if (f2fs_verity_in_progress(inode))
+ return false;
+ return true;
+}
+
+bool f2fs_should_enable_dax(struct inode *inode)
+{
+ struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+ if (F2FS_OPTION(sbi).dax_mode == DAX_MODE_NEVER)
+ return false;
+ if (!S_ISREG(inode->i_mode))
+ return false;
+ if (dax_mutually_exclusive(inode))
+ return false;
+ if (F2FS_OPTION(sbi).dax_mode == DAX_MODE_ALWAYS ||
+ F2FS_OPTION(sbi).dax_mode == DAX_MODE_LAGECY)
+ return true;
+
+ return F2FS_I(inode)->i_flags & F2FS_DAX_FL;
+}
+
+static void dax_dontcache(struct inode *inode, unsigned int oldflags,
+ unsigned int iflags)
+{
+ struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+ if (S_ISDIR(inode->i_mode))
+ return;
+
+ if (F2FS_OPTION(sbi).dax_mode == DAX_MODE_ALWAYS ||
+ F2FS_OPTION(sbi).dax_mode == DAX_MODE_NEVER)
+ return;
+
+ if ((oldflags ^ iflags) & F2FS_DAX_FL)
+ d_mark_dontcache(inode);
+}
+
static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
@@ -1946,6 +2001,11 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
return -EINVAL;
}
+ if (!dax_compatible(inode, iflags))
+ return -EOPNOTSUPP;
+
+ dax_dontcache(inode, masked_flags, iflags);
+
fi->i_flags = iflags | (fi->i_flags & ~mask);
f2fs_bug_on(F2FS_I_SB(inode), (fi->i_flags & F2FS_COMPR_FL) &&
(fi->i_flags & F2FS_NOCOMP_FL));
@@ -1956,7 +2016,7 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
clear_inode_flag(inode, FI_PROJ_INHERIT);
inode->i_ctime = current_time(inode);
- f2fs_set_inode_flags(inode);
+ f2fs_set_inode_flags(inode, false);
f2fs_mark_inode_dirty_sync(inode, true);
return 0;
}
@@ -1985,6 +2045,7 @@ static const struct {
{ F2FS_DIRSYNC_FL, FS_DIRSYNC_FL },
{ F2FS_PROJINHERIT_FL, FS_PROJINHERIT_FL },
{ F2FS_CASEFOLD_FL, FS_CASEFOLD_FL },
+ { F2FS_DAX_FL, FS_DAX_FL },
};
#define F2FS_GETTABLE_FS_FL ( \
@@ -2002,7 +2063,8 @@ static const struct {
FS_INLINE_DATA_FL | \
FS_NOCOW_FL | \
FS_VERITY_FL | \
- FS_CASEFOLD_FL)
+ FS_CASEFOLD_FL | \
+ FS_DAX_FL)
#define F2FS_SETTABLE_FS_FL ( \
FS_COMPR_FL | \
@@ -2014,7 +2076,8 @@ static const struct {
FS_NOCOMP_FL | \
FS_DIRSYNC_FL | \
FS_PROJINHERIT_FL | \
- FS_CASEFOLD_FL)
+ FS_CASEFOLD_FL | \
+ FS_DAX_FL)
/* Convert f2fs on-disk i_flags to FS_IOC_{GET,SET}FLAGS flags */
static inline u32 f2fs_iflags_to_fsflags(u32 iflags)
@@ -3220,6 +3283,7 @@ static const struct {
{ F2FS_NODUMP_FL, FS_XFLAG_NODUMP },
{ F2FS_NOATIME_FL, FS_XFLAG_NOATIME },
{ F2FS_PROJINHERIT_FL, FS_XFLAG_PROJINHERIT },
+ { F2FS_DAX_FL, FS_XFLAG_DAX },
};
#define F2FS_SUPPORTED_XFLAGS ( \
@@ -3228,7 +3292,8 @@ static const struct {
FS_XFLAG_APPEND | \
FS_XFLAG_NODUMP | \
FS_XFLAG_NOATIME | \
- FS_XFLAG_PROJINHERIT)
+ FS_XFLAG_PROJINHERIT | \
+ FS_XFLAG_DAX)
/* Convert f2fs on-disk i_flags to FS_IOC_FS{GET,SET}XATTR flags */
static inline u32 f2fs_iflags_to_xflags(u32 iflags)
@@ -3670,7 +3735,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg)
goto out;
F2FS_I(inode)->i_flags |= F2FS_IMMUTABLE_FL;
- f2fs_set_inode_flags(inode);
+ f2fs_set_inode_flags(inode, false);
inode->i_ctime = current_time(inode);
f2fs_mark_inode_dirty_sync(inode, true);
@@ -3871,7 +3936,7 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg)
if (ret >= 0) {
F2FS_I(inode)->i_flags &= ~F2FS_IMMUTABLE_FL;
- f2fs_set_inode_flags(inode);
+ f2fs_set_inode_flags(inode, false);
inode->i_ctime = current_time(inode);
f2fs_mark_inode_dirty_sync(inode, true);
}
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 5d2253d53f179..cd3e8c7fdb3ab 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -29,11 +29,13 @@ void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync)
mark_inode_dirty_sync(inode);
}
-void f2fs_set_inode_flags(struct inode *inode)
+void f2fs_set_inode_flags(struct inode *inode, bool init)
{
unsigned int flags = F2FS_I(inode)->i_flags;
unsigned int new_fl = 0;
+ WARN_ON_ONCE(init && IS_DAX(inode));
+
if (flags & F2FS_SYNC_FL)
new_fl |= S_SYNC;
if (flags & F2FS_APPEND_FL)
@@ -44,6 +46,10 @@ void f2fs_set_inode_flags(struct inode *inode)
new_fl |= S_NOATIME;
if (flags & F2FS_DIRSYNC_FL)
new_fl |= S_DIRSYNC;
+ if (IS_DAX(inode))
+ new_fl |= S_DAX;
+ if (init && f2fs_should_enable_dax(inode))
+ new_fl |= S_DAX;
if (file_is_encrypt(inode))
new_fl |= S_ENCRYPTED;
if (file_is_verity(inode))
@@ -52,7 +58,7 @@ void f2fs_set_inode_flags(struct inode *inode)
new_fl |= S_CASEFOLD;
inode_set_flags(inode, new_fl,
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|
- S_ENCRYPTED|S_VERITY|S_CASEFOLD);
+ S_ENCRYPTED|S_VERITY|S_CASEFOLD|S_DAX);
}
static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
@@ -528,7 +534,7 @@ make_now:
ret = -EIO;
goto bad_inode;
}
- f2fs_set_inode_flags(inode);
+ f2fs_set_inode_flags(inode, true);
unlock_new_inode(inode);
trace_f2fs_iget(inode);
return inode;
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 17bd072a5d393..90c87228dfc27 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -129,7 +129,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
set_compress_context(inode);
}
- f2fs_set_inode_flags(inode);
+ f2fs_set_inode_flags(inode, true);
trace_f2fs_new_inode(inode, 0);
return inode;
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index da75d5d52f0ac..cd880f7355e31 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -319,7 +319,8 @@ static int recover_inode(struct inode *inode, struct page *page)
F2FS_I(inode)->i_advise = raw->i_advise;
F2FS_I(inode)->i_flags = le32_to_cpu(raw->i_flags);
- f2fs_set_inode_flags(inode);
+ inode->i_flags &= ~S_DAX;
+ f2fs_set_inode_flags(inode, true);
F2FS_I(inode)->i_gc_failures[GC_FAILURE_PIN] =
le16_to_cpu(raw->i_gc_failures);
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 5863344be60fd..15e01ab6351ea 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -156,6 +156,8 @@ enum {
Opt_atgc,
Opt_gc_merge,
Opt_nogc_merge,
+ Opt_dax,
+ Opt_dax_mode,
Opt_err,
};
@@ -230,6 +232,8 @@ static match_table_t f2fs_tokens = {
{Opt_atgc, "atgc"},
{Opt_gc_merge, "gc_merge"},
{Opt_nogc_merge, "nogc_merge"},
+ {Opt_dax, "dax"},
+ {Opt_dax_mode, "dax=%s"},
{Opt_err, NULL},
};
@@ -1086,6 +1090,35 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
case Opt_nogc_merge:
clear_opt(sbi, GC_MERGE);
break;
+ case Opt_dax:
+#ifdef CONFIG_FS_DAX
+ F2FS_OPTION(sbi).dax_mode = DAX_MODE_LAGECY;
+ break;
+#else
+ f2fs_info(sbi, "dax option not supported");
+ break;
+#endif
+ case Opt_dax_mode:
+#ifdef CONFIG_FS_DAX
+ name = match_strdup(&args[0]);
+ if (!name)
+ return -ENOMEM;
+ if (!strcmp(name, "always")) {
+ F2FS_OPTION(sbi).dax_mode = DAX_MODE_ALWAYS;
+ } else if (!strcmp(name, "never")) {
+ F2FS_OPTION(sbi).dax_mode = DAX_MODE_NEVER;
+ } else if (!strcmp(name, "inode")) {
+ F2FS_OPTION(sbi).dax_mode = DAX_MODE_INODE;
+ } else {
+ kfree(name);
+ return -EINVAL;
+ }
+ kfree(name);
+ break;
+#else
+ f2fs_info(sbi, "dax option not supported");
+ break;
+#endif
default:
f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
p);
@@ -1159,6 +1192,26 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
return -EINVAL;
}
+ if (test_opt(sbi, DISABLE_CHECKPOINT) &&
+ test_opt(sbi, MERGE_CHECKPOINT)) {
+ f2fs_err(sbi, "checkpoint=merge cannot be used with checkpoint=disable\n");
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_FS_DAX
+ if (F2FS_OPTION(sbi).dax_mode != DAX_MODE_NONE) {
+ if (test_opt(sbi, INLINE_DATA)) {
+ f2fs_err(sbi, "Dax can not be enabled with inline_data "
+ "mountoption\n");
+ return -EINVAL;
+ }
+ if (!bdev_dax_supported(sb->s_bdev, F2FS_BLKSIZE)) {
+ f2fs_warn(sbi, "DAX unsupported by block device. Turning off DAX.");
+ F2FS_OPTION(sbi).dax_mode = DAX_MODE_NONE;
+ }
+ }
+#endif
+
/* Not pass down write hints if the number of active logs is lesser
* than NR_CURSEG_PERSIST_TYPE.
*/
@@ -1818,6 +1871,17 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
if (test_opt(sbi, ATGC))
seq_puts(seq, ",atgc");
+
+#ifdef CONFIG_FS_DAX
+ if (F2FS_OPTION(sbi).dax_mode == DAX_MODE_ALWAYS)
+ seq_printf(seq, ",dax=%s", "always");
+ else if (F2FS_OPTION(sbi).dax_mode == DAX_MODE_NEVER)
+ seq_printf(seq, ",dax=%s", "never");
+ else if (F2FS_OPTION(sbi).dax_mode == DAX_MODE_INODE)
+ seq_printf(seq, ",dax=%s", "inode");
+ else if (F2FS_OPTION(sbi).dax_mode == DAX_MODE_LAGECY)
+ seq_puts(seq, ",dax");
+#endif
return 0;
}
@@ -1836,6 +1900,7 @@ static void default_options(struct f2fs_sb_info *sbi)
F2FS_OPTION(sbi).compress_ext_cnt = 0;
F2FS_OPTION(sbi).compress_mode = COMPR_MODE_FS;
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
+ F2FS_OPTION(sbi).dax_mode = DAX_MODE_NONE;
sbi->sb->s_flags &= ~SB_INLINECRYPT;
@@ -2479,7 +2544,7 @@ static int f2fs_quota_on(struct super_block *sb, int type, int format_id,
inode_lock(inode);
F2FS_I(inode)->i_flags |= F2FS_NOATIME_FL | F2FS_IMMUTABLE_FL;
- f2fs_set_inode_flags(inode);
+ f2fs_set_inode_flags(inode, false);
inode_unlock(inode);
f2fs_mark_inode_dirty_sync(inode, false);
@@ -2504,7 +2569,7 @@ static int __f2fs_quota_off(struct super_block *sb, int type)
inode_lock(inode);
F2FS_I(inode)->i_flags &= ~(F2FS_NOATIME_FL | F2FS_IMMUTABLE_FL);
- f2fs_set_inode_flags(inode);
+ f2fs_set_inode_flags(inode, false);
inode_unlock(inode);
f2fs_mark_inode_dirty_sync(inode, false);
out_put:
diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c
index 15ba36926fad7..784b14dc81935 100644
--- a/fs/f2fs/verity.c
+++ b/fs/f2fs/verity.c
@@ -192,7 +192,7 @@ static int f2fs_end_enable_verity(struct file *filp, const void *desc,
/* Finally, set the verity inode flag. */
file_set_verity(inode);
- f2fs_set_inode_flags(inode);
+ f2fs_set_inode_flags(inode, false);
f2fs_mark_inode_dirty_sync(inode, true);
clear_inode_flag(inode, FI_VERITY_IN_PROGRESS);