f2fs: dax options

Signed-off-by: Chao Yu <yuchao0@huawei.com>
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 1fc7839..6ca41b2 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 bb87b9f..6993a2a 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 5d2253d..cd3e8c7 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 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
 		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 17bd072..90c8722 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 da75d5d..cd880f7 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 5863344..15e01ab 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 15ba369..784b14d 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);