All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] f2fs: add an ioctl to disable GC for specific file
@ 2017-12-08  5:15 Jaegeuk Kim
  2017-12-08 19:37 ` [PATCH v2] " Jaegeuk Kim
  0 siblings, 1 reply; 27+ messages in thread
From: Jaegeuk Kim @ 2017-12-08  5:15 UTC (permalink / raw)
  To: linux-kernel, linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch gives a flag to disable GC on given file, which would be useful, when
user wants to keep its block map.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/f2fs.h          | 17 ++++++++++++++++
 fs/f2fs/file.c          | 52 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/f2fs/gc.c            | 11 +++++++++++
 fs/f2fs/gc.h            |  2 ++
 fs/f2fs/sysfs.c         |  2 ++
 include/linux/f2fs_fs.h |  1 +
 6 files changed, 85 insertions(+)

diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 82f1dc345505..dd76cbf02791 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -375,6 +375,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
 #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
 #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
 
+#define F2FS_IOC_SET_DONTMOVE		_IO(F2FS_IOCTL_MAGIC, 13)
+
 struct f2fs_gc_range {
 	u32 sync;
 	u64 start;
@@ -1129,6 +1131,9 @@ struct f2fs_sb_info {
 	/* threshold for converting bg victims for fg */
 	u64 fggc_threshold;
 
+	/* threshold for dontmove gc trials */
+	u64 gc_dontmove;
+
 	/* maximum # of trials to find a victim segment for SSR and GC */
 	unsigned int max_victim_search;
 
@@ -2104,6 +2109,7 @@ enum {
 	FI_HOT_DATA,		/* indicate file is hot */
 	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
 	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
+	FI_DONTMOVE,		/* indicate file should not be gced */
 };
 
 static inline void __mark_inode_dirty_flag(struct inode *inode,
@@ -2117,6 +2123,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
 			return;
 	case FI_DATA_EXIST:
 	case FI_INLINE_DOTS:
+	case FI_DONTMOVE:
 		f2fs_mark_inode_dirty_sync(inode, true);
 	}
 }
@@ -2225,6 +2232,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
 		set_bit(FI_INLINE_DOTS, &fi->flags);
 	if (ri->i_inline & F2FS_EXTRA_ATTR)
 		set_bit(FI_EXTRA_ATTR, &fi->flags);
+	if (ri->i_inline & F2FS_DONTMOVE)
+		set_bit(FI_DONTMOVE, &fi->flags);
 }
 
 static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
@@ -2243,6 +2252,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
 		ri->i_inline |= F2FS_INLINE_DOTS;
 	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
 		ri->i_inline |= F2FS_EXTRA_ATTR;
+	if (is_inode_flag_set(inode, FI_DONTMOVE))
+		ri->i_inline |= F2FS_DONTMOVE;
 }
 
 static inline int f2fs_has_extra_attr(struct inode *inode)
@@ -2288,6 +2299,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
 	return is_inode_flag_set(inode, FI_INLINE_DOTS);
 }
 
+static inline bool f2fs_is_dontmove_file(struct inode *inode)
+{
+	return is_inode_flag_set(inode, FI_DONTMOVE);
+}
+
 static inline bool f2fs_is_atomic_file(struct inode *inode)
 {
 	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
@@ -2503,6 +2519,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
 void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
 long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+int f2fs_dontmove_control(struct inode *inode, bool inc);
 
 /*
  * inode.c
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 1db68f1bcd77..a383cdbf045f 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -2666,6 +2666,55 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
 	return 0;
 }
 
+int f2fs_dontmove_control(struct inode *inode, bool inc)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+	/* Use i_current_depth for normal file as a risk signal. */
+	if (inc)
+		f2fs_i_depth_write(inode, fi->i_current_depth + 1);
+
+	if (fi->i_current_depth > sbi->gc_dontmove) {
+		f2fs_msg(sbi->sb, KERN_WARNING,
+			"%s: Enable GC = ino %lx after %x GC trials\n",
+			__func__, inode->i_ino, fi->i_current_depth);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static int f2fs_ioc_set_dontmove(struct file *filp)
+{
+	struct inode *inode = file_inode(filp);
+	int ret;
+
+	if (!inode_owner_or_capable(inode))
+		return -EACCES;
+
+	if (!S_ISREG(inode->i_mode))
+		return -EINVAL;
+
+	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
+		return -EROFS;
+
+	inode_lock(inode);
+
+	ret = f2fs_convert_inline_inode(inode);
+	if (ret)
+		goto out;
+
+	if (f2fs_dontmove_control(inode, false))
+		return -EAGAIN;
+
+	set_inode_flag(inode, FI_DONTMOVE);
+	ret = F2FS_I(inode)->i_current_depth;
+	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
+out:
+	inode_unlock(inode);
+	return ret;
+}
+
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
@@ -2716,6 +2765,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		return f2fs_ioc_fsgetxattr(filp, arg);
 	case F2FS_IOC_FSSETXATTR:
 		return f2fs_ioc_fssetxattr(filp, arg);
+	case F2FS_IOC_SET_DONTMOVE:
+		return f2fs_ioc_set_dontmove(filp);
 	default:
 		return -ENOTTY;
 	}
@@ -2791,6 +2842,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case F2FS_IOC_GET_FEATURES:
 	case F2FS_IOC_FSGETXATTR:
 	case F2FS_IOC_FSSETXATTR:
+	case F2FS_IOC_SET_DONTMOVE:
 		break;
 	default:
 		return -ENOIOCTLCMD;
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 5d5bba462f26..263cb0b71ec8 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
 	if (f2fs_is_atomic_file(inode))
 		goto out;
 
+	if (f2fs_is_dontmove_file(inode)) {
+		f2fs_dontmove_control(inode, false);
+		goto out;
+	}
+
 	set_new_dnode(&dn, inode, NULL, NULL, 0);
 	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
 	if (err)
@@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
 
 	if (f2fs_is_atomic_file(inode))
 		goto out;
+	if (f2fs_is_dontmove_file(inode)) {
+		if (gc_type == FG_GC)
+			f2fs_dontmove_control(inode, false);
+		goto out;
+	}
 
 	if (gc_type == BG_GC) {
 		if (PageWriteback(page))
@@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
 
 	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
 				BLKS_PER_SEC(sbi), (main_count - resv_count));
+	sbi->gc_dontmove = DEF_GC_FAILED_DONTMOVE;
 
 	/* give warm/cold data area from slower device */
 	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
index 9325191fab2d..3fbb74253e52 100644
--- a/fs/f2fs/gc.h
+++ b/fs/f2fs/gc.h
@@ -20,6 +20,8 @@
 #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
 #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
 
+#define DEF_GC_FAILED_DONTMOVE		1024
+
 /* Search max. number of dirty segments to select a victim segment */
 #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
 
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index 93c3364250dd..57dd6660936a 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -300,6 +300,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
+F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_dontmove, gc_dontmove);
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
 F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
@@ -348,6 +349,7 @@ static struct attribute *f2fs_attrs[] = {
 	ATTR_LIST(idle_interval),
 	ATTR_LIST(iostat_enable),
 	ATTR_LIST(readdir_ra),
+	ATTR_LIST(gc_dontmove),
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 	ATTR_LIST(inject_rate),
 	ATTR_LIST(inject_type),
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 43e98d30d2df..fedb2698a8f7 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -212,6 +212,7 @@ struct f2fs_extent {
 #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
 #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
 #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
+#define F2FS_DONTMOVE		0x40	/* file should not be move */
 
 struct f2fs_inode {
 	__le16 i_mode;			/* file mode */
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] f2fs: add an ioctl to disable GC for specific file
  2017-12-08  5:15 [PATCH] f2fs: add an ioctl to disable GC for specific file Jaegeuk Kim
@ 2017-12-08 19:37 ` Jaegeuk Kim
  2017-12-12  9:52     ` Chao Yu
  2017-12-28  3:40     ` Jaegeuk Kim
  0 siblings, 2 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2017-12-08 19:37 UTC (permalink / raw)
  To: linux-kernel, linux-f2fs-devel

Change log from v1:
 - fix bug in error handling of ioctl 

>From b905e03d8aad7d25ecaf9bde05411a68d3d2460e Mon Sep 17 00:00:00 2001
From: Jaegeuk Kim <jaegeuk@kernel.org>
Date: Thu, 7 Dec 2017 16:25:39 -0800
Subject: [PATCH] f2fs: add an ioctl to disable GC for specific file

This patch gives a flag to disable GC on given file, which would be useful, when
user wants to keep its block map.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/f2fs.h          | 17 ++++++++++++++++
 fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/f2fs/gc.c            | 11 ++++++++++
 fs/f2fs/gc.h            |  2 ++
 fs/f2fs/sysfs.c         |  2 ++
 include/linux/f2fs_fs.h |  1 +
 6 files changed, 87 insertions(+)

diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 82f1dc345505..dd76cbf02791 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -375,6 +375,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
 #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
 #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
 
+#define F2FS_IOC_SET_DONTMOVE		_IO(F2FS_IOCTL_MAGIC, 13)
+
 struct f2fs_gc_range {
 	u32 sync;
 	u64 start;
@@ -1129,6 +1131,9 @@ struct f2fs_sb_info {
 	/* threshold for converting bg victims for fg */
 	u64 fggc_threshold;
 
+	/* threshold for dontmove gc trials */
+	u64 gc_dontmove;
+
 	/* maximum # of trials to find a victim segment for SSR and GC */
 	unsigned int max_victim_search;
 
@@ -2104,6 +2109,7 @@ enum {
 	FI_HOT_DATA,		/* indicate file is hot */
 	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
 	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
+	FI_DONTMOVE,		/* indicate file should not be gced */
 };
 
 static inline void __mark_inode_dirty_flag(struct inode *inode,
@@ -2117,6 +2123,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
 			return;
 	case FI_DATA_EXIST:
 	case FI_INLINE_DOTS:
+	case FI_DONTMOVE:
 		f2fs_mark_inode_dirty_sync(inode, true);
 	}
 }
@@ -2225,6 +2232,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
 		set_bit(FI_INLINE_DOTS, &fi->flags);
 	if (ri->i_inline & F2FS_EXTRA_ATTR)
 		set_bit(FI_EXTRA_ATTR, &fi->flags);
+	if (ri->i_inline & F2FS_DONTMOVE)
+		set_bit(FI_DONTMOVE, &fi->flags);
 }
 
 static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
@@ -2243,6 +2252,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
 		ri->i_inline |= F2FS_INLINE_DOTS;
 	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
 		ri->i_inline |= F2FS_EXTRA_ATTR;
+	if (is_inode_flag_set(inode, FI_DONTMOVE))
+		ri->i_inline |= F2FS_DONTMOVE;
 }
 
 static inline int f2fs_has_extra_attr(struct inode *inode)
@@ -2288,6 +2299,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
 	return is_inode_flag_set(inode, FI_INLINE_DOTS);
 }
 
+static inline bool f2fs_is_dontmove_file(struct inode *inode)
+{
+	return is_inode_flag_set(inode, FI_DONTMOVE);
+}
+
 static inline bool f2fs_is_atomic_file(struct inode *inode)
 {
 	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
@@ -2503,6 +2519,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
 void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
 long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+int f2fs_dontmove_control(struct inode *inode, bool inc);
 
 /*
  * inode.c
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 1db68f1bcd77..e232f8d91f6f 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
 	return 0;
 }
 
+int f2fs_dontmove_control(struct inode *inode, bool inc)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+	/* Use i_current_depth for normal file as a risk signal. */
+	if (inc)
+		f2fs_i_depth_write(inode, fi->i_current_depth + 1);
+
+	if (fi->i_current_depth > sbi->gc_dontmove) {
+		f2fs_msg(sbi->sb, KERN_WARNING,
+			"%s: Enable GC = ino %lx after %x GC trials\n",
+			__func__, inode->i_ino, fi->i_current_depth);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static int f2fs_ioc_set_dontmove(struct file *filp)
+{
+	struct inode *inode = file_inode(filp);
+	int ret;
+
+	if (!inode_owner_or_capable(inode))
+		return -EACCES;
+
+	if (!S_ISREG(inode->i_mode))
+		return -EINVAL;
+
+	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
+		return -EROFS;
+
+	inode_lock(inode);
+
+	if (f2fs_dontmove_control(inode, false)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	ret = f2fs_convert_inline_inode(inode);
+	if (ret)
+		goto out;
+
+	set_inode_flag(inode, FI_DONTMOVE);
+	ret = F2FS_I(inode)->i_current_depth;
+	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
+out:
+	inode_unlock(inode);
+	return ret;
+}
+
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
@@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		return f2fs_ioc_fsgetxattr(filp, arg);
 	case F2FS_IOC_FSSETXATTR:
 		return f2fs_ioc_fssetxattr(filp, arg);
+	case F2FS_IOC_SET_DONTMOVE:
+		return f2fs_ioc_set_dontmove(filp);
 	default:
 		return -ENOTTY;
 	}
@@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case F2FS_IOC_GET_FEATURES:
 	case F2FS_IOC_FSGETXATTR:
 	case F2FS_IOC_FSSETXATTR:
+	case F2FS_IOC_SET_DONTMOVE:
 		break;
 	default:
 		return -ENOIOCTLCMD;
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 5d5bba462f26..263cb0b71ec8 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
 	if (f2fs_is_atomic_file(inode))
 		goto out;
 
+	if (f2fs_is_dontmove_file(inode)) {
+		f2fs_dontmove_control(inode, false);
+		goto out;
+	}
+
 	set_new_dnode(&dn, inode, NULL, NULL, 0);
 	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
 	if (err)
@@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
 
 	if (f2fs_is_atomic_file(inode))
 		goto out;
+	if (f2fs_is_dontmove_file(inode)) {
+		if (gc_type == FG_GC)
+			f2fs_dontmove_control(inode, false);
+		goto out;
+	}
 
 	if (gc_type == BG_GC) {
 		if (PageWriteback(page))
@@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
 
 	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
 				BLKS_PER_SEC(sbi), (main_count - resv_count));
+	sbi->gc_dontmove = DEF_GC_FAILED_DONTMOVE;
 
 	/* give warm/cold data area from slower device */
 	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
index 9325191fab2d..3fbb74253e52 100644
--- a/fs/f2fs/gc.h
+++ b/fs/f2fs/gc.h
@@ -20,6 +20,8 @@
 #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
 #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
 
+#define DEF_GC_FAILED_DONTMOVE		1024
+
 /* Search max. number of dirty segments to select a victim segment */
 #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
 
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index 93c3364250dd..57dd6660936a 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -300,6 +300,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
+F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_dontmove, gc_dontmove);
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
 F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
@@ -348,6 +349,7 @@ static struct attribute *f2fs_attrs[] = {
 	ATTR_LIST(idle_interval),
 	ATTR_LIST(iostat_enable),
 	ATTR_LIST(readdir_ra),
+	ATTR_LIST(gc_dontmove),
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 	ATTR_LIST(inject_rate),
 	ATTR_LIST(inject_type),
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 43e98d30d2df..fedb2698a8f7 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -212,6 +212,7 @@ struct f2fs_extent {
 #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
 #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
 #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
+#define F2FS_DONTMOVE		0x40	/* file should not be move */
 
 struct f2fs_inode {
 	__le16 i_mode;			/* file mode */
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [f2fs-dev] [PATCH v2] f2fs: add an ioctl to disable GC for specific file
  2017-12-08 19:37 ` [PATCH v2] " Jaegeuk Kim
@ 2017-12-12  9:52     ` Chao Yu
  2017-12-28  3:40     ` Jaegeuk Kim
  1 sibling, 0 replies; 27+ messages in thread
From: Chao Yu @ 2017-12-12  9:52 UTC (permalink / raw)
  To: Jaegeuk Kim, linux-kernel, linux-f2fs-devel

Hi Jaegeuk,

On 2017/12/9 3:37, Jaegeuk Kim wrote:
> Change log from v1:
>  - fix bug in error handling of ioctl 
> 
>>From b905e03d8aad7d25ecaf9bde05411a68d3d2460e Mon Sep 17 00:00:00 2001
> From: Jaegeuk Kim <jaegeuk@kernel.org>
> Date: Thu, 7 Dec 2017 16:25:39 -0800
> Subject: [PATCH] f2fs: add an ioctl to disable GC for specific file
> 
> This patch gives a flag to disable GC on given file, which would be useful, when
> user wants to keep its block map.

Could you add some description about in which scenario userspace application
can use this ioctl, otherwise than android, other developers can get hint about
the usage of this interface, maybe later it can be used more wildly. ;)

Thanks,

> 
> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> ---
>  fs/f2fs/f2fs.h          | 17 ++++++++++++++++
>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/f2fs/gc.c            | 11 ++++++++++
>  fs/f2fs/gc.h            |  2 ++
>  fs/f2fs/sysfs.c         |  2 ++
>  include/linux/f2fs_fs.h |  1 +
>  6 files changed, 87 insertions(+)
> 
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index 82f1dc345505..dd76cbf02791 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -375,6 +375,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
>  
> +#define F2FS_IOC_SET_DONTMOVE		_IO(F2FS_IOCTL_MAGIC, 13)
> +
>  struct f2fs_gc_range {
>  	u32 sync;
>  	u64 start;
> @@ -1129,6 +1131,9 @@ struct f2fs_sb_info {
>  	/* threshold for converting bg victims for fg */
>  	u64 fggc_threshold;
>  
> +	/* threshold for dontmove gc trials */
> +	u64 gc_dontmove;
> +
>  	/* maximum # of trials to find a victim segment for SSR and GC */
>  	unsigned int max_victim_search;
>  
> @@ -2104,6 +2109,7 @@ enum {
>  	FI_HOT_DATA,		/* indicate file is hot */
>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> +	FI_DONTMOVE,		/* indicate file should not be gced */
>  };
>  
>  static inline void __mark_inode_dirty_flag(struct inode *inode,
> @@ -2117,6 +2123,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
>  			return;
>  	case FI_DATA_EXIST:
>  	case FI_INLINE_DOTS:
> +	case FI_DONTMOVE:
>  		f2fs_mark_inode_dirty_sync(inode, true);
>  	}
>  }
> @@ -2225,6 +2232,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
>  		set_bit(FI_INLINE_DOTS, &fi->flags);
>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> +	if (ri->i_inline & F2FS_DONTMOVE)
> +		set_bit(FI_DONTMOVE, &fi->flags);
>  }
>  
>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> @@ -2243,6 +2252,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>  		ri->i_inline |= F2FS_INLINE_DOTS;
>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
>  		ri->i_inline |= F2FS_EXTRA_ATTR;
> +	if (is_inode_flag_set(inode, FI_DONTMOVE))
> +		ri->i_inline |= F2FS_DONTMOVE;
>  }
>  
>  static inline int f2fs_has_extra_attr(struct inode *inode)
> @@ -2288,6 +2299,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
>  }
>  
> +static inline bool f2fs_is_dontmove_file(struct inode *inode)
> +{
> +	return is_inode_flag_set(inode, FI_DONTMOVE);
> +}
> +
>  static inline bool f2fs_is_atomic_file(struct inode *inode)
>  {
>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> @@ -2503,6 +2519,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> +int f2fs_dontmove_control(struct inode *inode, bool inc);
>  
>  /*
>   * inode.c
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 1db68f1bcd77..e232f8d91f6f 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
>  	return 0;
>  }
>  
> +int f2fs_dontmove_control(struct inode *inode, bool inc)
> +{
> +	struct f2fs_inode_info *fi = F2FS_I(inode);
> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> +
> +	/* Use i_current_depth for normal file as a risk signal. */
> +	if (inc)
> +		f2fs_i_depth_write(inode, fi->i_current_depth + 1);
> +
> +	if (fi->i_current_depth > sbi->gc_dontmove) {
> +		f2fs_msg(sbi->sb, KERN_WARNING,
> +			"%s: Enable GC = ino %lx after %x GC trials\n",
> +			__func__, inode->i_ino, fi->i_current_depth);
> +		return -EAGAIN;
> +	}
> +	return 0;
> +}
> +
> +static int f2fs_ioc_set_dontmove(struct file *filp)
> +{
> +	struct inode *inode = file_inode(filp);
> +	int ret;
> +
> +	if (!inode_owner_or_capable(inode))
> +		return -EACCES;
> +
> +	if (!S_ISREG(inode->i_mode))
> +		return -EINVAL;
> +
> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> +		return -EROFS;
> +
> +	inode_lock(inode);
> +
> +	if (f2fs_dontmove_control(inode, false)) {
> +		ret = -EAGAIN;
> +		goto out;
> +	}
> +
> +	ret = f2fs_convert_inline_inode(inode);
> +	if (ret)
> +		goto out;
> +
> +	set_inode_flag(inode, FI_DONTMOVE);
> +	ret = F2FS_I(inode)->i_current_depth;
> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> +out:
> +	inode_unlock(inode);
> +	return ret;
> +}
> +
>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  {
>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  		return f2fs_ioc_fsgetxattr(filp, arg);
>  	case F2FS_IOC_FSSETXATTR:
>  		return f2fs_ioc_fssetxattr(filp, arg);
> +	case F2FS_IOC_SET_DONTMOVE:
> +		return f2fs_ioc_set_dontmove(filp);
>  	default:
>  		return -ENOTTY;
>  	}
> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  	case F2FS_IOC_GET_FEATURES:
>  	case F2FS_IOC_FSGETXATTR:
>  	case F2FS_IOC_FSSETXATTR:
> +	case F2FS_IOC_SET_DONTMOVE:
>  		break;
>  	default:
>  		return -ENOIOCTLCMD;
> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> index 5d5bba462f26..263cb0b71ec8 100644
> --- a/fs/f2fs/gc.c
> +++ b/fs/f2fs/gc.c
> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
>  	if (f2fs_is_atomic_file(inode))
>  		goto out;
>  
> +	if (f2fs_is_dontmove_file(inode)) {
> +		f2fs_dontmove_control(inode, false);
> +		goto out;
> +	}
> +
>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
>  	if (err)
> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
>  
>  	if (f2fs_is_atomic_file(inode))
>  		goto out;
> +	if (f2fs_is_dontmove_file(inode)) {
> +		if (gc_type == FG_GC)
> +			f2fs_dontmove_control(inode, false);
> +		goto out;
> +	}
>  
>  	if (gc_type == BG_GC) {
>  		if (PageWriteback(page))
> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
>  
>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> +	sbi->gc_dontmove = DEF_GC_FAILED_DONTMOVE;
>  
>  	/* give warm/cold data area from slower device */
>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> index 9325191fab2d..3fbb74253e52 100644
> --- a/fs/f2fs/gc.h
> +++ b/fs/f2fs/gc.h
> @@ -20,6 +20,8 @@
>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
>  
> +#define DEF_GC_FAILED_DONTMOVE		1024
> +
>  /* Search max. number of dirty segments to select a victim segment */
>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
>  
> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> index 93c3364250dd..57dd6660936a 100644
> --- a/fs/f2fs/sysfs.c
> +++ b/fs/f2fs/sysfs.c
> @@ -300,6 +300,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_dontmove, gc_dontmove);
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> @@ -348,6 +349,7 @@ static struct attribute *f2fs_attrs[] = {
>  	ATTR_LIST(idle_interval),
>  	ATTR_LIST(iostat_enable),
>  	ATTR_LIST(readdir_ra),
> +	ATTR_LIST(gc_dontmove),
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  	ATTR_LIST(inject_rate),
>  	ATTR_LIST(inject_type),
> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> index 43e98d30d2df..fedb2698a8f7 100644
> --- a/include/linux/f2fs_fs.h
> +++ b/include/linux/f2fs_fs.h
> @@ -212,6 +212,7 @@ struct f2fs_extent {
>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> +#define F2FS_DONTMOVE		0x40	/* file should not be move */
>  
>  struct f2fs_inode {
>  	__le16 i_mode;			/* file mode */
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [f2fs-dev] [PATCH v2] f2fs: add an ioctl to disable GC for specific file
@ 2017-12-12  9:52     ` Chao Yu
  0 siblings, 0 replies; 27+ messages in thread
From: Chao Yu @ 2017-12-12  9:52 UTC (permalink / raw)
  To: Jaegeuk Kim, linux-kernel, linux-f2fs-devel

Hi Jaegeuk,

On 2017/12/9 3:37, Jaegeuk Kim wrote:
> Change log from v1:
>  - fix bug in error handling of ioctl 
> 
>>From b905e03d8aad7d25ecaf9bde05411a68d3d2460e Mon Sep 17 00:00:00 2001
> From: Jaegeuk Kim <jaegeuk@kernel.org>
> Date: Thu, 7 Dec 2017 16:25:39 -0800
> Subject: [PATCH] f2fs: add an ioctl to disable GC for specific file
> 
> This patch gives a flag to disable GC on given file, which would be useful, when
> user wants to keep its block map.

Could you add some description about in which scenario userspace application
can use this ioctl, otherwise than android, other developers can get hint about
the usage of this interface, maybe later it can be used more wildly. ;)

Thanks,

> 
> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> ---
>  fs/f2fs/f2fs.h          | 17 ++++++++++++++++
>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/f2fs/gc.c            | 11 ++++++++++
>  fs/f2fs/gc.h            |  2 ++
>  fs/f2fs/sysfs.c         |  2 ++
>  include/linux/f2fs_fs.h |  1 +
>  6 files changed, 87 insertions(+)
> 
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index 82f1dc345505..dd76cbf02791 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -375,6 +375,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
>  
> +#define F2FS_IOC_SET_DONTMOVE		_IO(F2FS_IOCTL_MAGIC, 13)
> +
>  struct f2fs_gc_range {
>  	u32 sync;
>  	u64 start;
> @@ -1129,6 +1131,9 @@ struct f2fs_sb_info {
>  	/* threshold for converting bg victims for fg */
>  	u64 fggc_threshold;
>  
> +	/* threshold for dontmove gc trials */
> +	u64 gc_dontmove;
> +
>  	/* maximum # of trials to find a victim segment for SSR and GC */
>  	unsigned int max_victim_search;
>  
> @@ -2104,6 +2109,7 @@ enum {
>  	FI_HOT_DATA,		/* indicate file is hot */
>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> +	FI_DONTMOVE,		/* indicate file should not be gced */
>  };
>  
>  static inline void __mark_inode_dirty_flag(struct inode *inode,
> @@ -2117,6 +2123,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
>  			return;
>  	case FI_DATA_EXIST:
>  	case FI_INLINE_DOTS:
> +	case FI_DONTMOVE:
>  		f2fs_mark_inode_dirty_sync(inode, true);
>  	}
>  }
> @@ -2225,6 +2232,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
>  		set_bit(FI_INLINE_DOTS, &fi->flags);
>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> +	if (ri->i_inline & F2FS_DONTMOVE)
> +		set_bit(FI_DONTMOVE, &fi->flags);
>  }
>  
>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> @@ -2243,6 +2252,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>  		ri->i_inline |= F2FS_INLINE_DOTS;
>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
>  		ri->i_inline |= F2FS_EXTRA_ATTR;
> +	if (is_inode_flag_set(inode, FI_DONTMOVE))
> +		ri->i_inline |= F2FS_DONTMOVE;
>  }
>  
>  static inline int f2fs_has_extra_attr(struct inode *inode)
> @@ -2288,6 +2299,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
>  }
>  
> +static inline bool f2fs_is_dontmove_file(struct inode *inode)
> +{
> +	return is_inode_flag_set(inode, FI_DONTMOVE);
> +}
> +
>  static inline bool f2fs_is_atomic_file(struct inode *inode)
>  {
>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> @@ -2503,6 +2519,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> +int f2fs_dontmove_control(struct inode *inode, bool inc);
>  
>  /*
>   * inode.c
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 1db68f1bcd77..e232f8d91f6f 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
>  	return 0;
>  }
>  
> +int f2fs_dontmove_control(struct inode *inode, bool inc)
> +{
> +	struct f2fs_inode_info *fi = F2FS_I(inode);
> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> +
> +	/* Use i_current_depth for normal file as a risk signal. */
> +	if (inc)
> +		f2fs_i_depth_write(inode, fi->i_current_depth + 1);
> +
> +	if (fi->i_current_depth > sbi->gc_dontmove) {
> +		f2fs_msg(sbi->sb, KERN_WARNING,
> +			"%s: Enable GC = ino %lx after %x GC trials\n",
> +			__func__, inode->i_ino, fi->i_current_depth);
> +		return -EAGAIN;
> +	}
> +	return 0;
> +}
> +
> +static int f2fs_ioc_set_dontmove(struct file *filp)
> +{
> +	struct inode *inode = file_inode(filp);
> +	int ret;
> +
> +	if (!inode_owner_or_capable(inode))
> +		return -EACCES;
> +
> +	if (!S_ISREG(inode->i_mode))
> +		return -EINVAL;
> +
> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> +		return -EROFS;
> +
> +	inode_lock(inode);
> +
> +	if (f2fs_dontmove_control(inode, false)) {
> +		ret = -EAGAIN;
> +		goto out;
> +	}
> +
> +	ret = f2fs_convert_inline_inode(inode);
> +	if (ret)
> +		goto out;
> +
> +	set_inode_flag(inode, FI_DONTMOVE);
> +	ret = F2FS_I(inode)->i_current_depth;
> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> +out:
> +	inode_unlock(inode);
> +	return ret;
> +}
> +
>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  {
>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  		return f2fs_ioc_fsgetxattr(filp, arg);
>  	case F2FS_IOC_FSSETXATTR:
>  		return f2fs_ioc_fssetxattr(filp, arg);
> +	case F2FS_IOC_SET_DONTMOVE:
> +		return f2fs_ioc_set_dontmove(filp);
>  	default:
>  		return -ENOTTY;
>  	}
> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  	case F2FS_IOC_GET_FEATURES:
>  	case F2FS_IOC_FSGETXATTR:
>  	case F2FS_IOC_FSSETXATTR:
> +	case F2FS_IOC_SET_DONTMOVE:
>  		break;
>  	default:
>  		return -ENOIOCTLCMD;
> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> index 5d5bba462f26..263cb0b71ec8 100644
> --- a/fs/f2fs/gc.c
> +++ b/fs/f2fs/gc.c
> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
>  	if (f2fs_is_atomic_file(inode))
>  		goto out;
>  
> +	if (f2fs_is_dontmove_file(inode)) {
> +		f2fs_dontmove_control(inode, false);
> +		goto out;
> +	}
> +
>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
>  	if (err)
> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
>  
>  	if (f2fs_is_atomic_file(inode))
>  		goto out;
> +	if (f2fs_is_dontmove_file(inode)) {
> +		if (gc_type == FG_GC)
> +			f2fs_dontmove_control(inode, false);
> +		goto out;
> +	}
>  
>  	if (gc_type == BG_GC) {
>  		if (PageWriteback(page))
> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
>  
>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> +	sbi->gc_dontmove = DEF_GC_FAILED_DONTMOVE;
>  
>  	/* give warm/cold data area from slower device */
>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> index 9325191fab2d..3fbb74253e52 100644
> --- a/fs/f2fs/gc.h
> +++ b/fs/f2fs/gc.h
> @@ -20,6 +20,8 @@
>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
>  
> +#define DEF_GC_FAILED_DONTMOVE		1024
> +
>  /* Search max. number of dirty segments to select a victim segment */
>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
>  
> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> index 93c3364250dd..57dd6660936a 100644
> --- a/fs/f2fs/sysfs.c
> +++ b/fs/f2fs/sysfs.c
> @@ -300,6 +300,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_dontmove, gc_dontmove);
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> @@ -348,6 +349,7 @@ static struct attribute *f2fs_attrs[] = {
>  	ATTR_LIST(idle_interval),
>  	ATTR_LIST(iostat_enable),
>  	ATTR_LIST(readdir_ra),
> +	ATTR_LIST(gc_dontmove),
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  	ATTR_LIST(inject_rate),
>  	ATTR_LIST(inject_type),
> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> index 43e98d30d2df..fedb2698a8f7 100644
> --- a/include/linux/f2fs_fs.h
> +++ b/include/linux/f2fs_fs.h
> @@ -212,6 +212,7 @@ struct f2fs_extent {
>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> +#define F2FS_DONTMOVE		0x40	/* file should not be move */
>  
>  struct f2fs_inode {
>  	__le16 i_mode;			/* file mode */
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [f2fs-dev] [PATCH v2] f2fs: add an ioctl to disable GC for specific file
  2017-12-12  9:52     ` Chao Yu
  (?)
@ 2017-12-14 19:50     ` Jaegeuk Kim
  2017-12-18 12:48         ` Chao Yu
  -1 siblings, 1 reply; 27+ messages in thread
From: Jaegeuk Kim @ 2017-12-14 19:50 UTC (permalink / raw)
  To: Chao Yu; +Cc: linux-kernel, linux-f2fs-devel

On 12/12, Chao Yu wrote:
> Hi Jaegeuk,
> 
> On 2017/12/9 3:37, Jaegeuk Kim wrote:
> > Change log from v1:
> >  - fix bug in error handling of ioctl 
> > 
> >>From b905e03d8aad7d25ecaf9bde05411a68d3d2460e Mon Sep 17 00:00:00 2001
> > From: Jaegeuk Kim <jaegeuk@kernel.org>
> > Date: Thu, 7 Dec 2017 16:25:39 -0800
> > Subject: [PATCH] f2fs: add an ioctl to disable GC for specific file
> > 
> > This patch gives a flag to disable GC on given file, which would be useful, when
> > user wants to keep its block map.
> 
> Could you add some description about in which scenario userspace application
> can use this ioctl, otherwise than android, other developers can get hint about
> the usage of this interface, maybe later it can be used more wildly. ;)

The usecase would be somewhat hacky tho, it looks like 1) building a block map
through fibmap, 2) storing the map in another partition, 3) using the map to
overwrite data contents directly by low-level tools. In that case, we have to
keep its block locations.

Thanks,

> 
> Thanks,
> 
> > 
> > Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> > ---
> >  fs/f2fs/f2fs.h          | 17 ++++++++++++++++
> >  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  fs/f2fs/gc.c            | 11 ++++++++++
> >  fs/f2fs/gc.h            |  2 ++
> >  fs/f2fs/sysfs.c         |  2 ++
> >  include/linux/f2fs_fs.h |  1 +
> >  6 files changed, 87 insertions(+)
> > 
> > diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> > index 82f1dc345505..dd76cbf02791 100644
> > --- a/fs/f2fs/f2fs.h
> > +++ b/fs/f2fs/f2fs.h
> > @@ -375,6 +375,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
> >  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
> >  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
> >  
> > +#define F2FS_IOC_SET_DONTMOVE		_IO(F2FS_IOCTL_MAGIC, 13)
> > +
> >  struct f2fs_gc_range {
> >  	u32 sync;
> >  	u64 start;
> > @@ -1129,6 +1131,9 @@ struct f2fs_sb_info {
> >  	/* threshold for converting bg victims for fg */
> >  	u64 fggc_threshold;
> >  
> > +	/* threshold for dontmove gc trials */
> > +	u64 gc_dontmove;
> > +
> >  	/* maximum # of trials to find a victim segment for SSR and GC */
> >  	unsigned int max_victim_search;
> >  
> > @@ -2104,6 +2109,7 @@ enum {
> >  	FI_HOT_DATA,		/* indicate file is hot */
> >  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
> >  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> > +	FI_DONTMOVE,		/* indicate file should not be gced */
> >  };
> >  
> >  static inline void __mark_inode_dirty_flag(struct inode *inode,
> > @@ -2117,6 +2123,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
> >  			return;
> >  	case FI_DATA_EXIST:
> >  	case FI_INLINE_DOTS:
> > +	case FI_DONTMOVE:
> >  		f2fs_mark_inode_dirty_sync(inode, true);
> >  	}
> >  }
> > @@ -2225,6 +2232,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
> >  		set_bit(FI_INLINE_DOTS, &fi->flags);
> >  	if (ri->i_inline & F2FS_EXTRA_ATTR)
> >  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> > +	if (ri->i_inline & F2FS_DONTMOVE)
> > +		set_bit(FI_DONTMOVE, &fi->flags);
> >  }
> >  
> >  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> > @@ -2243,6 +2252,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> >  		ri->i_inline |= F2FS_INLINE_DOTS;
> >  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
> >  		ri->i_inline |= F2FS_EXTRA_ATTR;
> > +	if (is_inode_flag_set(inode, FI_DONTMOVE))
> > +		ri->i_inline |= F2FS_DONTMOVE;
> >  }
> >  
> >  static inline int f2fs_has_extra_attr(struct inode *inode)
> > @@ -2288,6 +2299,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
> >  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
> >  }
> >  
> > +static inline bool f2fs_is_dontmove_file(struct inode *inode)
> > +{
> > +	return is_inode_flag_set(inode, FI_DONTMOVE);
> > +}
> > +
> >  static inline bool f2fs_is_atomic_file(struct inode *inode)
> >  {
> >  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> > @@ -2503,6 +2519,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
> >  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
> >  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
> >  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> > +int f2fs_dontmove_control(struct inode *inode, bool inc);
> >  
> >  /*
> >   * inode.c
> > diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> > index 1db68f1bcd77..e232f8d91f6f 100644
> > --- a/fs/f2fs/file.c
> > +++ b/fs/f2fs/file.c
> > @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
> >  	return 0;
> >  }
> >  
> > +int f2fs_dontmove_control(struct inode *inode, bool inc)
> > +{
> > +	struct f2fs_inode_info *fi = F2FS_I(inode);
> > +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> > +
> > +	/* Use i_current_depth for normal file as a risk signal. */
> > +	if (inc)
> > +		f2fs_i_depth_write(inode, fi->i_current_depth + 1);
> > +
> > +	if (fi->i_current_depth > sbi->gc_dontmove) {
> > +		f2fs_msg(sbi->sb, KERN_WARNING,
> > +			"%s: Enable GC = ino %lx after %x GC trials\n",
> > +			__func__, inode->i_ino, fi->i_current_depth);
> > +		return -EAGAIN;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int f2fs_ioc_set_dontmove(struct file *filp)
> > +{
> > +	struct inode *inode = file_inode(filp);
> > +	int ret;
> > +
> > +	if (!inode_owner_or_capable(inode))
> > +		return -EACCES;
> > +
> > +	if (!S_ISREG(inode->i_mode))
> > +		return -EINVAL;
> > +
> > +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> > +		return -EROFS;
> > +
> > +	inode_lock(inode);
> > +
> > +	if (f2fs_dontmove_control(inode, false)) {
> > +		ret = -EAGAIN;
> > +		goto out;
> > +	}
> > +
> > +	ret = f2fs_convert_inline_inode(inode);
> > +	if (ret)
> > +		goto out;
> > +
> > +	set_inode_flag(inode, FI_DONTMOVE);
> > +	ret = F2FS_I(inode)->i_current_depth;
> > +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> > +out:
> > +	inode_unlock(inode);
> > +	return ret;
> > +}
> > +
> >  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >  {
> >  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> > @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >  		return f2fs_ioc_fsgetxattr(filp, arg);
> >  	case F2FS_IOC_FSSETXATTR:
> >  		return f2fs_ioc_fssetxattr(filp, arg);
> > +	case F2FS_IOC_SET_DONTMOVE:
> > +		return f2fs_ioc_set_dontmove(filp);
> >  	default:
> >  		return -ENOTTY;
> >  	}
> > @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >  	case F2FS_IOC_GET_FEATURES:
> >  	case F2FS_IOC_FSGETXATTR:
> >  	case F2FS_IOC_FSSETXATTR:
> > +	case F2FS_IOC_SET_DONTMOVE:
> >  		break;
> >  	default:
> >  		return -ENOIOCTLCMD;
> > diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> > index 5d5bba462f26..263cb0b71ec8 100644
> > --- a/fs/f2fs/gc.c
> > +++ b/fs/f2fs/gc.c
> > @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
> >  	if (f2fs_is_atomic_file(inode))
> >  		goto out;
> >  
> > +	if (f2fs_is_dontmove_file(inode)) {
> > +		f2fs_dontmove_control(inode, false);
> > +		goto out;
> > +	}
> > +
> >  	set_new_dnode(&dn, inode, NULL, NULL, 0);
> >  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
> >  	if (err)
> > @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
> >  
> >  	if (f2fs_is_atomic_file(inode))
> >  		goto out;
> > +	if (f2fs_is_dontmove_file(inode)) {
> > +		if (gc_type == FG_GC)
> > +			f2fs_dontmove_control(inode, false);
> > +		goto out;
> > +	}
> >  
> >  	if (gc_type == BG_GC) {
> >  		if (PageWriteback(page))
> > @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
> >  
> >  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
> >  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> > +	sbi->gc_dontmove = DEF_GC_FAILED_DONTMOVE;
> >  
> >  	/* give warm/cold data area from slower device */
> >  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> > diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> > index 9325191fab2d..3fbb74253e52 100644
> > --- a/fs/f2fs/gc.h
> > +++ b/fs/f2fs/gc.h
> > @@ -20,6 +20,8 @@
> >  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
> >  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
> >  
> > +#define DEF_GC_FAILED_DONTMOVE		1024
> > +
> >  /* Search max. number of dirty segments to select a victim segment */
> >  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
> >  
> > diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> > index 93c3364250dd..57dd6660936a 100644
> > --- a/fs/f2fs/sysfs.c
> > +++ b/fs/f2fs/sysfs.c
> > @@ -300,6 +300,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
> >  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
> >  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
> >  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> > +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_dontmove, gc_dontmove);
> >  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
> >  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> > @@ -348,6 +349,7 @@ static struct attribute *f2fs_attrs[] = {
> >  	ATTR_LIST(idle_interval),
> >  	ATTR_LIST(iostat_enable),
> >  	ATTR_LIST(readdir_ra),
> > +	ATTR_LIST(gc_dontmove),
> >  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >  	ATTR_LIST(inject_rate),
> >  	ATTR_LIST(inject_type),
> > diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> > index 43e98d30d2df..fedb2698a8f7 100644
> > --- a/include/linux/f2fs_fs.h
> > +++ b/include/linux/f2fs_fs.h
> > @@ -212,6 +212,7 @@ struct f2fs_extent {
> >  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
> >  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
> >  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> > +#define F2FS_DONTMOVE		0x40	/* file should not be move */
> >  
> >  struct f2fs_inode {
> >  	__le16 i_mode;			/* file mode */
> > 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [f2fs-dev] [PATCH v2] f2fs: add an ioctl to disable GC for specific file
  2017-12-14 19:50     ` Jaegeuk Kim
@ 2017-12-18 12:48         ` Chao Yu
  0 siblings, 0 replies; 27+ messages in thread
From: Chao Yu @ 2017-12-18 12:48 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: linux-kernel, linux-f2fs-devel

On 2017/12/15 3:50, Jaegeuk Kim wrote:
> On 12/12, Chao Yu wrote:
>> Hi Jaegeuk,
>>
>> On 2017/12/9 3:37, Jaegeuk Kim wrote:
>>> Change log from v1:
>>>  - fix bug in error handling of ioctl 
>>>
>>> >From b905e03d8aad7d25ecaf9bde05411a68d3d2460e Mon Sep 17 00:00:00 2001
>>> From: Jaegeuk Kim <jaegeuk@kernel.org>
>>> Date: Thu, 7 Dec 2017 16:25:39 -0800
>>> Subject: [PATCH] f2fs: add an ioctl to disable GC for specific file
>>>
>>> This patch gives a flag to disable GC on given file, which would be useful, when
>>> user wants to keep its block map.
>>
>> Could you add some description about in which scenario userspace application
>> can use this ioctl, otherwise than android, other developers can get hint about
>> the usage of this interface, maybe later it can be used more wildly. ;)
> 
> The usecase would be somewhat hacky tho, it looks like 1) building a block map
> through fibmap, 2) storing the map in another partition, 3) using the map to
> overwrite data contents directly by low-level tools. In that case, we have to
> keep its block locations.

That's so hacky, it will be better to add simple description in comment log. ;)

> 
> Thanks,
> 
>>
>> Thanks,
>>
>>>
>>> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
>>> ---
>>>  fs/f2fs/f2fs.h          | 17 ++++++++++++++++
>>>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>  fs/f2fs/gc.c            | 11 ++++++++++
>>>  fs/f2fs/gc.h            |  2 ++
>>>  fs/f2fs/sysfs.c         |  2 ++
>>>  include/linux/f2fs_fs.h |  1 +
>>>  6 files changed, 87 insertions(+)
>>>
>>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>>> index 82f1dc345505..dd76cbf02791 100644
>>> --- a/fs/f2fs/f2fs.h
>>> +++ b/fs/f2fs/f2fs.h
>>> @@ -375,6 +375,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
>>>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
>>>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
>>>  
>>> +#define F2FS_IOC_SET_DONTMOVE		_IO(F2FS_IOCTL_MAGIC, 13)
>>> +
>>>  struct f2fs_gc_range {
>>>  	u32 sync;
>>>  	u64 start;
>>> @@ -1129,6 +1131,9 @@ struct f2fs_sb_info {
>>>  	/* threshold for converting bg victims for fg */
>>>  	u64 fggc_threshold;
>>>  
>>> +	/* threshold for dontmove gc trials */
>>> +	u64 gc_dontmove;
>>> +
>>>  	/* maximum # of trials to find a victim segment for SSR and GC */
>>>  	unsigned int max_victim_search;
>>>  
>>> @@ -2104,6 +2109,7 @@ enum {
>>>  	FI_HOT_DATA,		/* indicate file is hot */
>>>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
>>>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
>>> +	FI_DONTMOVE,		/* indicate file should not be gced */
>>>  };
>>>  
>>>  static inline void __mark_inode_dirty_flag(struct inode *inode,
>>> @@ -2117,6 +2123,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
>>>  			return;
>>>  	case FI_DATA_EXIST:
>>>  	case FI_INLINE_DOTS:
>>> +	case FI_DONTMOVE:
>>>  		f2fs_mark_inode_dirty_sync(inode, true);
>>>  	}
>>>  }
>>> @@ -2225,6 +2232,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
>>>  		set_bit(FI_INLINE_DOTS, &fi->flags);
>>>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
>>>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
>>> +	if (ri->i_inline & F2FS_DONTMOVE)
>>> +		set_bit(FI_DONTMOVE, &fi->flags);
>>>  }
>>>  
>>>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>>> @@ -2243,6 +2252,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>>>  		ri->i_inline |= F2FS_INLINE_DOTS;
>>>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
>>>  		ri->i_inline |= F2FS_EXTRA_ATTR;
>>> +	if (is_inode_flag_set(inode, FI_DONTMOVE))
>>> +		ri->i_inline |= F2FS_DONTMOVE;
>>>  }
>>>  
>>>  static inline int f2fs_has_extra_attr(struct inode *inode)
>>> @@ -2288,6 +2299,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
>>>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
>>>  }
>>>  
>>> +static inline bool f2fs_is_dontmove_file(struct inode *inode)
>>> +{
>>> +	return is_inode_flag_set(inode, FI_DONTMOVE);
>>> +}
>>> +
>>>  static inline bool f2fs_is_atomic_file(struct inode *inode)
>>>  {
>>>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
>>> @@ -2503,6 +2519,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
>>>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
>>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
>>>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
>>> +int f2fs_dontmove_control(struct inode *inode, bool inc);
>>>  
>>>  /*
>>>   * inode.c
>>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
>>> index 1db68f1bcd77..e232f8d91f6f 100644
>>> --- a/fs/f2fs/file.c
>>> +++ b/fs/f2fs/file.c
>>> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
>>>  	return 0;
>>>  }
>>>  
>>> +int f2fs_dontmove_control(struct inode *inode, bool inc)

What's the purpose here? If GC tries to move block belong to unremovable inode,
we don't allow to set other inode as unremovable one?

>>> +{
>>> +	struct f2fs_inode_info *fi = F2FS_I(inode);
>>> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>>> +
>>> +	/* Use i_current_depth for normal file as a risk signal. */
>>> +	if (inc)
>>> +		f2fs_i_depth_write(inode, fi->i_current_depth + 1);
>>> +
>>> +	if (fi->i_current_depth > sbi->gc_dontmove) {

If we want to persist per-inode metadata, what about adding new field in inode
layout to store such metadata?

Thanks,

>>> +		f2fs_msg(sbi->sb, KERN_WARNING,
>>> +			"%s: Enable GC = ino %lx after %x GC trials\n",
>>> +			__func__, inode->i_ino, fi->i_current_depth);
>>> +		return -EAGAIN;
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +static int f2fs_ioc_set_dontmove(struct file *filp)
>>> +{
>>> +	struct inode *inode = file_inode(filp);
>>> +	int ret;
>>> +
>>> +	if (!inode_owner_or_capable(inode))
>>> +		return -EACCES;
>>> +
>>> +	if (!S_ISREG(inode->i_mode))
>>> +		return -EINVAL;
>>> +
>>> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
>>> +		return -EROFS;
>>> +
>>> +	inode_lock(inode);
>>> +
>>> +	if (f2fs_dontmove_control(inode, false)) {
>>> +		ret = -EAGAIN;
>>> +		goto out;
>>> +	}
>>> +
>>> +	ret = f2fs_convert_inline_inode(inode);
>>> +	if (ret)
>>> +		goto out;
>>> +
>>> +	set_inode_flag(inode, FI_DONTMOVE);
>>> +	ret = F2FS_I(inode)->i_current_depth;
>>> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>>> +out:
>>> +	inode_unlock(inode);
>>> +	return ret;
>>> +}
>>> +
>>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>>  {
>>>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
>>> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>>  		return f2fs_ioc_fsgetxattr(filp, arg);
>>>  	case F2FS_IOC_FSSETXATTR:
>>>  		return f2fs_ioc_fssetxattr(filp, arg);
>>> +	case F2FS_IOC_SET_DONTMOVE:
>>> +		return f2fs_ioc_set_dontmove(filp);
>>>  	default:
>>>  		return -ENOTTY;
>>>  	}
>>> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>>  	case F2FS_IOC_GET_FEATURES:
>>>  	case F2FS_IOC_FSGETXATTR:
>>>  	case F2FS_IOC_FSSETXATTR:
>>> +	case F2FS_IOC_SET_DONTMOVE:
>>>  		break;
>>>  	default:
>>>  		return -ENOIOCTLCMD;
>>> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
>>> index 5d5bba462f26..263cb0b71ec8 100644
>>> --- a/fs/f2fs/gc.c
>>> +++ b/fs/f2fs/gc.c
>>> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
>>>  	if (f2fs_is_atomic_file(inode))
>>>  		goto out;
>>>  
>>> +	if (f2fs_is_dontmove_file(inode)) {
>>> +		f2fs_dontmove_control(inode, false);
>>> +		goto out;
>>> +	}
>>> +
>>>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
>>>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
>>>  	if (err)
>>> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
>>>  
>>>  	if (f2fs_is_atomic_file(inode))
>>>  		goto out;
>>> +	if (f2fs_is_dontmove_file(inode)) {
>>> +		if (gc_type == FG_GC)
>>> +			f2fs_dontmove_control(inode, false);
>>> +		goto out;
>>> +	}
>>>  
>>>  	if (gc_type == BG_GC) {
>>>  		if (PageWriteback(page))
>>> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
>>>  
>>>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
>>>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
>>> +	sbi->gc_dontmove = DEF_GC_FAILED_DONTMOVE;
>>>  
>>>  	/* give warm/cold data area from slower device */
>>>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
>>> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
>>> index 9325191fab2d..3fbb74253e52 100644
>>> --- a/fs/f2fs/gc.h
>>> +++ b/fs/f2fs/gc.h
>>> @@ -20,6 +20,8 @@
>>>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
>>>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
>>>  
>>> +#define DEF_GC_FAILED_DONTMOVE		1024
>>> +
>>>  /* Search max. number of dirty segments to select a victim segment */
>>>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
>>>  
>>> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
>>> index 93c3364250dd..57dd6660936a 100644
>>> --- a/fs/f2fs/sysfs.c
>>> +++ b/fs/f2fs/sysfs.c
>>> @@ -300,6 +300,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
>>> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_dontmove, gc_dontmove);
>>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
>>>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
>>> @@ -348,6 +349,7 @@ static struct attribute *f2fs_attrs[] = {
>>>  	ATTR_LIST(idle_interval),
>>>  	ATTR_LIST(iostat_enable),
>>>  	ATTR_LIST(readdir_ra),
>>> +	ATTR_LIST(gc_dontmove),
>>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>>  	ATTR_LIST(inject_rate),
>>>  	ATTR_LIST(inject_type),
>>> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
>>> index 43e98d30d2df..fedb2698a8f7 100644
>>> --- a/include/linux/f2fs_fs.h
>>> +++ b/include/linux/f2fs_fs.h
>>> @@ -212,6 +212,7 @@ struct f2fs_extent {
>>>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
>>>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
>>>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
>>> +#define F2FS_DONTMOVE		0x40	/* file should not be move */
>>>  
>>>  struct f2fs_inode {
>>>  	__le16 i_mode;			/* file mode */
>>>
> 
> .
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [f2fs-dev] [PATCH v2] f2fs: add an ioctl to disable GC for specific file
@ 2017-12-18 12:48         ` Chao Yu
  0 siblings, 0 replies; 27+ messages in thread
From: Chao Yu @ 2017-12-18 12:48 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: linux-kernel, linux-f2fs-devel

On 2017/12/15 3:50, Jaegeuk Kim wrote:
> On 12/12, Chao Yu wrote:
>> Hi Jaegeuk,
>>
>> On 2017/12/9 3:37, Jaegeuk Kim wrote:
>>> Change log from v1:
>>>  - fix bug in error handling of ioctl 
>>>
>>> >From b905e03d8aad7d25ecaf9bde05411a68d3d2460e Mon Sep 17 00:00:00 2001
>>> From: Jaegeuk Kim <jaegeuk@kernel.org>
>>> Date: Thu, 7 Dec 2017 16:25:39 -0800
>>> Subject: [PATCH] f2fs: add an ioctl to disable GC for specific file
>>>
>>> This patch gives a flag to disable GC on given file, which would be useful, when
>>> user wants to keep its block map.
>>
>> Could you add some description about in which scenario userspace application
>> can use this ioctl, otherwise than android, other developers can get hint about
>> the usage of this interface, maybe later it can be used more wildly. ;)
> 
> The usecase would be somewhat hacky tho, it looks like 1) building a block map
> through fibmap, 2) storing the map in another partition, 3) using the map to
> overwrite data contents directly by low-level tools. In that case, we have to
> keep its block locations.

That's so hacky, it will be better to add simple description in comment log. ;)

> 
> Thanks,
> 
>>
>> Thanks,
>>
>>>
>>> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
>>> ---
>>>  fs/f2fs/f2fs.h          | 17 ++++++++++++++++
>>>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>  fs/f2fs/gc.c            | 11 ++++++++++
>>>  fs/f2fs/gc.h            |  2 ++
>>>  fs/f2fs/sysfs.c         |  2 ++
>>>  include/linux/f2fs_fs.h |  1 +
>>>  6 files changed, 87 insertions(+)
>>>
>>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>>> index 82f1dc345505..dd76cbf02791 100644
>>> --- a/fs/f2fs/f2fs.h
>>> +++ b/fs/f2fs/f2fs.h
>>> @@ -375,6 +375,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
>>>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
>>>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
>>>  
>>> +#define F2FS_IOC_SET_DONTMOVE		_IO(F2FS_IOCTL_MAGIC, 13)
>>> +
>>>  struct f2fs_gc_range {
>>>  	u32 sync;
>>>  	u64 start;
>>> @@ -1129,6 +1131,9 @@ struct f2fs_sb_info {
>>>  	/* threshold for converting bg victims for fg */
>>>  	u64 fggc_threshold;
>>>  
>>> +	/* threshold for dontmove gc trials */
>>> +	u64 gc_dontmove;
>>> +
>>>  	/* maximum # of trials to find a victim segment for SSR and GC */
>>>  	unsigned int max_victim_search;
>>>  
>>> @@ -2104,6 +2109,7 @@ enum {
>>>  	FI_HOT_DATA,		/* indicate file is hot */
>>>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
>>>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
>>> +	FI_DONTMOVE,		/* indicate file should not be gced */
>>>  };
>>>  
>>>  static inline void __mark_inode_dirty_flag(struct inode *inode,
>>> @@ -2117,6 +2123,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
>>>  			return;
>>>  	case FI_DATA_EXIST:
>>>  	case FI_INLINE_DOTS:
>>> +	case FI_DONTMOVE:
>>>  		f2fs_mark_inode_dirty_sync(inode, true);
>>>  	}
>>>  }
>>> @@ -2225,6 +2232,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
>>>  		set_bit(FI_INLINE_DOTS, &fi->flags);
>>>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
>>>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
>>> +	if (ri->i_inline & F2FS_DONTMOVE)
>>> +		set_bit(FI_DONTMOVE, &fi->flags);
>>>  }
>>>  
>>>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>>> @@ -2243,6 +2252,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>>>  		ri->i_inline |= F2FS_INLINE_DOTS;
>>>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
>>>  		ri->i_inline |= F2FS_EXTRA_ATTR;
>>> +	if (is_inode_flag_set(inode, FI_DONTMOVE))
>>> +		ri->i_inline |= F2FS_DONTMOVE;
>>>  }
>>>  
>>>  static inline int f2fs_has_extra_attr(struct inode *inode)
>>> @@ -2288,6 +2299,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
>>>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
>>>  }
>>>  
>>> +static inline bool f2fs_is_dontmove_file(struct inode *inode)
>>> +{
>>> +	return is_inode_flag_set(inode, FI_DONTMOVE);
>>> +}
>>> +
>>>  static inline bool f2fs_is_atomic_file(struct inode *inode)
>>>  {
>>>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
>>> @@ -2503,6 +2519,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
>>>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
>>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
>>>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
>>> +int f2fs_dontmove_control(struct inode *inode, bool inc);
>>>  
>>>  /*
>>>   * inode.c
>>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
>>> index 1db68f1bcd77..e232f8d91f6f 100644
>>> --- a/fs/f2fs/file.c
>>> +++ b/fs/f2fs/file.c
>>> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
>>>  	return 0;
>>>  }
>>>  
>>> +int f2fs_dontmove_control(struct inode *inode, bool inc)

What's the purpose here? If GC tries to move block belong to unremovable inode,
we don't allow to set other inode as unremovable one?

>>> +{
>>> +	struct f2fs_inode_info *fi = F2FS_I(inode);
>>> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>>> +
>>> +	/* Use i_current_depth for normal file as a risk signal. */
>>> +	if (inc)
>>> +		f2fs_i_depth_write(inode, fi->i_current_depth + 1);
>>> +
>>> +	if (fi->i_current_depth > sbi->gc_dontmove) {

If we want to persist per-inode metadata, what about adding new field in inode
layout to store such metadata?

Thanks,

>>> +		f2fs_msg(sbi->sb, KERN_WARNING,
>>> +			"%s: Enable GC = ino %lx after %x GC trials\n",
>>> +			__func__, inode->i_ino, fi->i_current_depth);
>>> +		return -EAGAIN;
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +static int f2fs_ioc_set_dontmove(struct file *filp)
>>> +{
>>> +	struct inode *inode = file_inode(filp);
>>> +	int ret;
>>> +
>>> +	if (!inode_owner_or_capable(inode))
>>> +		return -EACCES;
>>> +
>>> +	if (!S_ISREG(inode->i_mode))
>>> +		return -EINVAL;
>>> +
>>> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
>>> +		return -EROFS;
>>> +
>>> +	inode_lock(inode);
>>> +
>>> +	if (f2fs_dontmove_control(inode, false)) {
>>> +		ret = -EAGAIN;
>>> +		goto out;
>>> +	}
>>> +
>>> +	ret = f2fs_convert_inline_inode(inode);
>>> +	if (ret)
>>> +		goto out;
>>> +
>>> +	set_inode_flag(inode, FI_DONTMOVE);
>>> +	ret = F2FS_I(inode)->i_current_depth;
>>> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>>> +out:
>>> +	inode_unlock(inode);
>>> +	return ret;
>>> +}
>>> +
>>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>>  {
>>>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
>>> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>>  		return f2fs_ioc_fsgetxattr(filp, arg);
>>>  	case F2FS_IOC_FSSETXATTR:
>>>  		return f2fs_ioc_fssetxattr(filp, arg);
>>> +	case F2FS_IOC_SET_DONTMOVE:
>>> +		return f2fs_ioc_set_dontmove(filp);
>>>  	default:
>>>  		return -ENOTTY;
>>>  	}
>>> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>>  	case F2FS_IOC_GET_FEATURES:
>>>  	case F2FS_IOC_FSGETXATTR:
>>>  	case F2FS_IOC_FSSETXATTR:
>>> +	case F2FS_IOC_SET_DONTMOVE:
>>>  		break;
>>>  	default:
>>>  		return -ENOIOCTLCMD;
>>> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
>>> index 5d5bba462f26..263cb0b71ec8 100644
>>> --- a/fs/f2fs/gc.c
>>> +++ b/fs/f2fs/gc.c
>>> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
>>>  	if (f2fs_is_atomic_file(inode))
>>>  		goto out;
>>>  
>>> +	if (f2fs_is_dontmove_file(inode)) {
>>> +		f2fs_dontmove_control(inode, false);
>>> +		goto out;
>>> +	}
>>> +
>>>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
>>>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
>>>  	if (err)
>>> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
>>>  
>>>  	if (f2fs_is_atomic_file(inode))
>>>  		goto out;
>>> +	if (f2fs_is_dontmove_file(inode)) {
>>> +		if (gc_type == FG_GC)
>>> +			f2fs_dontmove_control(inode, false);
>>> +		goto out;
>>> +	}
>>>  
>>>  	if (gc_type == BG_GC) {
>>>  		if (PageWriteback(page))
>>> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
>>>  
>>>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
>>>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
>>> +	sbi->gc_dontmove = DEF_GC_FAILED_DONTMOVE;
>>>  
>>>  	/* give warm/cold data area from slower device */
>>>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
>>> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
>>> index 9325191fab2d..3fbb74253e52 100644
>>> --- a/fs/f2fs/gc.h
>>> +++ b/fs/f2fs/gc.h
>>> @@ -20,6 +20,8 @@
>>>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
>>>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
>>>  
>>> +#define DEF_GC_FAILED_DONTMOVE		1024
>>> +
>>>  /* Search max. number of dirty segments to select a victim segment */
>>>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
>>>  
>>> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
>>> index 93c3364250dd..57dd6660936a 100644
>>> --- a/fs/f2fs/sysfs.c
>>> +++ b/fs/f2fs/sysfs.c
>>> @@ -300,6 +300,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
>>> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_dontmove, gc_dontmove);
>>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
>>>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
>>> @@ -348,6 +349,7 @@ static struct attribute *f2fs_attrs[] = {
>>>  	ATTR_LIST(idle_interval),
>>>  	ATTR_LIST(iostat_enable),
>>>  	ATTR_LIST(readdir_ra),
>>> +	ATTR_LIST(gc_dontmove),
>>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>>  	ATTR_LIST(inject_rate),
>>>  	ATTR_LIST(inject_type),
>>> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
>>> index 43e98d30d2df..fedb2698a8f7 100644
>>> --- a/include/linux/f2fs_fs.h
>>> +++ b/include/linux/f2fs_fs.h
>>> @@ -212,6 +212,7 @@ struct f2fs_extent {
>>>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
>>>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
>>>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
>>> +#define F2FS_DONTMOVE		0x40	/* file should not be move */
>>>  
>>>  struct f2fs_inode {
>>>  	__le16 i_mode;			/* file mode */
>>>
> 
> .
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [f2fs-dev] [PATCH v2] f2fs: add an ioctl to disable GC for specific file
  2017-12-18 12:48         ` Chao Yu
@ 2017-12-19 22:52           ` Jaegeuk Kim
  -1 siblings, 0 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2017-12-19 22:52 UTC (permalink / raw)
  To: Chao Yu; +Cc: linux-kernel, linux-f2fs-devel

On 12/18, Chao Yu wrote:
> On 2017/12/15 3:50, Jaegeuk Kim wrote:
> > On 12/12, Chao Yu wrote:
> >> Hi Jaegeuk,
> >>
> >> On 2017/12/9 3:37, Jaegeuk Kim wrote:
> >>> Change log from v1:
> >>>  - fix bug in error handling of ioctl 
> >>>
> >>> >From b905e03d8aad7d25ecaf9bde05411a68d3d2460e Mon Sep 17 00:00:00 2001
> >>> From: Jaegeuk Kim <jaegeuk@kernel.org>
> >>> Date: Thu, 7 Dec 2017 16:25:39 -0800
> >>> Subject: [PATCH] f2fs: add an ioctl to disable GC for specific file
> >>>
> >>> This patch gives a flag to disable GC on given file, which would be useful, when
> >>> user wants to keep its block map.
> >>
> >> Could you add some description about in which scenario userspace application
> >> can use this ioctl, otherwise than android, other developers can get hint about
> >> the usage of this interface, maybe later it can be used more wildly. ;)
> > 
> > The usecase would be somewhat hacky tho, it looks like 1) building a block map
> > through fibmap, 2) storing the map in another partition, 3) using the map to
> > overwrite data contents directly by low-level tools. In that case, we have to
> > keep its block locations.
> 
> That's so hacky, it will be better to add simple description in comment log. ;)

Actually, I don't want to add this kind of hacky example. :P

> >>> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> >>> ---
> >>>  fs/f2fs/f2fs.h          | 17 ++++++++++++++++
> >>>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
> >>>  fs/f2fs/gc.c            | 11 ++++++++++
> >>>  fs/f2fs/gc.h            |  2 ++
> >>>  fs/f2fs/sysfs.c         |  2 ++
> >>>  include/linux/f2fs_fs.h |  1 +
> >>>  6 files changed, 87 insertions(+)
> >>>
> >>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> >>> index 82f1dc345505..dd76cbf02791 100644
> >>> --- a/fs/f2fs/f2fs.h
> >>> +++ b/fs/f2fs/f2fs.h
> >>> @@ -375,6 +375,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
> >>>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
> >>>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
> >>>  
> >>> +#define F2FS_IOC_SET_DONTMOVE		_IO(F2FS_IOCTL_MAGIC, 13)
> >>> +
> >>>  struct f2fs_gc_range {
> >>>  	u32 sync;
> >>>  	u64 start;
> >>> @@ -1129,6 +1131,9 @@ struct f2fs_sb_info {
> >>>  	/* threshold for converting bg victims for fg */
> >>>  	u64 fggc_threshold;
> >>>  
> >>> +	/* threshold for dontmove gc trials */
> >>> +	u64 gc_dontmove;
> >>> +
> >>>  	/* maximum # of trials to find a victim segment for SSR and GC */
> >>>  	unsigned int max_victim_search;
> >>>  
> >>> @@ -2104,6 +2109,7 @@ enum {
> >>>  	FI_HOT_DATA,		/* indicate file is hot */
> >>>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
> >>>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> >>> +	FI_DONTMOVE,		/* indicate file should not be gced */
> >>>  };
> >>>  
> >>>  static inline void __mark_inode_dirty_flag(struct inode *inode,
> >>> @@ -2117,6 +2123,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
> >>>  			return;
> >>>  	case FI_DATA_EXIST:
> >>>  	case FI_INLINE_DOTS:
> >>> +	case FI_DONTMOVE:
> >>>  		f2fs_mark_inode_dirty_sync(inode, true);
> >>>  	}
> >>>  }
> >>> @@ -2225,6 +2232,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
> >>>  		set_bit(FI_INLINE_DOTS, &fi->flags);
> >>>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
> >>>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> >>> +	if (ri->i_inline & F2FS_DONTMOVE)
> >>> +		set_bit(FI_DONTMOVE, &fi->flags);
> >>>  }
> >>>  
> >>>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> >>> @@ -2243,6 +2252,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> >>>  		ri->i_inline |= F2FS_INLINE_DOTS;
> >>>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
> >>>  		ri->i_inline |= F2FS_EXTRA_ATTR;
> >>> +	if (is_inode_flag_set(inode, FI_DONTMOVE))
> >>> +		ri->i_inline |= F2FS_DONTMOVE;
> >>>  }
> >>>  
> >>>  static inline int f2fs_has_extra_attr(struct inode *inode)
> >>> @@ -2288,6 +2299,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
> >>>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
> >>>  }
> >>>  
> >>> +static inline bool f2fs_is_dontmove_file(struct inode *inode)
> >>> +{
> >>> +	return is_inode_flag_set(inode, FI_DONTMOVE);
> >>> +}
> >>> +
> >>>  static inline bool f2fs_is_atomic_file(struct inode *inode)
> >>>  {
> >>>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> >>> @@ -2503,6 +2519,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
> >>>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
> >>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
> >>>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> >>> +int f2fs_dontmove_control(struct inode *inode, bool inc);
> >>>  
> >>>  /*
> >>>   * inode.c
> >>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> >>> index 1db68f1bcd77..e232f8d91f6f 100644
> >>> --- a/fs/f2fs/file.c
> >>> +++ b/fs/f2fs/file.c
> >>> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
> >>>  	return 0;
> >>>  }
> >>>  
> >>> +int f2fs_dontmove_control(struct inode *inode, bool inc)
> 
> What's the purpose here? If GC tries to move block belong to unremovable inode,
> we don't allow to set other inode as unremovable one?

I just wanted to avoid infinite loop to move this during foreground GC case.

> 
> >>> +{
> >>> +	struct f2fs_inode_info *fi = F2FS_I(inode);
> >>> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> >>> +
> >>> +	/* Use i_current_depth for normal file as a risk signal. */
> >>> +	if (inc)
> >>> +		f2fs_i_depth_write(inode, fi->i_current_depth + 1);
> >>> +
> >>> +	if (fi->i_current_depth > sbi->gc_dontmove) {
> 
> If we want to persist per-inode metadata, what about adding new field in inode
> layout to store such metadata?

I thought that this would not be such the important field which makes us extend
inode fields.

Thanks,

> 
> Thanks,
> 
> >>> +		f2fs_msg(sbi->sb, KERN_WARNING,
> >>> +			"%s: Enable GC = ino %lx after %x GC trials\n",
> >>> +			__func__, inode->i_ino, fi->i_current_depth);
> >>> +		return -EAGAIN;
> >>> +	}
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static int f2fs_ioc_set_dontmove(struct file *filp)
> >>> +{
> >>> +	struct inode *inode = file_inode(filp);
> >>> +	int ret;
> >>> +
> >>> +	if (!inode_owner_or_capable(inode))
> >>> +		return -EACCES;
> >>> +
> >>> +	if (!S_ISREG(inode->i_mode))
> >>> +		return -EINVAL;
> >>> +
> >>> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> >>> +		return -EROFS;
> >>> +
> >>> +	inode_lock(inode);
> >>> +
> >>> +	if (f2fs_dontmove_control(inode, false)) {
> >>> +		ret = -EAGAIN;
> >>> +		goto out;
> >>> +	}
> >>> +
> >>> +	ret = f2fs_convert_inline_inode(inode);
> >>> +	if (ret)
> >>> +		goto out;
> >>> +
> >>> +	set_inode_flag(inode, FI_DONTMOVE);
> >>> +	ret = F2FS_I(inode)->i_current_depth;
> >>> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> >>> +out:
> >>> +	inode_unlock(inode);
> >>> +	return ret;
> >>> +}
> >>> +
> >>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >>>  {
> >>>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> >>> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >>>  		return f2fs_ioc_fsgetxattr(filp, arg);
> >>>  	case F2FS_IOC_FSSETXATTR:
> >>>  		return f2fs_ioc_fssetxattr(filp, arg);
> >>> +	case F2FS_IOC_SET_DONTMOVE:
> >>> +		return f2fs_ioc_set_dontmove(filp);
> >>>  	default:
> >>>  		return -ENOTTY;
> >>>  	}
> >>> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >>>  	case F2FS_IOC_GET_FEATURES:
> >>>  	case F2FS_IOC_FSGETXATTR:
> >>>  	case F2FS_IOC_FSSETXATTR:
> >>> +	case F2FS_IOC_SET_DONTMOVE:
> >>>  		break;
> >>>  	default:
> >>>  		return -ENOIOCTLCMD;
> >>> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> >>> index 5d5bba462f26..263cb0b71ec8 100644
> >>> --- a/fs/f2fs/gc.c
> >>> +++ b/fs/f2fs/gc.c
> >>> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
> >>>  	if (f2fs_is_atomic_file(inode))
> >>>  		goto out;
> >>>  
> >>> +	if (f2fs_is_dontmove_file(inode)) {
> >>> +		f2fs_dontmove_control(inode, false);
> >>> +		goto out;
> >>> +	}
> >>> +
> >>>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
> >>>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
> >>>  	if (err)
> >>> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
> >>>  
> >>>  	if (f2fs_is_atomic_file(inode))
> >>>  		goto out;
> >>> +	if (f2fs_is_dontmove_file(inode)) {
> >>> +		if (gc_type == FG_GC)
> >>> +			f2fs_dontmove_control(inode, false);
> >>> +		goto out;
> >>> +	}
> >>>  
> >>>  	if (gc_type == BG_GC) {
> >>>  		if (PageWriteback(page))
> >>> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
> >>>  
> >>>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
> >>>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> >>> +	sbi->gc_dontmove = DEF_GC_FAILED_DONTMOVE;
> >>>  
> >>>  	/* give warm/cold data area from slower device */
> >>>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> >>> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> >>> index 9325191fab2d..3fbb74253e52 100644
> >>> --- a/fs/f2fs/gc.h
> >>> +++ b/fs/f2fs/gc.h
> >>> @@ -20,6 +20,8 @@
> >>>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
> >>>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
> >>>  
> >>> +#define DEF_GC_FAILED_DONTMOVE		1024
> >>> +
> >>>  /* Search max. number of dirty segments to select a victim segment */
> >>>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
> >>>  
> >>> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> >>> index 93c3364250dd..57dd6660936a 100644
> >>> --- a/fs/f2fs/sysfs.c
> >>> +++ b/fs/f2fs/sysfs.c
> >>> @@ -300,6 +300,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
> >>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
> >>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
> >>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> >>> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_dontmove, gc_dontmove);
> >>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >>>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
> >>>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> >>> @@ -348,6 +349,7 @@ static struct attribute *f2fs_attrs[] = {
> >>>  	ATTR_LIST(idle_interval),
> >>>  	ATTR_LIST(iostat_enable),
> >>>  	ATTR_LIST(readdir_ra),
> >>> +	ATTR_LIST(gc_dontmove),
> >>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >>>  	ATTR_LIST(inject_rate),
> >>>  	ATTR_LIST(inject_type),
> >>> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> >>> index 43e98d30d2df..fedb2698a8f7 100644
> >>> --- a/include/linux/f2fs_fs.h
> >>> +++ b/include/linux/f2fs_fs.h
> >>> @@ -212,6 +212,7 @@ struct f2fs_extent {
> >>>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
> >>>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
> >>>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> >>> +#define F2FS_DONTMOVE		0x40	/* file should not be move */
> >>>  
> >>>  struct f2fs_inode {
> >>>  	__le16 i_mode;			/* file mode */
> >>>
> > 
> > .
> > 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] f2fs: add an ioctl to disable GC for specific file
@ 2017-12-19 22:52           ` Jaegeuk Kim
  0 siblings, 0 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2017-12-19 22:52 UTC (permalink / raw)
  To: Chao Yu; +Cc: linux-kernel, linux-f2fs-devel

On 12/18, Chao Yu wrote:
> On 2017/12/15 3:50, Jaegeuk Kim wrote:
> > On 12/12, Chao Yu wrote:
> >> Hi Jaegeuk,
> >>
> >> On 2017/12/9 3:37, Jaegeuk Kim wrote:
> >>> Change log from v1:
> >>>  - fix bug in error handling of ioctl 
> >>>
> >>> >From b905e03d8aad7d25ecaf9bde05411a68d3d2460e Mon Sep 17 00:00:00 2001
> >>> From: Jaegeuk Kim <jaegeuk@kernel.org>
> >>> Date: Thu, 7 Dec 2017 16:25:39 -0800
> >>> Subject: [PATCH] f2fs: add an ioctl to disable GC for specific file
> >>>
> >>> This patch gives a flag to disable GC on given file, which would be useful, when
> >>> user wants to keep its block map.
> >>
> >> Could you add some description about in which scenario userspace application
> >> can use this ioctl, otherwise than android, other developers can get hint about
> >> the usage of this interface, maybe later it can be used more wildly. ;)
> > 
> > The usecase would be somewhat hacky tho, it looks like 1) building a block map
> > through fibmap, 2) storing the map in another partition, 3) using the map to
> > overwrite data contents directly by low-level tools. In that case, we have to
> > keep its block locations.
> 
> That's so hacky, it will be better to add simple description in comment log. ;)

Actually, I don't want to add this kind of hacky example. :P

> >>> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> >>> ---
> >>>  fs/f2fs/f2fs.h          | 17 ++++++++++++++++
> >>>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
> >>>  fs/f2fs/gc.c            | 11 ++++++++++
> >>>  fs/f2fs/gc.h            |  2 ++
> >>>  fs/f2fs/sysfs.c         |  2 ++
> >>>  include/linux/f2fs_fs.h |  1 +
> >>>  6 files changed, 87 insertions(+)
> >>>
> >>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> >>> index 82f1dc345505..dd76cbf02791 100644
> >>> --- a/fs/f2fs/f2fs.h
> >>> +++ b/fs/f2fs/f2fs.h
> >>> @@ -375,6 +375,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
> >>>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
> >>>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
> >>>  
> >>> +#define F2FS_IOC_SET_DONTMOVE		_IO(F2FS_IOCTL_MAGIC, 13)
> >>> +
> >>>  struct f2fs_gc_range {
> >>>  	u32 sync;
> >>>  	u64 start;
> >>> @@ -1129,6 +1131,9 @@ struct f2fs_sb_info {
> >>>  	/* threshold for converting bg victims for fg */
> >>>  	u64 fggc_threshold;
> >>>  
> >>> +	/* threshold for dontmove gc trials */
> >>> +	u64 gc_dontmove;
> >>> +
> >>>  	/* maximum # of trials to find a victim segment for SSR and GC */
> >>>  	unsigned int max_victim_search;
> >>>  
> >>> @@ -2104,6 +2109,7 @@ enum {
> >>>  	FI_HOT_DATA,		/* indicate file is hot */
> >>>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
> >>>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> >>> +	FI_DONTMOVE,		/* indicate file should not be gced */
> >>>  };
> >>>  
> >>>  static inline void __mark_inode_dirty_flag(struct inode *inode,
> >>> @@ -2117,6 +2123,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
> >>>  			return;
> >>>  	case FI_DATA_EXIST:
> >>>  	case FI_INLINE_DOTS:
> >>> +	case FI_DONTMOVE:
> >>>  		f2fs_mark_inode_dirty_sync(inode, true);
> >>>  	}
> >>>  }
> >>> @@ -2225,6 +2232,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
> >>>  		set_bit(FI_INLINE_DOTS, &fi->flags);
> >>>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
> >>>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> >>> +	if (ri->i_inline & F2FS_DONTMOVE)
> >>> +		set_bit(FI_DONTMOVE, &fi->flags);
> >>>  }
> >>>  
> >>>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> >>> @@ -2243,6 +2252,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> >>>  		ri->i_inline |= F2FS_INLINE_DOTS;
> >>>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
> >>>  		ri->i_inline |= F2FS_EXTRA_ATTR;
> >>> +	if (is_inode_flag_set(inode, FI_DONTMOVE))
> >>> +		ri->i_inline |= F2FS_DONTMOVE;
> >>>  }
> >>>  
> >>>  static inline int f2fs_has_extra_attr(struct inode *inode)
> >>> @@ -2288,6 +2299,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
> >>>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
> >>>  }
> >>>  
> >>> +static inline bool f2fs_is_dontmove_file(struct inode *inode)
> >>> +{
> >>> +	return is_inode_flag_set(inode, FI_DONTMOVE);
> >>> +}
> >>> +
> >>>  static inline bool f2fs_is_atomic_file(struct inode *inode)
> >>>  {
> >>>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> >>> @@ -2503,6 +2519,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
> >>>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
> >>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
> >>>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> >>> +int f2fs_dontmove_control(struct inode *inode, bool inc);
> >>>  
> >>>  /*
> >>>   * inode.c
> >>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> >>> index 1db68f1bcd77..e232f8d91f6f 100644
> >>> --- a/fs/f2fs/file.c
> >>> +++ b/fs/f2fs/file.c
> >>> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
> >>>  	return 0;
> >>>  }
> >>>  
> >>> +int f2fs_dontmove_control(struct inode *inode, bool inc)
> 
> What's the purpose here? If GC tries to move block belong to unremovable inode,
> we don't allow to set other inode as unremovable one?

I just wanted to avoid infinite loop to move this during foreground GC case.

> 
> >>> +{
> >>> +	struct f2fs_inode_info *fi = F2FS_I(inode);
> >>> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> >>> +
> >>> +	/* Use i_current_depth for normal file as a risk signal. */
> >>> +	if (inc)
> >>> +		f2fs_i_depth_write(inode, fi->i_current_depth + 1);
> >>> +
> >>> +	if (fi->i_current_depth > sbi->gc_dontmove) {
> 
> If we want to persist per-inode metadata, what about adding new field in inode
> layout to store such metadata?

I thought that this would not be such the important field which makes us extend
inode fields.

Thanks,

> 
> Thanks,
> 
> >>> +		f2fs_msg(sbi->sb, KERN_WARNING,
> >>> +			"%s: Enable GC = ino %lx after %x GC trials\n",
> >>> +			__func__, inode->i_ino, fi->i_current_depth);
> >>> +		return -EAGAIN;
> >>> +	}
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static int f2fs_ioc_set_dontmove(struct file *filp)
> >>> +{
> >>> +	struct inode *inode = file_inode(filp);
> >>> +	int ret;
> >>> +
> >>> +	if (!inode_owner_or_capable(inode))
> >>> +		return -EACCES;
> >>> +
> >>> +	if (!S_ISREG(inode->i_mode))
> >>> +		return -EINVAL;
> >>> +
> >>> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> >>> +		return -EROFS;
> >>> +
> >>> +	inode_lock(inode);
> >>> +
> >>> +	if (f2fs_dontmove_control(inode, false)) {
> >>> +		ret = -EAGAIN;
> >>> +		goto out;
> >>> +	}
> >>> +
> >>> +	ret = f2fs_convert_inline_inode(inode);
> >>> +	if (ret)
> >>> +		goto out;
> >>> +
> >>> +	set_inode_flag(inode, FI_DONTMOVE);
> >>> +	ret = F2FS_I(inode)->i_current_depth;
> >>> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> >>> +out:
> >>> +	inode_unlock(inode);
> >>> +	return ret;
> >>> +}
> >>> +
> >>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >>>  {
> >>>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> >>> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >>>  		return f2fs_ioc_fsgetxattr(filp, arg);
> >>>  	case F2FS_IOC_FSSETXATTR:
> >>>  		return f2fs_ioc_fssetxattr(filp, arg);
> >>> +	case F2FS_IOC_SET_DONTMOVE:
> >>> +		return f2fs_ioc_set_dontmove(filp);
> >>>  	default:
> >>>  		return -ENOTTY;
> >>>  	}
> >>> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >>>  	case F2FS_IOC_GET_FEATURES:
> >>>  	case F2FS_IOC_FSGETXATTR:
> >>>  	case F2FS_IOC_FSSETXATTR:
> >>> +	case F2FS_IOC_SET_DONTMOVE:
> >>>  		break;
> >>>  	default:
> >>>  		return -ENOIOCTLCMD;
> >>> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> >>> index 5d5bba462f26..263cb0b71ec8 100644
> >>> --- a/fs/f2fs/gc.c
> >>> +++ b/fs/f2fs/gc.c
> >>> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
> >>>  	if (f2fs_is_atomic_file(inode))
> >>>  		goto out;
> >>>  
> >>> +	if (f2fs_is_dontmove_file(inode)) {
> >>> +		f2fs_dontmove_control(inode, false);
> >>> +		goto out;
> >>> +	}
> >>> +
> >>>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
> >>>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
> >>>  	if (err)
> >>> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
> >>>  
> >>>  	if (f2fs_is_atomic_file(inode))
> >>>  		goto out;
> >>> +	if (f2fs_is_dontmove_file(inode)) {
> >>> +		if (gc_type == FG_GC)
> >>> +			f2fs_dontmove_control(inode, false);
> >>> +		goto out;
> >>> +	}
> >>>  
> >>>  	if (gc_type == BG_GC) {
> >>>  		if (PageWriteback(page))
> >>> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
> >>>  
> >>>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
> >>>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> >>> +	sbi->gc_dontmove = DEF_GC_FAILED_DONTMOVE;
> >>>  
> >>>  	/* give warm/cold data area from slower device */
> >>>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> >>> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> >>> index 9325191fab2d..3fbb74253e52 100644
> >>> --- a/fs/f2fs/gc.h
> >>> +++ b/fs/f2fs/gc.h
> >>> @@ -20,6 +20,8 @@
> >>>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
> >>>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
> >>>  
> >>> +#define DEF_GC_FAILED_DONTMOVE		1024
> >>> +
> >>>  /* Search max. number of dirty segments to select a victim segment */
> >>>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
> >>>  
> >>> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> >>> index 93c3364250dd..57dd6660936a 100644
> >>> --- a/fs/f2fs/sysfs.c
> >>> +++ b/fs/f2fs/sysfs.c
> >>> @@ -300,6 +300,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
> >>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
> >>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
> >>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> >>> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_dontmove, gc_dontmove);
> >>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >>>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
> >>>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> >>> @@ -348,6 +349,7 @@ static struct attribute *f2fs_attrs[] = {
> >>>  	ATTR_LIST(idle_interval),
> >>>  	ATTR_LIST(iostat_enable),
> >>>  	ATTR_LIST(readdir_ra),
> >>> +	ATTR_LIST(gc_dontmove),
> >>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >>>  	ATTR_LIST(inject_rate),
> >>>  	ATTR_LIST(inject_type),
> >>> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> >>> index 43e98d30d2df..fedb2698a8f7 100644
> >>> --- a/include/linux/f2fs_fs.h
> >>> +++ b/include/linux/f2fs_fs.h
> >>> @@ -212,6 +212,7 @@ struct f2fs_extent {
> >>>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
> >>>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
> >>>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> >>> +#define F2FS_DONTMOVE		0x40	/* file should not be move */
> >>>  
> >>>  struct f2fs_inode {
> >>>  	__le16 i_mode;			/* file mode */
> >>>
> > 
> > .
> > 

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [f2fs-dev] [PATCH v2] f2fs: add an ioctl to disable GC for specific file
  2017-12-19 22:52           ` Jaegeuk Kim
@ 2017-12-25  2:52             ` Chao Yu
  -1 siblings, 0 replies; 27+ messages in thread
From: Chao Yu @ 2017-12-25  2:52 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: linux-kernel, linux-f2fs-devel

On 2017/12/20 6:52, Jaegeuk Kim wrote:
> On 12/18, Chao Yu wrote:
>> On 2017/12/15 3:50, Jaegeuk Kim wrote:
>>> On 12/12, Chao Yu wrote:
>>>> Hi Jaegeuk,
>>>>
>>>> On 2017/12/9 3:37, Jaegeuk Kim wrote:
>>>>> Change log from v1:
>>>>>  - fix bug in error handling of ioctl 
>>>>>
>>>>> >From b905e03d8aad7d25ecaf9bde05411a68d3d2460e Mon Sep 17 00:00:00 2001
>>>>> From: Jaegeuk Kim <jaegeuk@kernel.org>
>>>>> Date: Thu, 7 Dec 2017 16:25:39 -0800
>>>>> Subject: [PATCH] f2fs: add an ioctl to disable GC for specific file
>>>>>
>>>>> This patch gives a flag to disable GC on given file, which would be useful, when
>>>>> user wants to keep its block map.
>>>>
>>>> Could you add some description about in which scenario userspace application
>>>> can use this ioctl, otherwise than android, other developers can get hint about
>>>> the usage of this interface, maybe later it can be used more wildly. ;)
>>>
>>> The usecase would be somewhat hacky tho, it looks like 1) building a block map
>>> through fibmap, 2) storing the map in another partition, 3) using the map to
>>> overwrite data contents directly by low-level tools. In that case, we have to
>>> keep its block locations.
>>
>> That's so hacky, it will be better to add simple description in comment log. ;)
> 
> Actually, I don't want to add this kind of hacky example. :P

No a big deal, anyway, I've know your purpose. ;)

> 
>>>>> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
>>>>> ---
>>>>>  fs/f2fs/f2fs.h          | 17 ++++++++++++++++
>>>>>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>>>  fs/f2fs/gc.c            | 11 ++++++++++
>>>>>  fs/f2fs/gc.h            |  2 ++
>>>>>  fs/f2fs/sysfs.c         |  2 ++
>>>>>  include/linux/f2fs_fs.h |  1 +
>>>>>  6 files changed, 87 insertions(+)
>>>>>
>>>>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>>>>> index 82f1dc345505..dd76cbf02791 100644
>>>>> --- a/fs/f2fs/f2fs.h
>>>>> +++ b/fs/f2fs/f2fs.h
>>>>> @@ -375,6 +375,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
>>>>>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
>>>>>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
>>>>>  
>>>>> +#define F2FS_IOC_SET_DONTMOVE		_IO(F2FS_IOCTL_MAGIC, 13)
>>>>> +
>>>>>  struct f2fs_gc_range {
>>>>>  	u32 sync;
>>>>>  	u64 start;
>>>>> @@ -1129,6 +1131,9 @@ struct f2fs_sb_info {
>>>>>  	/* threshold for converting bg victims for fg */
>>>>>  	u64 fggc_threshold;
>>>>>  
>>>>> +	/* threshold for dontmove gc trials */
>>>>> +	u64 gc_dontmove;
>>>>> +
>>>>>  	/* maximum # of trials to find a victim segment for SSR and GC */
>>>>>  	unsigned int max_victim_search;
>>>>>  
>>>>> @@ -2104,6 +2109,7 @@ enum {
>>>>>  	FI_HOT_DATA,		/* indicate file is hot */
>>>>>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
>>>>>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
>>>>> +	FI_DONTMOVE,		/* indicate file should not be gced */
>>>>>  };
>>>>>  
>>>>>  static inline void __mark_inode_dirty_flag(struct inode *inode,
>>>>> @@ -2117,6 +2123,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
>>>>>  			return;
>>>>>  	case FI_DATA_EXIST:
>>>>>  	case FI_INLINE_DOTS:
>>>>> +	case FI_DONTMOVE:
>>>>>  		f2fs_mark_inode_dirty_sync(inode, true);
>>>>>  	}
>>>>>  }
>>>>> @@ -2225,6 +2232,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
>>>>>  		set_bit(FI_INLINE_DOTS, &fi->flags);
>>>>>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
>>>>>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
>>>>> +	if (ri->i_inline & F2FS_DONTMOVE)
>>>>> +		set_bit(FI_DONTMOVE, &fi->flags);
>>>>>  }
>>>>>  
>>>>>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>>>>> @@ -2243,6 +2252,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>>>>>  		ri->i_inline |= F2FS_INLINE_DOTS;
>>>>>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
>>>>>  		ri->i_inline |= F2FS_EXTRA_ATTR;
>>>>> +	if (is_inode_flag_set(inode, FI_DONTMOVE))
>>>>> +		ri->i_inline |= F2FS_DONTMOVE;
>>>>>  }
>>>>>  
>>>>>  static inline int f2fs_has_extra_attr(struct inode *inode)
>>>>> @@ -2288,6 +2299,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
>>>>>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
>>>>>  }
>>>>>  
>>>>> +static inline bool f2fs_is_dontmove_file(struct inode *inode)
>>>>> +{
>>>>> +	return is_inode_flag_set(inode, FI_DONTMOVE);
>>>>> +}
>>>>> +
>>>>>  static inline bool f2fs_is_atomic_file(struct inode *inode)
>>>>>  {
>>>>>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
>>>>> @@ -2503,6 +2519,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
>>>>>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
>>>>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
>>>>>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
>>>>> +int f2fs_dontmove_control(struct inode *inode, bool inc);
>>>>>  
>>>>>  /*
>>>>>   * inode.c
>>>>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
>>>>> index 1db68f1bcd77..e232f8d91f6f 100644
>>>>> --- a/fs/f2fs/file.c
>>>>> +++ b/fs/f2fs/file.c
>>>>> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
>>>>>  	return 0;
>>>>>  }
>>>>>  
>>>>> +int f2fs_dontmove_control(struct inode *inode, bool inc)
>>
>> What's the purpose here? If GC tries to move block belong to unremovable inode,
>> we don't allow to set other inode as unremovable one?
> 
> I just wanted to avoid infinite loop to move this during foreground GC case.

If so, do we need to account unremovable block number in each segment, and if
the count in candidate section is non-null, we just skip selecting the section
as victim of GC?

BTW, all caller passed false value in @inc, do you intend to call
dontmove_control(, true) in f2fs_gc?

> 
>>
>>>>> +{
>>>>> +	struct f2fs_inode_info *fi = F2FS_I(inode);
>>>>> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>>>>> +
>>>>> +	/* Use i_current_depth for normal file as a risk signal. */
>>>>> +	if (inc)
>>>>> +		f2fs_i_depth_write(inode, fi->i_current_depth + 1);
>>>>> +
>>>>> +	if (fi->i_current_depth > sbi->gc_dontmove) {
>>
>> If we want to persist per-inode metadata, what about adding new field in inode
>> layout to store such metadata?
> 
> I thought that this would not be such the important field which makes us extend
> inode fields.

OK, how about changing this field in inode layout as below:

union {
	__le32 i_current_depth;		/* only for directory depth */
	__le16 i_gc_failed_count;	/*
					 * # of gc failed to move unremovable
					 * block, for regular only.
					 */
}

Thanks,

> 
> Thanks,
> 
>>
>> Thanks,
>>
>>>>> +		f2fs_msg(sbi->sb, KERN_WARNING,
>>>>> +			"%s: Enable GC = ino %lx after %x GC trials\n",
>>>>> +			__func__, inode->i_ino, fi->i_current_depth);
>>>>> +		return -EAGAIN;
>>>>> +	}
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int f2fs_ioc_set_dontmove(struct file *filp)
>>>>> +{
>>>>> +	struct inode *inode = file_inode(filp);
>>>>> +	int ret;
>>>>> +
>>>>> +	if (!inode_owner_or_capable(inode))
>>>>> +		return -EACCES;
>>>>> +
>>>>> +	if (!S_ISREG(inode->i_mode))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
>>>>> +		return -EROFS;
>>>>> +
>>>>> +	inode_lock(inode);
>>>>> +
>>>>> +	if (f2fs_dontmove_control(inode, false)) {
>>>>> +		ret = -EAGAIN;
>>>>> +		goto out;
>>>>> +	}
>>>>> +
>>>>> +	ret = f2fs_convert_inline_inode(inode);
>>>>> +	if (ret)
>>>>> +		goto out;
>>>>> +
>>>>> +	set_inode_flag(inode, FI_DONTMOVE);
>>>>> +	ret = F2FS_I(inode)->i_current_depth;
>>>>> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>>>>> +out:
>>>>> +	inode_unlock(inode);
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>>>>  {
>>>>>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
>>>>> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>>>>  		return f2fs_ioc_fsgetxattr(filp, arg);
>>>>>  	case F2FS_IOC_FSSETXATTR:
>>>>>  		return f2fs_ioc_fssetxattr(filp, arg);
>>>>> +	case F2FS_IOC_SET_DONTMOVE:
>>>>> +		return f2fs_ioc_set_dontmove(filp);
>>>>>  	default:
>>>>>  		return -ENOTTY;
>>>>>  	}
>>>>> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>>>>  	case F2FS_IOC_GET_FEATURES:
>>>>>  	case F2FS_IOC_FSGETXATTR:
>>>>>  	case F2FS_IOC_FSSETXATTR:
>>>>> +	case F2FS_IOC_SET_DONTMOVE:
>>>>>  		break;
>>>>>  	default:
>>>>>  		return -ENOIOCTLCMD;
>>>>> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
>>>>> index 5d5bba462f26..263cb0b71ec8 100644
>>>>> --- a/fs/f2fs/gc.c
>>>>> +++ b/fs/f2fs/gc.c
>>>>> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
>>>>>  	if (f2fs_is_atomic_file(inode))
>>>>>  		goto out;
>>>>>  
>>>>> +	if (f2fs_is_dontmove_file(inode)) {
>>>>> +		f2fs_dontmove_control(inode, false);
>>>>> +		goto out;
>>>>> +	}
>>>>> +
>>>>>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
>>>>>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
>>>>>  	if (err)
>>>>> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
>>>>>  
>>>>>  	if (f2fs_is_atomic_file(inode))
>>>>>  		goto out;
>>>>> +	if (f2fs_is_dontmove_file(inode)) {
>>>>> +		if (gc_type == FG_GC)
>>>>> +			f2fs_dontmove_control(inode, false);
>>>>> +		goto out;
>>>>> +	}
>>>>>  
>>>>>  	if (gc_type == BG_GC) {
>>>>>  		if (PageWriteback(page))
>>>>> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
>>>>>  
>>>>>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
>>>>>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
>>>>> +	sbi->gc_dontmove = DEF_GC_FAILED_DONTMOVE;
>>>>>  
>>>>>  	/* give warm/cold data area from slower device */
>>>>>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
>>>>> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
>>>>> index 9325191fab2d..3fbb74253e52 100644
>>>>> --- a/fs/f2fs/gc.h
>>>>> +++ b/fs/f2fs/gc.h
>>>>> @@ -20,6 +20,8 @@
>>>>>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
>>>>>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
>>>>>  
>>>>> +#define DEF_GC_FAILED_DONTMOVE		1024
>>>>> +
>>>>>  /* Search max. number of dirty segments to select a victim segment */
>>>>>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
>>>>>  
>>>>> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
>>>>> index 93c3364250dd..57dd6660936a 100644
>>>>> --- a/fs/f2fs/sysfs.c
>>>>> +++ b/fs/f2fs/sysfs.c
>>>>> @@ -300,6 +300,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
>>>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
>>>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
>>>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
>>>>> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_dontmove, gc_dontmove);
>>>>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>>>>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
>>>>>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
>>>>> @@ -348,6 +349,7 @@ static struct attribute *f2fs_attrs[] = {
>>>>>  	ATTR_LIST(idle_interval),
>>>>>  	ATTR_LIST(iostat_enable),
>>>>>  	ATTR_LIST(readdir_ra),
>>>>> +	ATTR_LIST(gc_dontmove),
>>>>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>>>>  	ATTR_LIST(inject_rate),
>>>>>  	ATTR_LIST(inject_type),
>>>>> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
>>>>> index 43e98d30d2df..fedb2698a8f7 100644
>>>>> --- a/include/linux/f2fs_fs.h
>>>>> +++ b/include/linux/f2fs_fs.h
>>>>> @@ -212,6 +212,7 @@ struct f2fs_extent {
>>>>>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
>>>>>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
>>>>>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
>>>>> +#define F2FS_DONTMOVE		0x40	/* file should not be move */
>>>>>  
>>>>>  struct f2fs_inode {
>>>>>  	__le16 i_mode;			/* file mode */
>>>>>
>>>
>>> .
>>>
> 
> .
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [f2fs-dev] [PATCH v2] f2fs: add an ioctl to disable GC for specific file
@ 2017-12-25  2:52             ` Chao Yu
  0 siblings, 0 replies; 27+ messages in thread
From: Chao Yu @ 2017-12-25  2:52 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: linux-kernel, linux-f2fs-devel

On 2017/12/20 6:52, Jaegeuk Kim wrote:
> On 12/18, Chao Yu wrote:
>> On 2017/12/15 3:50, Jaegeuk Kim wrote:
>>> On 12/12, Chao Yu wrote:
>>>> Hi Jaegeuk,
>>>>
>>>> On 2017/12/9 3:37, Jaegeuk Kim wrote:
>>>>> Change log from v1:
>>>>>  - fix bug in error handling of ioctl 
>>>>>
>>>>> >From b905e03d8aad7d25ecaf9bde05411a68d3d2460e Mon Sep 17 00:00:00 2001
>>>>> From: Jaegeuk Kim <jaegeuk@kernel.org>
>>>>> Date: Thu, 7 Dec 2017 16:25:39 -0800
>>>>> Subject: [PATCH] f2fs: add an ioctl to disable GC for specific file
>>>>>
>>>>> This patch gives a flag to disable GC on given file, which would be useful, when
>>>>> user wants to keep its block map.
>>>>
>>>> Could you add some description about in which scenario userspace application
>>>> can use this ioctl, otherwise than android, other developers can get hint about
>>>> the usage of this interface, maybe later it can be used more wildly. ;)
>>>
>>> The usecase would be somewhat hacky tho, it looks like 1) building a block map
>>> through fibmap, 2) storing the map in another partition, 3) using the map to
>>> overwrite data contents directly by low-level tools. In that case, we have to
>>> keep its block locations.
>>
>> That's so hacky, it will be better to add simple description in comment log. ;)
> 
> Actually, I don't want to add this kind of hacky example. :P

No a big deal, anyway, I've know your purpose. ;)

> 
>>>>> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
>>>>> ---
>>>>>  fs/f2fs/f2fs.h          | 17 ++++++++++++++++
>>>>>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>>>  fs/f2fs/gc.c            | 11 ++++++++++
>>>>>  fs/f2fs/gc.h            |  2 ++
>>>>>  fs/f2fs/sysfs.c         |  2 ++
>>>>>  include/linux/f2fs_fs.h |  1 +
>>>>>  6 files changed, 87 insertions(+)
>>>>>
>>>>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>>>>> index 82f1dc345505..dd76cbf02791 100644
>>>>> --- a/fs/f2fs/f2fs.h
>>>>> +++ b/fs/f2fs/f2fs.h
>>>>> @@ -375,6 +375,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
>>>>>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
>>>>>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
>>>>>  
>>>>> +#define F2FS_IOC_SET_DONTMOVE		_IO(F2FS_IOCTL_MAGIC, 13)
>>>>> +
>>>>>  struct f2fs_gc_range {
>>>>>  	u32 sync;
>>>>>  	u64 start;
>>>>> @@ -1129,6 +1131,9 @@ struct f2fs_sb_info {
>>>>>  	/* threshold for converting bg victims for fg */
>>>>>  	u64 fggc_threshold;
>>>>>  
>>>>> +	/* threshold for dontmove gc trials */
>>>>> +	u64 gc_dontmove;
>>>>> +
>>>>>  	/* maximum # of trials to find a victim segment for SSR and GC */
>>>>>  	unsigned int max_victim_search;
>>>>>  
>>>>> @@ -2104,6 +2109,7 @@ enum {
>>>>>  	FI_HOT_DATA,		/* indicate file is hot */
>>>>>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
>>>>>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
>>>>> +	FI_DONTMOVE,		/* indicate file should not be gced */
>>>>>  };
>>>>>  
>>>>>  static inline void __mark_inode_dirty_flag(struct inode *inode,
>>>>> @@ -2117,6 +2123,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
>>>>>  			return;
>>>>>  	case FI_DATA_EXIST:
>>>>>  	case FI_INLINE_DOTS:
>>>>> +	case FI_DONTMOVE:
>>>>>  		f2fs_mark_inode_dirty_sync(inode, true);
>>>>>  	}
>>>>>  }
>>>>> @@ -2225,6 +2232,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
>>>>>  		set_bit(FI_INLINE_DOTS, &fi->flags);
>>>>>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
>>>>>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
>>>>> +	if (ri->i_inline & F2FS_DONTMOVE)
>>>>> +		set_bit(FI_DONTMOVE, &fi->flags);
>>>>>  }
>>>>>  
>>>>>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>>>>> @@ -2243,6 +2252,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>>>>>  		ri->i_inline |= F2FS_INLINE_DOTS;
>>>>>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
>>>>>  		ri->i_inline |= F2FS_EXTRA_ATTR;
>>>>> +	if (is_inode_flag_set(inode, FI_DONTMOVE))
>>>>> +		ri->i_inline |= F2FS_DONTMOVE;
>>>>>  }
>>>>>  
>>>>>  static inline int f2fs_has_extra_attr(struct inode *inode)
>>>>> @@ -2288,6 +2299,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
>>>>>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
>>>>>  }
>>>>>  
>>>>> +static inline bool f2fs_is_dontmove_file(struct inode *inode)
>>>>> +{
>>>>> +	return is_inode_flag_set(inode, FI_DONTMOVE);
>>>>> +}
>>>>> +
>>>>>  static inline bool f2fs_is_atomic_file(struct inode *inode)
>>>>>  {
>>>>>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
>>>>> @@ -2503,6 +2519,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
>>>>>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
>>>>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
>>>>>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
>>>>> +int f2fs_dontmove_control(struct inode *inode, bool inc);
>>>>>  
>>>>>  /*
>>>>>   * inode.c
>>>>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
>>>>> index 1db68f1bcd77..e232f8d91f6f 100644
>>>>> --- a/fs/f2fs/file.c
>>>>> +++ b/fs/f2fs/file.c
>>>>> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
>>>>>  	return 0;
>>>>>  }
>>>>>  
>>>>> +int f2fs_dontmove_control(struct inode *inode, bool inc)
>>
>> What's the purpose here? If GC tries to move block belong to unremovable inode,
>> we don't allow to set other inode as unremovable one?
> 
> I just wanted to avoid infinite loop to move this during foreground GC case.

If so, do we need to account unremovable block number in each segment, and if
the count in candidate section is non-null, we just skip selecting the section
as victim of GC?

BTW, all caller passed false value in @inc, do you intend to call
dontmove_control(, true) in f2fs_gc?

> 
>>
>>>>> +{
>>>>> +	struct f2fs_inode_info *fi = F2FS_I(inode);
>>>>> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>>>>> +
>>>>> +	/* Use i_current_depth for normal file as a risk signal. */
>>>>> +	if (inc)
>>>>> +		f2fs_i_depth_write(inode, fi->i_current_depth + 1);
>>>>> +
>>>>> +	if (fi->i_current_depth > sbi->gc_dontmove) {
>>
>> If we want to persist per-inode metadata, what about adding new field in inode
>> layout to store such metadata?
> 
> I thought that this would not be such the important field which makes us extend
> inode fields.

OK, how about changing this field in inode layout as below:

union {
	__le32 i_current_depth;		/* only for directory depth */
	__le16 i_gc_failed_count;	/*
					 * # of gc failed to move unremovable
					 * block, for regular only.
					 */
}

Thanks,

> 
> Thanks,
> 
>>
>> Thanks,
>>
>>>>> +		f2fs_msg(sbi->sb, KERN_WARNING,
>>>>> +			"%s: Enable GC = ino %lx after %x GC trials\n",
>>>>> +			__func__, inode->i_ino, fi->i_current_depth);
>>>>> +		return -EAGAIN;
>>>>> +	}
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int f2fs_ioc_set_dontmove(struct file *filp)
>>>>> +{
>>>>> +	struct inode *inode = file_inode(filp);
>>>>> +	int ret;
>>>>> +
>>>>> +	if (!inode_owner_or_capable(inode))
>>>>> +		return -EACCES;
>>>>> +
>>>>> +	if (!S_ISREG(inode->i_mode))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
>>>>> +		return -EROFS;
>>>>> +
>>>>> +	inode_lock(inode);
>>>>> +
>>>>> +	if (f2fs_dontmove_control(inode, false)) {
>>>>> +		ret = -EAGAIN;
>>>>> +		goto out;
>>>>> +	}
>>>>> +
>>>>> +	ret = f2fs_convert_inline_inode(inode);
>>>>> +	if (ret)
>>>>> +		goto out;
>>>>> +
>>>>> +	set_inode_flag(inode, FI_DONTMOVE);
>>>>> +	ret = F2FS_I(inode)->i_current_depth;
>>>>> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>>>>> +out:
>>>>> +	inode_unlock(inode);
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>>>>  {
>>>>>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
>>>>> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>>>>  		return f2fs_ioc_fsgetxattr(filp, arg);
>>>>>  	case F2FS_IOC_FSSETXATTR:
>>>>>  		return f2fs_ioc_fssetxattr(filp, arg);
>>>>> +	case F2FS_IOC_SET_DONTMOVE:
>>>>> +		return f2fs_ioc_set_dontmove(filp);
>>>>>  	default:
>>>>>  		return -ENOTTY;
>>>>>  	}
>>>>> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>>>>  	case F2FS_IOC_GET_FEATURES:
>>>>>  	case F2FS_IOC_FSGETXATTR:
>>>>>  	case F2FS_IOC_FSSETXATTR:
>>>>> +	case F2FS_IOC_SET_DONTMOVE:
>>>>>  		break;
>>>>>  	default:
>>>>>  		return -ENOIOCTLCMD;
>>>>> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
>>>>> index 5d5bba462f26..263cb0b71ec8 100644
>>>>> --- a/fs/f2fs/gc.c
>>>>> +++ b/fs/f2fs/gc.c
>>>>> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
>>>>>  	if (f2fs_is_atomic_file(inode))
>>>>>  		goto out;
>>>>>  
>>>>> +	if (f2fs_is_dontmove_file(inode)) {
>>>>> +		f2fs_dontmove_control(inode, false);
>>>>> +		goto out;
>>>>> +	}
>>>>> +
>>>>>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
>>>>>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
>>>>>  	if (err)
>>>>> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
>>>>>  
>>>>>  	if (f2fs_is_atomic_file(inode))
>>>>>  		goto out;
>>>>> +	if (f2fs_is_dontmove_file(inode)) {
>>>>> +		if (gc_type == FG_GC)
>>>>> +			f2fs_dontmove_control(inode, false);
>>>>> +		goto out;
>>>>> +	}
>>>>>  
>>>>>  	if (gc_type == BG_GC) {
>>>>>  		if (PageWriteback(page))
>>>>> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
>>>>>  
>>>>>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
>>>>>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
>>>>> +	sbi->gc_dontmove = DEF_GC_FAILED_DONTMOVE;
>>>>>  
>>>>>  	/* give warm/cold data area from slower device */
>>>>>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
>>>>> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
>>>>> index 9325191fab2d..3fbb74253e52 100644
>>>>> --- a/fs/f2fs/gc.h
>>>>> +++ b/fs/f2fs/gc.h
>>>>> @@ -20,6 +20,8 @@
>>>>>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
>>>>>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
>>>>>  
>>>>> +#define DEF_GC_FAILED_DONTMOVE		1024
>>>>> +
>>>>>  /* Search max. number of dirty segments to select a victim segment */
>>>>>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
>>>>>  
>>>>> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
>>>>> index 93c3364250dd..57dd6660936a 100644
>>>>> --- a/fs/f2fs/sysfs.c
>>>>> +++ b/fs/f2fs/sysfs.c
>>>>> @@ -300,6 +300,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
>>>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
>>>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
>>>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
>>>>> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_dontmove, gc_dontmove);
>>>>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>>>>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
>>>>>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
>>>>> @@ -348,6 +349,7 @@ static struct attribute *f2fs_attrs[] = {
>>>>>  	ATTR_LIST(idle_interval),
>>>>>  	ATTR_LIST(iostat_enable),
>>>>>  	ATTR_LIST(readdir_ra),
>>>>> +	ATTR_LIST(gc_dontmove),
>>>>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>>>>  	ATTR_LIST(inject_rate),
>>>>>  	ATTR_LIST(inject_type),
>>>>> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
>>>>> index 43e98d30d2df..fedb2698a8f7 100644
>>>>> --- a/include/linux/f2fs_fs.h
>>>>> +++ b/include/linux/f2fs_fs.h
>>>>> @@ -212,6 +212,7 @@ struct f2fs_extent {
>>>>>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
>>>>>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
>>>>>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
>>>>> +#define F2FS_DONTMOVE		0x40	/* file should not be move */
>>>>>  
>>>>>  struct f2fs_inode {
>>>>>  	__le16 i_mode;			/* file mode */
>>>>>
>>>
>>> .
>>>
> 
> .
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] f2fs: add an ioctl to disable GC for specific file
  2017-12-08 19:37 ` [PATCH v2] " Jaegeuk Kim
@ 2017-12-28  3:40     ` Jaegeuk Kim
  2017-12-28  3:40     ` Jaegeuk Kim
  1 sibling, 0 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2017-12-28  3:40 UTC (permalink / raw)
  To: linux-kernel, linux-f2fs-devel

This patch gives a flag to disable GC on given file, which would be useful, when
user wants to keep its block map. It also conducts in-place-update for dontmove
file.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---

Change log from v2:
 - change the naming from dontmove to pin_file
 - do in-place-update for pinned files
 - use union for i_current_depth with i_gc_failures
 - pass increasing i_gc_failures

 fs/f2fs/data.c          |  2 ++
 fs/f2fs/f2fs.h          | 29 +++++++++++++++++++++++++-
 fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/f2fs/gc.c            | 11 ++++++++++
 fs/f2fs/gc.h            |  2 ++
 fs/f2fs/sysfs.c         |  2 ++
 include/linux/f2fs_fs.h |  9 ++++++++-
 7 files changed, 107 insertions(+), 2 deletions(-)

diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 7aca6ccd01f6..b9fab6186f28 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1394,6 +1394,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
 {
 	struct inode *inode = fio->page->mapping->host;
 
+	if (f2fs_is_pinned_file(inode))
+		return true;
 	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
 		return false;
 	if (is_cold_data(fio->page))
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 65a51b941146..398ed95d4036 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -376,6 +376,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
 #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
 #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
 
+#define F2FS_IOC_PIN_FILE		_IO(F2FS_IOCTL_MAGIC, 13)
+
 struct f2fs_gc_range {
 	u32 sync;
 	u64 start;
@@ -586,7 +588,10 @@ struct f2fs_inode_info {
 	unsigned long i_flags;		/* keep an inode flags for ioctl */
 	unsigned char i_advise;		/* use to give file attribute hints */
 	unsigned char i_dir_level;	/* use for dentry level for large dir */
-	unsigned int i_current_depth;	/* use only in directory structure */
+	union {
+		unsigned int i_current_depth;	/* only for directory depth */
+		unsigned short i_gc_failures;	/* only for regular file */
+	};
 	unsigned int i_pino;		/* parent inode number */
 	umode_t i_acl_mode;		/* keep file acl mode temporarily */
 
@@ -1130,6 +1135,9 @@ struct f2fs_sb_info {
 	/* threshold for converting bg victims for fg */
 	u64 fggc_threshold;
 
+	/* threshold for gc trials on pinned files */
+	u64 gc_pin_file_threshold;
+
 	/* maximum # of trials to find a victim segment for SSR and GC */
 	unsigned int max_victim_search;
 
@@ -2119,6 +2127,7 @@ enum {
 	FI_HOT_DATA,		/* indicate file is hot */
 	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
 	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
+	FI_PIN_FILE,		/* indicate file should not be gced */
 };
 
 static inline void __mark_inode_dirty_flag(struct inode *inode,
@@ -2132,6 +2141,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
 			return;
 	case FI_DATA_EXIST:
 	case FI_INLINE_DOTS:
+	case FI_PIN_FILE:
 		f2fs_mark_inode_dirty_sync(inode, true);
 	}
 }
@@ -2212,6 +2222,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
 	f2fs_mark_inode_dirty_sync(inode, true);
 }
 
+static inline void f2fs_i_gc_failures_write(struct inode *inode,
+					unsigned int count)
+{
+	F2FS_I(inode)->i_gc_failures = count;
+	f2fs_mark_inode_dirty_sync(inode, true);
+}
+
 static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
 {
 	F2FS_I(inode)->i_xattr_nid = xnid;
@@ -2240,6 +2257,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
 		set_bit(FI_INLINE_DOTS, &fi->flags);
 	if (ri->i_inline & F2FS_EXTRA_ATTR)
 		set_bit(FI_EXTRA_ATTR, &fi->flags);
+	if (ri->i_inline & F2FS_PIN_FILE)
+		set_bit(FI_PIN_FILE, &fi->flags);
 }
 
 static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
@@ -2258,6 +2277,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
 		ri->i_inline |= F2FS_INLINE_DOTS;
 	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
 		ri->i_inline |= F2FS_EXTRA_ATTR;
+	if (is_inode_flag_set(inode, FI_PIN_FILE))
+		ri->i_inline |= F2FS_PIN_FILE;
 }
 
 static inline int f2fs_has_extra_attr(struct inode *inode)
@@ -2303,6 +2324,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
 	return is_inode_flag_set(inode, FI_INLINE_DOTS);
 }
 
+static inline bool f2fs_is_pinned_file(struct inode *inode)
+{
+	return is_inode_flag_set(inode, FI_PIN_FILE);
+}
+
 static inline bool f2fs_is_atomic_file(struct inode *inode)
 {
 	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
@@ -2518,6 +2544,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
 void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
 long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+int f2fs_pin_file_control(struct inode *inode, bool inc);
 
 /*
  * inode.c
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 1db68f1bcd77..5a3b9a07d72d 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
 	return 0;
 }
 
+int f2fs_pin_file_control(struct inode *inode, bool inc)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+	/* Use i_gc_failures for normal file as a risk signal. */
+	if (inc)
+		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
+
+	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
+		f2fs_msg(sbi->sb, KERN_WARNING,
+			"%s: Enable GC = ino %lx after %x GC trials\n",
+			__func__, inode->i_ino, fi->i_gc_failures);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static int f2fs_ioc_pin_file(struct file *filp)
+{
+	struct inode *inode = file_inode(filp);
+	int ret;
+
+	if (!inode_owner_or_capable(inode))
+		return -EACCES;
+
+	if (!S_ISREG(inode->i_mode))
+		return -EINVAL;
+
+	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
+		return -EROFS;
+
+	inode_lock(inode);
+
+	if (f2fs_pin_file_control(inode, false)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	ret = f2fs_convert_inline_inode(inode);
+	if (ret)
+		goto out;
+
+	set_inode_flag(inode, FI_PIN_FILE);
+	ret = F2FS_I(inode)->i_gc_failures;
+	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
+out:
+	inode_unlock(inode);
+	return ret;
+}
+
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
@@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		return f2fs_ioc_fsgetxattr(filp, arg);
 	case F2FS_IOC_FSSETXATTR:
 		return f2fs_ioc_fssetxattr(filp, arg);
+	case F2FS_IOC_PIN_FILE:
+		return f2fs_ioc_pin_file(filp);
 	default:
 		return -ENOTTY;
 	}
@@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case F2FS_IOC_GET_FEATURES:
 	case F2FS_IOC_FSGETXATTR:
 	case F2FS_IOC_FSSETXATTR:
+	case F2FS_IOC_PIN_FILE:
 		break;
 	default:
 		return -ENOIOCTLCMD;
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 5d5bba462f26..9bffef153a12 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
 	if (f2fs_is_atomic_file(inode))
 		goto out;
 
+	if (f2fs_is_pinned_file(inode)) {
+		f2fs_pin_file_control(inode, true);
+		goto out;
+	}
+
 	set_new_dnode(&dn, inode, NULL, NULL, 0);
 	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
 	if (err)
@@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
 
 	if (f2fs_is_atomic_file(inode))
 		goto out;
+	if (f2fs_is_pinned_file(inode)) {
+		if (gc_type == FG_GC)
+			f2fs_pin_file_control(inode, true);
+		goto out;
+	}
 
 	if (gc_type == BG_GC) {
 		if (PageWriteback(page))
@@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
 
 	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
 				BLKS_PER_SEC(sbi), (main_count - resv_count));
+	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
 
 	/* give warm/cold data area from slower device */
 	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
index 9325191fab2d..33310165cb78 100644
--- a/fs/f2fs/gc.h
+++ b/fs/f2fs/gc.h
@@ -20,6 +20,8 @@
 #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
 #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
 
+#define DEF_GC_FAILED_PINNED_FILES	1024
+
 /* Search max. number of dirty segments to select a victim segment */
 #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
 
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index 581c7ce23c22..0e3253d7cb10 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -303,6 +303,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
+F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
 F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
@@ -351,6 +352,7 @@ static struct attribute *f2fs_attrs[] = {
 	ATTR_LIST(idle_interval),
 	ATTR_LIST(iostat_enable),
 	ATTR_LIST(readdir_ra),
+	ATTR_LIST(gc_pin_file_thresh),
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 	ATTR_LIST(inject_rate),
 	ATTR_LIST(inject_type),
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 43e98d30d2df..cfb522e6affc 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -212,6 +212,7 @@ struct f2fs_extent {
 #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
 #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
 #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
+#define F2FS_PIN_FILE		0x40	/* file should not be gced */
 
 struct f2fs_inode {
 	__le16 i_mode;			/* file mode */
@@ -229,7 +230,13 @@ struct f2fs_inode {
 	__le32 i_ctime_nsec;		/* change time in nano scale */
 	__le32 i_mtime_nsec;		/* modification time in nano scale */
 	__le32 i_generation;		/* file version (for NFS) */
-	__le32 i_current_depth;		/* only for directory depth */
+	union {
+		__le32 i_current_depth;	/* only for directory depth */
+		__le16 i_gc_failures;	/*
+					 * # of gc failures on pinned file.
+					 * only for regular files.
+					 */
+	};
 	__le32 i_xattr_nid;		/* nid to save xattr */
 	__le32 i_flags;			/* file attributes */
 	__le32 i_pino;			/* parent inode number */
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] f2fs: add an ioctl to disable GC for specific file
@ 2017-12-28  3:40     ` Jaegeuk Kim
  0 siblings, 0 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2017-12-28  3:40 UTC (permalink / raw)
  To: linux-kernel, linux-f2fs-devel

This patch gives a flag to disable GC on given file, which would be useful, when
user wants to keep its block map. It also conducts in-place-update for dontmove
file.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---

Change log from v2:
 - change the naming from dontmove to pin_file
 - do in-place-update for pinned files
 - use union for i_current_depth with i_gc_failures
 - pass increasing i_gc_failures

 fs/f2fs/data.c          |  2 ++
 fs/f2fs/f2fs.h          | 29 +++++++++++++++++++++++++-
 fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/f2fs/gc.c            | 11 ++++++++++
 fs/f2fs/gc.h            |  2 ++
 fs/f2fs/sysfs.c         |  2 ++
 include/linux/f2fs_fs.h |  9 ++++++++-
 7 files changed, 107 insertions(+), 2 deletions(-)

diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 7aca6ccd01f6..b9fab6186f28 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1394,6 +1394,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
 {
 	struct inode *inode = fio->page->mapping->host;
 
+	if (f2fs_is_pinned_file(inode))
+		return true;
 	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
 		return false;
 	if (is_cold_data(fio->page))
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 65a51b941146..398ed95d4036 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -376,6 +376,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
 #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
 #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
 
+#define F2FS_IOC_PIN_FILE		_IO(F2FS_IOCTL_MAGIC, 13)
+
 struct f2fs_gc_range {
 	u32 sync;
 	u64 start;
@@ -586,7 +588,10 @@ struct f2fs_inode_info {
 	unsigned long i_flags;		/* keep an inode flags for ioctl */
 	unsigned char i_advise;		/* use to give file attribute hints */
 	unsigned char i_dir_level;	/* use for dentry level for large dir */
-	unsigned int i_current_depth;	/* use only in directory structure */
+	union {
+		unsigned int i_current_depth;	/* only for directory depth */
+		unsigned short i_gc_failures;	/* only for regular file */
+	};
 	unsigned int i_pino;		/* parent inode number */
 	umode_t i_acl_mode;		/* keep file acl mode temporarily */
 
@@ -1130,6 +1135,9 @@ struct f2fs_sb_info {
 	/* threshold for converting bg victims for fg */
 	u64 fggc_threshold;
 
+	/* threshold for gc trials on pinned files */
+	u64 gc_pin_file_threshold;
+
 	/* maximum # of trials to find a victim segment for SSR and GC */
 	unsigned int max_victim_search;
 
@@ -2119,6 +2127,7 @@ enum {
 	FI_HOT_DATA,		/* indicate file is hot */
 	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
 	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
+	FI_PIN_FILE,		/* indicate file should not be gced */
 };
 
 static inline void __mark_inode_dirty_flag(struct inode *inode,
@@ -2132,6 +2141,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
 			return;
 	case FI_DATA_EXIST:
 	case FI_INLINE_DOTS:
+	case FI_PIN_FILE:
 		f2fs_mark_inode_dirty_sync(inode, true);
 	}
 }
@@ -2212,6 +2222,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
 	f2fs_mark_inode_dirty_sync(inode, true);
 }
 
+static inline void f2fs_i_gc_failures_write(struct inode *inode,
+					unsigned int count)
+{
+	F2FS_I(inode)->i_gc_failures = count;
+	f2fs_mark_inode_dirty_sync(inode, true);
+}
+
 static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
 {
 	F2FS_I(inode)->i_xattr_nid = xnid;
@@ -2240,6 +2257,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
 		set_bit(FI_INLINE_DOTS, &fi->flags);
 	if (ri->i_inline & F2FS_EXTRA_ATTR)
 		set_bit(FI_EXTRA_ATTR, &fi->flags);
+	if (ri->i_inline & F2FS_PIN_FILE)
+		set_bit(FI_PIN_FILE, &fi->flags);
 }
 
 static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
@@ -2258,6 +2277,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
 		ri->i_inline |= F2FS_INLINE_DOTS;
 	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
 		ri->i_inline |= F2FS_EXTRA_ATTR;
+	if (is_inode_flag_set(inode, FI_PIN_FILE))
+		ri->i_inline |= F2FS_PIN_FILE;
 }
 
 static inline int f2fs_has_extra_attr(struct inode *inode)
@@ -2303,6 +2324,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
 	return is_inode_flag_set(inode, FI_INLINE_DOTS);
 }
 
+static inline bool f2fs_is_pinned_file(struct inode *inode)
+{
+	return is_inode_flag_set(inode, FI_PIN_FILE);
+}
+
 static inline bool f2fs_is_atomic_file(struct inode *inode)
 {
 	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
@@ -2518,6 +2544,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
 void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
 long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+int f2fs_pin_file_control(struct inode *inode, bool inc);
 
 /*
  * inode.c
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 1db68f1bcd77..5a3b9a07d72d 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
 	return 0;
 }
 
+int f2fs_pin_file_control(struct inode *inode, bool inc)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+	/* Use i_gc_failures for normal file as a risk signal. */
+	if (inc)
+		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
+
+	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
+		f2fs_msg(sbi->sb, KERN_WARNING,
+			"%s: Enable GC = ino %lx after %x GC trials\n",
+			__func__, inode->i_ino, fi->i_gc_failures);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static int f2fs_ioc_pin_file(struct file *filp)
+{
+	struct inode *inode = file_inode(filp);
+	int ret;
+
+	if (!inode_owner_or_capable(inode))
+		return -EACCES;
+
+	if (!S_ISREG(inode->i_mode))
+		return -EINVAL;
+
+	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
+		return -EROFS;
+
+	inode_lock(inode);
+
+	if (f2fs_pin_file_control(inode, false)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	ret = f2fs_convert_inline_inode(inode);
+	if (ret)
+		goto out;
+
+	set_inode_flag(inode, FI_PIN_FILE);
+	ret = F2FS_I(inode)->i_gc_failures;
+	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
+out:
+	inode_unlock(inode);
+	return ret;
+}
+
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
@@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		return f2fs_ioc_fsgetxattr(filp, arg);
 	case F2FS_IOC_FSSETXATTR:
 		return f2fs_ioc_fssetxattr(filp, arg);
+	case F2FS_IOC_PIN_FILE:
+		return f2fs_ioc_pin_file(filp);
 	default:
 		return -ENOTTY;
 	}
@@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case F2FS_IOC_GET_FEATURES:
 	case F2FS_IOC_FSGETXATTR:
 	case F2FS_IOC_FSSETXATTR:
+	case F2FS_IOC_PIN_FILE:
 		break;
 	default:
 		return -ENOIOCTLCMD;
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 5d5bba462f26..9bffef153a12 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
 	if (f2fs_is_atomic_file(inode))
 		goto out;
 
+	if (f2fs_is_pinned_file(inode)) {
+		f2fs_pin_file_control(inode, true);
+		goto out;
+	}
+
 	set_new_dnode(&dn, inode, NULL, NULL, 0);
 	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
 	if (err)
@@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
 
 	if (f2fs_is_atomic_file(inode))
 		goto out;
+	if (f2fs_is_pinned_file(inode)) {
+		if (gc_type == FG_GC)
+			f2fs_pin_file_control(inode, true);
+		goto out;
+	}
 
 	if (gc_type == BG_GC) {
 		if (PageWriteback(page))
@@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
 
 	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
 				BLKS_PER_SEC(sbi), (main_count - resv_count));
+	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
 
 	/* give warm/cold data area from slower device */
 	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
index 9325191fab2d..33310165cb78 100644
--- a/fs/f2fs/gc.h
+++ b/fs/f2fs/gc.h
@@ -20,6 +20,8 @@
 #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
 #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
 
+#define DEF_GC_FAILED_PINNED_FILES	1024
+
 /* Search max. number of dirty segments to select a victim segment */
 #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
 
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index 581c7ce23c22..0e3253d7cb10 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -303,6 +303,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
+F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
 F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
@@ -351,6 +352,7 @@ static struct attribute *f2fs_attrs[] = {
 	ATTR_LIST(idle_interval),
 	ATTR_LIST(iostat_enable),
 	ATTR_LIST(readdir_ra),
+	ATTR_LIST(gc_pin_file_thresh),
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 	ATTR_LIST(inject_rate),
 	ATTR_LIST(inject_type),
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 43e98d30d2df..cfb522e6affc 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -212,6 +212,7 @@ struct f2fs_extent {
 #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
 #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
 #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
+#define F2FS_PIN_FILE		0x40	/* file should not be gced */
 
 struct f2fs_inode {
 	__le16 i_mode;			/* file mode */
@@ -229,7 +230,13 @@ struct f2fs_inode {
 	__le32 i_ctime_nsec;		/* change time in nano scale */
 	__le32 i_mtime_nsec;		/* modification time in nano scale */
 	__le32 i_generation;		/* file version (for NFS) */
-	__le32 i_current_depth;		/* only for directory depth */
+	union {
+		__le32 i_current_depth;	/* only for directory depth */
+		__le16 i_gc_failures;	/*
+					 * # of gc failures on pinned file.
+					 * only for regular files.
+					 */
+	};
 	__le32 i_xattr_nid;		/* nid to save xattr */
 	__le32 i_flags;			/* file attributes */
 	__le32 i_pino;			/* parent inode number */
-- 
2.15.0.531.g2ccb3012c9-goog


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] f2fs: add an ioctl to disable GC for specific file
  2017-12-28  3:40     ` Jaegeuk Kim
@ 2017-12-29  8:29       ` Chao Yu
  -1 siblings, 0 replies; 27+ messages in thread
From: Chao Yu @ 2017-12-29  8:29 UTC (permalink / raw)
  To: Jaegeuk Kim, linux-kernel, linux-f2fs-devel

On 2017/12/28 11:40, Jaegeuk Kim wrote:
> This patch gives a flag to disable GC on given file, which would be useful, when
> user wants to keep its block map. It also conducts in-place-update for dontmove
> file.

One question, we may encounter out-of-space during foreground GC if there
is too many data blocks of pinned files as they are removable.

Do we need to record all segments which contain block of pinned file? and
then trigger foreground GC more early by adjust threshold in
has_not_enough_free_secs.

Thanks,

> 
> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> ---
> 
> Change log from v2:
>  - change the naming from dontmove to pin_file
>  - do in-place-update for pinned files
>  - use union for i_current_depth with i_gc_failures
>  - pass increasing i_gc_failures
> 
>  fs/f2fs/data.c          |  2 ++
>  fs/f2fs/f2fs.h          | 29 +++++++++++++++++++++++++-
>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/f2fs/gc.c            | 11 ++++++++++
>  fs/f2fs/gc.h            |  2 ++
>  fs/f2fs/sysfs.c         |  2 ++
>  include/linux/f2fs_fs.h |  9 ++++++++-
>  7 files changed, 107 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
> index 7aca6ccd01f6..b9fab6186f28 100644
> --- a/fs/f2fs/data.c
> +++ b/fs/f2fs/data.c
> @@ -1394,6 +1394,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
>  {
>  	struct inode *inode = fio->page->mapping->host;
>  
> +	if (f2fs_is_pinned_file(inode))
> +		return true;
>  	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
>  		return false;
>  	if (is_cold_data(fio->page))
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index 65a51b941146..398ed95d4036 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -376,6 +376,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
>  
> +#define F2FS_IOC_PIN_FILE		_IO(F2FS_IOCTL_MAGIC, 13)
> +
>  struct f2fs_gc_range {
>  	u32 sync;
>  	u64 start;
> @@ -586,7 +588,10 @@ struct f2fs_inode_info {
>  	unsigned long i_flags;		/* keep an inode flags for ioctl */
>  	unsigned char i_advise;		/* use to give file attribute hints */
>  	unsigned char i_dir_level;	/* use for dentry level for large dir */
> -	unsigned int i_current_depth;	/* use only in directory structure */
> +	union {
> +		unsigned int i_current_depth;	/* only for directory depth */
> +		unsigned short i_gc_failures;	/* only for regular file */
> +	};
>  	unsigned int i_pino;		/* parent inode number */
>  	umode_t i_acl_mode;		/* keep file acl mode temporarily */
>  
> @@ -1130,6 +1135,9 @@ struct f2fs_sb_info {
>  	/* threshold for converting bg victims for fg */
>  	u64 fggc_threshold;
>  
> +	/* threshold for gc trials on pinned files */
> +	u64 gc_pin_file_threshold;
> +
>  	/* maximum # of trials to find a victim segment for SSR and GC */
>  	unsigned int max_victim_search;
>  
> @@ -2119,6 +2127,7 @@ enum {
>  	FI_HOT_DATA,		/* indicate file is hot */
>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> +	FI_PIN_FILE,		/* indicate file should not be gced */
>  };
>  
>  static inline void __mark_inode_dirty_flag(struct inode *inode,
> @@ -2132,6 +2141,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
>  			return;
>  	case FI_DATA_EXIST:
>  	case FI_INLINE_DOTS:
> +	case FI_PIN_FILE:
>  		f2fs_mark_inode_dirty_sync(inode, true);
>  	}
>  }
> @@ -2212,6 +2222,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
>  	f2fs_mark_inode_dirty_sync(inode, true);
>  }
>  
> +static inline void f2fs_i_gc_failures_write(struct inode *inode,
> +					unsigned int count)
> +{
> +	F2FS_I(inode)->i_gc_failures = count;
> +	f2fs_mark_inode_dirty_sync(inode, true);
> +}
> +
>  static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
>  {
>  	F2FS_I(inode)->i_xattr_nid = xnid;
> @@ -2240,6 +2257,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
>  		set_bit(FI_INLINE_DOTS, &fi->flags);
>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> +	if (ri->i_inline & F2FS_PIN_FILE)
> +		set_bit(FI_PIN_FILE, &fi->flags);
>  }
>  
>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> @@ -2258,6 +2277,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>  		ri->i_inline |= F2FS_INLINE_DOTS;
>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
>  		ri->i_inline |= F2FS_EXTRA_ATTR;
> +	if (is_inode_flag_set(inode, FI_PIN_FILE))
> +		ri->i_inline |= F2FS_PIN_FILE;
>  }
>  
>  static inline int f2fs_has_extra_attr(struct inode *inode)
> @@ -2303,6 +2324,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
>  }
>  
> +static inline bool f2fs_is_pinned_file(struct inode *inode)
> +{
> +	return is_inode_flag_set(inode, FI_PIN_FILE);
> +}
> +
>  static inline bool f2fs_is_atomic_file(struct inode *inode)
>  {
>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> @@ -2518,6 +2544,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> +int f2fs_pin_file_control(struct inode *inode, bool inc);
>  
>  /*
>   * inode.c
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 1db68f1bcd77..5a3b9a07d72d 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
>  	return 0;
>  }
>  
> +int f2fs_pin_file_control(struct inode *inode, bool inc)
> +{
> +	struct f2fs_inode_info *fi = F2FS_I(inode);
> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> +
> +	/* Use i_gc_failures for normal file as a risk signal. */
> +	if (inc)
> +		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
> +
> +	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
> +		f2fs_msg(sbi->sb, KERN_WARNING,
> +			"%s: Enable GC = ino %lx after %x GC trials\n",
> +			__func__, inode->i_ino, fi->i_gc_failures);
> +		return -EAGAIN;
> +	}
> +	return 0;
> +}
> +
> +static int f2fs_ioc_pin_file(struct file *filp)
> +{
> +	struct inode *inode = file_inode(filp);
> +	int ret;
> +
> +	if (!inode_owner_or_capable(inode))
> +		return -EACCES;
> +
> +	if (!S_ISREG(inode->i_mode))
> +		return -EINVAL;
> +
> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> +		return -EROFS;
> +
> +	inode_lock(inode);
> +
> +	if (f2fs_pin_file_control(inode, false)) {
> +		ret = -EAGAIN;
> +		goto out;
> +	}
> +
> +	ret = f2fs_convert_inline_inode(inode);
> +	if (ret)
> +		goto out;
> +
> +	set_inode_flag(inode, FI_PIN_FILE);
> +	ret = F2FS_I(inode)->i_gc_failures;
> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> +out:
> +	inode_unlock(inode);
> +	return ret;
> +}
> +
>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  {
>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  		return f2fs_ioc_fsgetxattr(filp, arg);
>  	case F2FS_IOC_FSSETXATTR:
>  		return f2fs_ioc_fssetxattr(filp, arg);
> +	case F2FS_IOC_PIN_FILE:
> +		return f2fs_ioc_pin_file(filp);
>  	default:
>  		return -ENOTTY;
>  	}
> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  	case F2FS_IOC_GET_FEATURES:
>  	case F2FS_IOC_FSGETXATTR:
>  	case F2FS_IOC_FSSETXATTR:
> +	case F2FS_IOC_PIN_FILE:
>  		break;
>  	default:
>  		return -ENOIOCTLCMD;
> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> index 5d5bba462f26..9bffef153a12 100644
> --- a/fs/f2fs/gc.c
> +++ b/fs/f2fs/gc.c
> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
>  	if (f2fs_is_atomic_file(inode))
>  		goto out;
>  
> +	if (f2fs_is_pinned_file(inode)) {
> +		f2fs_pin_file_control(inode, true);
> +		goto out;
> +	}
> +
>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
>  	if (err)
> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
>  
>  	if (f2fs_is_atomic_file(inode))
>  		goto out;
> +	if (f2fs_is_pinned_file(inode)) {
> +		if (gc_type == FG_GC)
> +			f2fs_pin_file_control(inode, true);
> +		goto out;
> +	}
>  
>  	if (gc_type == BG_GC) {
>  		if (PageWriteback(page))
> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
>  
>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> +	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
>  
>  	/* give warm/cold data area from slower device */
>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> index 9325191fab2d..33310165cb78 100644
> --- a/fs/f2fs/gc.h
> +++ b/fs/f2fs/gc.h
> @@ -20,6 +20,8 @@
>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
>  
> +#define DEF_GC_FAILED_PINNED_FILES	1024
> +
>  /* Search max. number of dirty segments to select a victim segment */
>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
>  
> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> index 581c7ce23c22..0e3253d7cb10 100644
> --- a/fs/f2fs/sysfs.c
> +++ b/fs/f2fs/sysfs.c
> @@ -303,6 +303,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> @@ -351,6 +352,7 @@ static struct attribute *f2fs_attrs[] = {
>  	ATTR_LIST(idle_interval),
>  	ATTR_LIST(iostat_enable),
>  	ATTR_LIST(readdir_ra),
> +	ATTR_LIST(gc_pin_file_thresh),
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  	ATTR_LIST(inject_rate),
>  	ATTR_LIST(inject_type),
> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> index 43e98d30d2df..cfb522e6affc 100644
> --- a/include/linux/f2fs_fs.h
> +++ b/include/linux/f2fs_fs.h
> @@ -212,6 +212,7 @@ struct f2fs_extent {
>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> +#define F2FS_PIN_FILE		0x40	/* file should not be gced */
>  
>  struct f2fs_inode {
>  	__le16 i_mode;			/* file mode */
> @@ -229,7 +230,13 @@ struct f2fs_inode {
>  	__le32 i_ctime_nsec;		/* change time in nano scale */
>  	__le32 i_mtime_nsec;		/* modification time in nano scale */
>  	__le32 i_generation;		/* file version (for NFS) */
> -	__le32 i_current_depth;		/* only for directory depth */
> +	union {
> +		__le32 i_current_depth;	/* only for directory depth */
> +		__le16 i_gc_failures;	/*
> +					 * # of gc failures on pinned file.
> +					 * only for regular files.
> +					 */
> +	};
>  	__le32 i_xattr_nid;		/* nid to save xattr */
>  	__le32 i_flags;			/* file attributes */
>  	__le32 i_pino;			/* parent inode number */
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] f2fs: add an ioctl to disable GC for specific file
@ 2017-12-29  8:29       ` Chao Yu
  0 siblings, 0 replies; 27+ messages in thread
From: Chao Yu @ 2017-12-29  8:29 UTC (permalink / raw)
  To: Jaegeuk Kim, linux-kernel, linux-f2fs-devel

On 2017/12/28 11:40, Jaegeuk Kim wrote:
> This patch gives a flag to disable GC on given file, which would be useful, when
> user wants to keep its block map. It also conducts in-place-update for dontmove
> file.

One question, we may encounter out-of-space during foreground GC if there
is too many data blocks of pinned files as they are removable.

Do we need to record all segments which contain block of pinned file? and
then trigger foreground GC more early by adjust threshold in
has_not_enough_free_secs.

Thanks,

> 
> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> ---
> 
> Change log from v2:
>  - change the naming from dontmove to pin_file
>  - do in-place-update for pinned files
>  - use union for i_current_depth with i_gc_failures
>  - pass increasing i_gc_failures
> 
>  fs/f2fs/data.c          |  2 ++
>  fs/f2fs/f2fs.h          | 29 +++++++++++++++++++++++++-
>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/f2fs/gc.c            | 11 ++++++++++
>  fs/f2fs/gc.h            |  2 ++
>  fs/f2fs/sysfs.c         |  2 ++
>  include/linux/f2fs_fs.h |  9 ++++++++-
>  7 files changed, 107 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
> index 7aca6ccd01f6..b9fab6186f28 100644
> --- a/fs/f2fs/data.c
> +++ b/fs/f2fs/data.c
> @@ -1394,6 +1394,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
>  {
>  	struct inode *inode = fio->page->mapping->host;
>  
> +	if (f2fs_is_pinned_file(inode))
> +		return true;
>  	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
>  		return false;
>  	if (is_cold_data(fio->page))
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index 65a51b941146..398ed95d4036 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -376,6 +376,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
>  
> +#define F2FS_IOC_PIN_FILE		_IO(F2FS_IOCTL_MAGIC, 13)
> +
>  struct f2fs_gc_range {
>  	u32 sync;
>  	u64 start;
> @@ -586,7 +588,10 @@ struct f2fs_inode_info {
>  	unsigned long i_flags;		/* keep an inode flags for ioctl */
>  	unsigned char i_advise;		/* use to give file attribute hints */
>  	unsigned char i_dir_level;	/* use for dentry level for large dir */
> -	unsigned int i_current_depth;	/* use only in directory structure */
> +	union {
> +		unsigned int i_current_depth;	/* only for directory depth */
> +		unsigned short i_gc_failures;	/* only for regular file */
> +	};
>  	unsigned int i_pino;		/* parent inode number */
>  	umode_t i_acl_mode;		/* keep file acl mode temporarily */
>  
> @@ -1130,6 +1135,9 @@ struct f2fs_sb_info {
>  	/* threshold for converting bg victims for fg */
>  	u64 fggc_threshold;
>  
> +	/* threshold for gc trials on pinned files */
> +	u64 gc_pin_file_threshold;
> +
>  	/* maximum # of trials to find a victim segment for SSR and GC */
>  	unsigned int max_victim_search;
>  
> @@ -2119,6 +2127,7 @@ enum {
>  	FI_HOT_DATA,		/* indicate file is hot */
>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> +	FI_PIN_FILE,		/* indicate file should not be gced */
>  };
>  
>  static inline void __mark_inode_dirty_flag(struct inode *inode,
> @@ -2132,6 +2141,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
>  			return;
>  	case FI_DATA_EXIST:
>  	case FI_INLINE_DOTS:
> +	case FI_PIN_FILE:
>  		f2fs_mark_inode_dirty_sync(inode, true);
>  	}
>  }
> @@ -2212,6 +2222,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
>  	f2fs_mark_inode_dirty_sync(inode, true);
>  }
>  
> +static inline void f2fs_i_gc_failures_write(struct inode *inode,
> +					unsigned int count)
> +{
> +	F2FS_I(inode)->i_gc_failures = count;
> +	f2fs_mark_inode_dirty_sync(inode, true);
> +}
> +
>  static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
>  {
>  	F2FS_I(inode)->i_xattr_nid = xnid;
> @@ -2240,6 +2257,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
>  		set_bit(FI_INLINE_DOTS, &fi->flags);
>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> +	if (ri->i_inline & F2FS_PIN_FILE)
> +		set_bit(FI_PIN_FILE, &fi->flags);
>  }
>  
>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> @@ -2258,6 +2277,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>  		ri->i_inline |= F2FS_INLINE_DOTS;
>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
>  		ri->i_inline |= F2FS_EXTRA_ATTR;
> +	if (is_inode_flag_set(inode, FI_PIN_FILE))
> +		ri->i_inline |= F2FS_PIN_FILE;
>  }
>  
>  static inline int f2fs_has_extra_attr(struct inode *inode)
> @@ -2303,6 +2324,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
>  }
>  
> +static inline bool f2fs_is_pinned_file(struct inode *inode)
> +{
> +	return is_inode_flag_set(inode, FI_PIN_FILE);
> +}
> +
>  static inline bool f2fs_is_atomic_file(struct inode *inode)
>  {
>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> @@ -2518,6 +2544,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> +int f2fs_pin_file_control(struct inode *inode, bool inc);
>  
>  /*
>   * inode.c
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 1db68f1bcd77..5a3b9a07d72d 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
>  	return 0;
>  }
>  
> +int f2fs_pin_file_control(struct inode *inode, bool inc)
> +{
> +	struct f2fs_inode_info *fi = F2FS_I(inode);
> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> +
> +	/* Use i_gc_failures for normal file as a risk signal. */
> +	if (inc)
> +		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
> +
> +	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
> +		f2fs_msg(sbi->sb, KERN_WARNING,
> +			"%s: Enable GC = ino %lx after %x GC trials\n",
> +			__func__, inode->i_ino, fi->i_gc_failures);
> +		return -EAGAIN;
> +	}
> +	return 0;
> +}
> +
> +static int f2fs_ioc_pin_file(struct file *filp)
> +{
> +	struct inode *inode = file_inode(filp);
> +	int ret;
> +
> +	if (!inode_owner_or_capable(inode))
> +		return -EACCES;
> +
> +	if (!S_ISREG(inode->i_mode))
> +		return -EINVAL;
> +
> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> +		return -EROFS;
> +
> +	inode_lock(inode);
> +
> +	if (f2fs_pin_file_control(inode, false)) {
> +		ret = -EAGAIN;
> +		goto out;
> +	}
> +
> +	ret = f2fs_convert_inline_inode(inode);
> +	if (ret)
> +		goto out;
> +
> +	set_inode_flag(inode, FI_PIN_FILE);
> +	ret = F2FS_I(inode)->i_gc_failures;
> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> +out:
> +	inode_unlock(inode);
> +	return ret;
> +}
> +
>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  {
>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  		return f2fs_ioc_fsgetxattr(filp, arg);
>  	case F2FS_IOC_FSSETXATTR:
>  		return f2fs_ioc_fssetxattr(filp, arg);
> +	case F2FS_IOC_PIN_FILE:
> +		return f2fs_ioc_pin_file(filp);
>  	default:
>  		return -ENOTTY;
>  	}
> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  	case F2FS_IOC_GET_FEATURES:
>  	case F2FS_IOC_FSGETXATTR:
>  	case F2FS_IOC_FSSETXATTR:
> +	case F2FS_IOC_PIN_FILE:
>  		break;
>  	default:
>  		return -ENOIOCTLCMD;
> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> index 5d5bba462f26..9bffef153a12 100644
> --- a/fs/f2fs/gc.c
> +++ b/fs/f2fs/gc.c
> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
>  	if (f2fs_is_atomic_file(inode))
>  		goto out;
>  
> +	if (f2fs_is_pinned_file(inode)) {
> +		f2fs_pin_file_control(inode, true);
> +		goto out;
> +	}
> +
>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
>  	if (err)
> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
>  
>  	if (f2fs_is_atomic_file(inode))
>  		goto out;
> +	if (f2fs_is_pinned_file(inode)) {
> +		if (gc_type == FG_GC)
> +			f2fs_pin_file_control(inode, true);
> +		goto out;
> +	}
>  
>  	if (gc_type == BG_GC) {
>  		if (PageWriteback(page))
> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
>  
>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> +	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
>  
>  	/* give warm/cold data area from slower device */
>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> index 9325191fab2d..33310165cb78 100644
> --- a/fs/f2fs/gc.h
> +++ b/fs/f2fs/gc.h
> @@ -20,6 +20,8 @@
>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
>  
> +#define DEF_GC_FAILED_PINNED_FILES	1024
> +
>  /* Search max. number of dirty segments to select a victim segment */
>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
>  
> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> index 581c7ce23c22..0e3253d7cb10 100644
> --- a/fs/f2fs/sysfs.c
> +++ b/fs/f2fs/sysfs.c
> @@ -303,6 +303,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> @@ -351,6 +352,7 @@ static struct attribute *f2fs_attrs[] = {
>  	ATTR_LIST(idle_interval),
>  	ATTR_LIST(iostat_enable),
>  	ATTR_LIST(readdir_ra),
> +	ATTR_LIST(gc_pin_file_thresh),
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  	ATTR_LIST(inject_rate),
>  	ATTR_LIST(inject_type),
> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> index 43e98d30d2df..cfb522e6affc 100644
> --- a/include/linux/f2fs_fs.h
> +++ b/include/linux/f2fs_fs.h
> @@ -212,6 +212,7 @@ struct f2fs_extent {
>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> +#define F2FS_PIN_FILE		0x40	/* file should not be gced */
>  
>  struct f2fs_inode {
>  	__le16 i_mode;			/* file mode */
> @@ -229,7 +230,13 @@ struct f2fs_inode {
>  	__le32 i_ctime_nsec;		/* change time in nano scale */
>  	__le32 i_mtime_nsec;		/* modification time in nano scale */
>  	__le32 i_generation;		/* file version (for NFS) */
> -	__le32 i_current_depth;		/* only for directory depth */
> +	union {
> +		__le32 i_current_depth;	/* only for directory depth */
> +		__le16 i_gc_failures;	/*
> +					 * # of gc failures on pinned file.
> +					 * only for regular files.
> +					 */
> +	};
>  	__le32 i_xattr_nid;		/* nid to save xattr */
>  	__le32 i_flags;			/* file attributes */
>  	__le32 i_pino;			/* parent inode number */
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] f2fs: add an ioctl to disable GC for specific file
  2017-12-29  8:29       ` Chao Yu
@ 2018-01-01  1:07         ` Jaegeuk Kim
  -1 siblings, 0 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2018-01-01  1:07 UTC (permalink / raw)
  To: Chao Yu; +Cc: linux-kernel, linux-f2fs-devel

On 12/29, Chao Yu wrote:
> On 2017/12/28 11:40, Jaegeuk Kim wrote:
> > This patch gives a flag to disable GC on given file, which would be useful, when
> > user wants to keep its block map. It also conducts in-place-update for dontmove
> > file.
> 
> One question, we may encounter out-of-space during foreground GC if there
> is too many data blocks of pinned files as they are removable.
> 
> Do we need to record all segments which contain block of pinned file? and
> then trigger foreground GC more early by adjust threshold in
> has_not_enough_free_secs.

This should be used only when application knows how this works. If not, I wanted
to just unset the flag as much as possible, as foreground GCs have been done for
pinned file too frequently. I expect the usecase must be quite unusual with just
small number of files in short period likewise atomic files.

Thanks,

> 
> Thanks,
> 
> > 
> > Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> > ---
> > 
> > Change log from v2:
> >  - change the naming from dontmove to pin_file
> >  - do in-place-update for pinned files
> >  - use union for i_current_depth with i_gc_failures
> >  - pass increasing i_gc_failures
> > 
> >  fs/f2fs/data.c          |  2 ++
> >  fs/f2fs/f2fs.h          | 29 +++++++++++++++++++++++++-
> >  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  fs/f2fs/gc.c            | 11 ++++++++++
> >  fs/f2fs/gc.h            |  2 ++
> >  fs/f2fs/sysfs.c         |  2 ++
> >  include/linux/f2fs_fs.h |  9 ++++++++-
> >  7 files changed, 107 insertions(+), 2 deletions(-)
> > 
> > diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
> > index 7aca6ccd01f6..b9fab6186f28 100644
> > --- a/fs/f2fs/data.c
> > +++ b/fs/f2fs/data.c
> > @@ -1394,6 +1394,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
> >  {
> >  	struct inode *inode = fio->page->mapping->host;
> >  
> > +	if (f2fs_is_pinned_file(inode))
> > +		return true;
> >  	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
> >  		return false;
> >  	if (is_cold_data(fio->page))
> > diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> > index 65a51b941146..398ed95d4036 100644
> > --- a/fs/f2fs/f2fs.h
> > +++ b/fs/f2fs/f2fs.h
> > @@ -376,6 +376,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
> >  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
> >  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
> >  
> > +#define F2FS_IOC_PIN_FILE		_IO(F2FS_IOCTL_MAGIC, 13)
> > +
> >  struct f2fs_gc_range {
> >  	u32 sync;
> >  	u64 start;
> > @@ -586,7 +588,10 @@ struct f2fs_inode_info {
> >  	unsigned long i_flags;		/* keep an inode flags for ioctl */
> >  	unsigned char i_advise;		/* use to give file attribute hints */
> >  	unsigned char i_dir_level;	/* use for dentry level for large dir */
> > -	unsigned int i_current_depth;	/* use only in directory structure */
> > +	union {
> > +		unsigned int i_current_depth;	/* only for directory depth */
> > +		unsigned short i_gc_failures;	/* only for regular file */
> > +	};
> >  	unsigned int i_pino;		/* parent inode number */
> >  	umode_t i_acl_mode;		/* keep file acl mode temporarily */
> >  
> > @@ -1130,6 +1135,9 @@ struct f2fs_sb_info {
> >  	/* threshold for converting bg victims for fg */
> >  	u64 fggc_threshold;
> >  
> > +	/* threshold for gc trials on pinned files */
> > +	u64 gc_pin_file_threshold;
> > +
> >  	/* maximum # of trials to find a victim segment for SSR and GC */
> >  	unsigned int max_victim_search;
> >  
> > @@ -2119,6 +2127,7 @@ enum {
> >  	FI_HOT_DATA,		/* indicate file is hot */
> >  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
> >  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> > +	FI_PIN_FILE,		/* indicate file should not be gced */
> >  };
> >  
> >  static inline void __mark_inode_dirty_flag(struct inode *inode,
> > @@ -2132,6 +2141,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
> >  			return;
> >  	case FI_DATA_EXIST:
> >  	case FI_INLINE_DOTS:
> > +	case FI_PIN_FILE:
> >  		f2fs_mark_inode_dirty_sync(inode, true);
> >  	}
> >  }
> > @@ -2212,6 +2222,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
> >  	f2fs_mark_inode_dirty_sync(inode, true);
> >  }
> >  
> > +static inline void f2fs_i_gc_failures_write(struct inode *inode,
> > +					unsigned int count)
> > +{
> > +	F2FS_I(inode)->i_gc_failures = count;
> > +	f2fs_mark_inode_dirty_sync(inode, true);
> > +}
> > +
> >  static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
> >  {
> >  	F2FS_I(inode)->i_xattr_nid = xnid;
> > @@ -2240,6 +2257,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
> >  		set_bit(FI_INLINE_DOTS, &fi->flags);
> >  	if (ri->i_inline & F2FS_EXTRA_ATTR)
> >  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> > +	if (ri->i_inline & F2FS_PIN_FILE)
> > +		set_bit(FI_PIN_FILE, &fi->flags);
> >  }
> >  
> >  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> > @@ -2258,6 +2277,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> >  		ri->i_inline |= F2FS_INLINE_DOTS;
> >  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
> >  		ri->i_inline |= F2FS_EXTRA_ATTR;
> > +	if (is_inode_flag_set(inode, FI_PIN_FILE))
> > +		ri->i_inline |= F2FS_PIN_FILE;
> >  }
> >  
> >  static inline int f2fs_has_extra_attr(struct inode *inode)
> > @@ -2303,6 +2324,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
> >  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
> >  }
> >  
> > +static inline bool f2fs_is_pinned_file(struct inode *inode)
> > +{
> > +	return is_inode_flag_set(inode, FI_PIN_FILE);
> > +}
> > +
> >  static inline bool f2fs_is_atomic_file(struct inode *inode)
> >  {
> >  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> > @@ -2518,6 +2544,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
> >  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
> >  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
> >  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> > +int f2fs_pin_file_control(struct inode *inode, bool inc);
> >  
> >  /*
> >   * inode.c
> > diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> > index 1db68f1bcd77..5a3b9a07d72d 100644
> > --- a/fs/f2fs/file.c
> > +++ b/fs/f2fs/file.c
> > @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
> >  	return 0;
> >  }
> >  
> > +int f2fs_pin_file_control(struct inode *inode, bool inc)
> > +{
> > +	struct f2fs_inode_info *fi = F2FS_I(inode);
> > +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> > +
> > +	/* Use i_gc_failures for normal file as a risk signal. */
> > +	if (inc)
> > +		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
> > +
> > +	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
> > +		f2fs_msg(sbi->sb, KERN_WARNING,
> > +			"%s: Enable GC = ino %lx after %x GC trials\n",
> > +			__func__, inode->i_ino, fi->i_gc_failures);
> > +		return -EAGAIN;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int f2fs_ioc_pin_file(struct file *filp)
> > +{
> > +	struct inode *inode = file_inode(filp);
> > +	int ret;
> > +
> > +	if (!inode_owner_or_capable(inode))
> > +		return -EACCES;
> > +
> > +	if (!S_ISREG(inode->i_mode))
> > +		return -EINVAL;
> > +
> > +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> > +		return -EROFS;
> > +
> > +	inode_lock(inode);
> > +
> > +	if (f2fs_pin_file_control(inode, false)) {
> > +		ret = -EAGAIN;
> > +		goto out;
> > +	}
> > +
> > +	ret = f2fs_convert_inline_inode(inode);
> > +	if (ret)
> > +		goto out;
> > +
> > +	set_inode_flag(inode, FI_PIN_FILE);
> > +	ret = F2FS_I(inode)->i_gc_failures;
> > +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> > +out:
> > +	inode_unlock(inode);
> > +	return ret;
> > +}
> > +
> >  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >  {
> >  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> > @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >  		return f2fs_ioc_fsgetxattr(filp, arg);
> >  	case F2FS_IOC_FSSETXATTR:
> >  		return f2fs_ioc_fssetxattr(filp, arg);
> > +	case F2FS_IOC_PIN_FILE:
> > +		return f2fs_ioc_pin_file(filp);
> >  	default:
> >  		return -ENOTTY;
> >  	}
> > @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >  	case F2FS_IOC_GET_FEATURES:
> >  	case F2FS_IOC_FSGETXATTR:
> >  	case F2FS_IOC_FSSETXATTR:
> > +	case F2FS_IOC_PIN_FILE:
> >  		break;
> >  	default:
> >  		return -ENOIOCTLCMD;
> > diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> > index 5d5bba462f26..9bffef153a12 100644
> > --- a/fs/f2fs/gc.c
> > +++ b/fs/f2fs/gc.c
> > @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
> >  	if (f2fs_is_atomic_file(inode))
> >  		goto out;
> >  
> > +	if (f2fs_is_pinned_file(inode)) {
> > +		f2fs_pin_file_control(inode, true);
> > +		goto out;
> > +	}
> > +
> >  	set_new_dnode(&dn, inode, NULL, NULL, 0);
> >  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
> >  	if (err)
> > @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
> >  
> >  	if (f2fs_is_atomic_file(inode))
> >  		goto out;
> > +	if (f2fs_is_pinned_file(inode)) {
> > +		if (gc_type == FG_GC)
> > +			f2fs_pin_file_control(inode, true);
> > +		goto out;
> > +	}
> >  
> >  	if (gc_type == BG_GC) {
> >  		if (PageWriteback(page))
> > @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
> >  
> >  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
> >  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> > +	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
> >  
> >  	/* give warm/cold data area from slower device */
> >  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> > diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> > index 9325191fab2d..33310165cb78 100644
> > --- a/fs/f2fs/gc.h
> > +++ b/fs/f2fs/gc.h
> > @@ -20,6 +20,8 @@
> >  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
> >  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
> >  
> > +#define DEF_GC_FAILED_PINNED_FILES	1024
> > +
> >  /* Search max. number of dirty segments to select a victim segment */
> >  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
> >  
> > diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> > index 581c7ce23c22..0e3253d7cb10 100644
> > --- a/fs/f2fs/sysfs.c
> > +++ b/fs/f2fs/sysfs.c
> > @@ -303,6 +303,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
> >  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
> >  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
> >  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> > +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
> >  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
> >  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> > @@ -351,6 +352,7 @@ static struct attribute *f2fs_attrs[] = {
> >  	ATTR_LIST(idle_interval),
> >  	ATTR_LIST(iostat_enable),
> >  	ATTR_LIST(readdir_ra),
> > +	ATTR_LIST(gc_pin_file_thresh),
> >  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >  	ATTR_LIST(inject_rate),
> >  	ATTR_LIST(inject_type),
> > diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> > index 43e98d30d2df..cfb522e6affc 100644
> > --- a/include/linux/f2fs_fs.h
> > +++ b/include/linux/f2fs_fs.h
> > @@ -212,6 +212,7 @@ struct f2fs_extent {
> >  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
> >  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
> >  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> > +#define F2FS_PIN_FILE		0x40	/* file should not be gced */
> >  
> >  struct f2fs_inode {
> >  	__le16 i_mode;			/* file mode */
> > @@ -229,7 +230,13 @@ struct f2fs_inode {
> >  	__le32 i_ctime_nsec;		/* change time in nano scale */
> >  	__le32 i_mtime_nsec;		/* modification time in nano scale */
> >  	__le32 i_generation;		/* file version (for NFS) */
> > -	__le32 i_current_depth;		/* only for directory depth */
> > +	union {
> > +		__le32 i_current_depth;	/* only for directory depth */
> > +		__le16 i_gc_failures;	/*
> > +					 * # of gc failures on pinned file.
> > +					 * only for regular files.
> > +					 */
> > +	};
> >  	__le32 i_xattr_nid;		/* nid to save xattr */
> >  	__le32 i_flags;			/* file attributes */
> >  	__le32 i_pino;			/* parent inode number */
> > 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] f2fs: add an ioctl to disable GC for specific file
@ 2018-01-01  1:07         ` Jaegeuk Kim
  0 siblings, 0 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2018-01-01  1:07 UTC (permalink / raw)
  To: Chao Yu; +Cc: linux-kernel, linux-f2fs-devel

On 12/29, Chao Yu wrote:
> On 2017/12/28 11:40, Jaegeuk Kim wrote:
> > This patch gives a flag to disable GC on given file, which would be useful, when
> > user wants to keep its block map. It also conducts in-place-update for dontmove
> > file.
> 
> One question, we may encounter out-of-space during foreground GC if there
> is too many data blocks of pinned files as they are removable.
> 
> Do we need to record all segments which contain block of pinned file? and
> then trigger foreground GC more early by adjust threshold in
> has_not_enough_free_secs.

This should be used only when application knows how this works. If not, I wanted
to just unset the flag as much as possible, as foreground GCs have been done for
pinned file too frequently. I expect the usecase must be quite unusual with just
small number of files in short period likewise atomic files.

Thanks,

> 
> Thanks,
> 
> > 
> > Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> > ---
> > 
> > Change log from v2:
> >  - change the naming from dontmove to pin_file
> >  - do in-place-update for pinned files
> >  - use union for i_current_depth with i_gc_failures
> >  - pass increasing i_gc_failures
> > 
> >  fs/f2fs/data.c          |  2 ++
> >  fs/f2fs/f2fs.h          | 29 +++++++++++++++++++++++++-
> >  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  fs/f2fs/gc.c            | 11 ++++++++++
> >  fs/f2fs/gc.h            |  2 ++
> >  fs/f2fs/sysfs.c         |  2 ++
> >  include/linux/f2fs_fs.h |  9 ++++++++-
> >  7 files changed, 107 insertions(+), 2 deletions(-)
> > 
> > diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
> > index 7aca6ccd01f6..b9fab6186f28 100644
> > --- a/fs/f2fs/data.c
> > +++ b/fs/f2fs/data.c
> > @@ -1394,6 +1394,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
> >  {
> >  	struct inode *inode = fio->page->mapping->host;
> >  
> > +	if (f2fs_is_pinned_file(inode))
> > +		return true;
> >  	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
> >  		return false;
> >  	if (is_cold_data(fio->page))
> > diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> > index 65a51b941146..398ed95d4036 100644
> > --- a/fs/f2fs/f2fs.h
> > +++ b/fs/f2fs/f2fs.h
> > @@ -376,6 +376,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
> >  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
> >  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
> >  
> > +#define F2FS_IOC_PIN_FILE		_IO(F2FS_IOCTL_MAGIC, 13)
> > +
> >  struct f2fs_gc_range {
> >  	u32 sync;
> >  	u64 start;
> > @@ -586,7 +588,10 @@ struct f2fs_inode_info {
> >  	unsigned long i_flags;		/* keep an inode flags for ioctl */
> >  	unsigned char i_advise;		/* use to give file attribute hints */
> >  	unsigned char i_dir_level;	/* use for dentry level for large dir */
> > -	unsigned int i_current_depth;	/* use only in directory structure */
> > +	union {
> > +		unsigned int i_current_depth;	/* only for directory depth */
> > +		unsigned short i_gc_failures;	/* only for regular file */
> > +	};
> >  	unsigned int i_pino;		/* parent inode number */
> >  	umode_t i_acl_mode;		/* keep file acl mode temporarily */
> >  
> > @@ -1130,6 +1135,9 @@ struct f2fs_sb_info {
> >  	/* threshold for converting bg victims for fg */
> >  	u64 fggc_threshold;
> >  
> > +	/* threshold for gc trials on pinned files */
> > +	u64 gc_pin_file_threshold;
> > +
> >  	/* maximum # of trials to find a victim segment for SSR and GC */
> >  	unsigned int max_victim_search;
> >  
> > @@ -2119,6 +2127,7 @@ enum {
> >  	FI_HOT_DATA,		/* indicate file is hot */
> >  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
> >  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> > +	FI_PIN_FILE,		/* indicate file should not be gced */
> >  };
> >  
> >  static inline void __mark_inode_dirty_flag(struct inode *inode,
> > @@ -2132,6 +2141,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
> >  			return;
> >  	case FI_DATA_EXIST:
> >  	case FI_INLINE_DOTS:
> > +	case FI_PIN_FILE:
> >  		f2fs_mark_inode_dirty_sync(inode, true);
> >  	}
> >  }
> > @@ -2212,6 +2222,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
> >  	f2fs_mark_inode_dirty_sync(inode, true);
> >  }
> >  
> > +static inline void f2fs_i_gc_failures_write(struct inode *inode,
> > +					unsigned int count)
> > +{
> > +	F2FS_I(inode)->i_gc_failures = count;
> > +	f2fs_mark_inode_dirty_sync(inode, true);
> > +}
> > +
> >  static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
> >  {
> >  	F2FS_I(inode)->i_xattr_nid = xnid;
> > @@ -2240,6 +2257,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
> >  		set_bit(FI_INLINE_DOTS, &fi->flags);
> >  	if (ri->i_inline & F2FS_EXTRA_ATTR)
> >  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> > +	if (ri->i_inline & F2FS_PIN_FILE)
> > +		set_bit(FI_PIN_FILE, &fi->flags);
> >  }
> >  
> >  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> > @@ -2258,6 +2277,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> >  		ri->i_inline |= F2FS_INLINE_DOTS;
> >  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
> >  		ri->i_inline |= F2FS_EXTRA_ATTR;
> > +	if (is_inode_flag_set(inode, FI_PIN_FILE))
> > +		ri->i_inline |= F2FS_PIN_FILE;
> >  }
> >  
> >  static inline int f2fs_has_extra_attr(struct inode *inode)
> > @@ -2303,6 +2324,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
> >  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
> >  }
> >  
> > +static inline bool f2fs_is_pinned_file(struct inode *inode)
> > +{
> > +	return is_inode_flag_set(inode, FI_PIN_FILE);
> > +}
> > +
> >  static inline bool f2fs_is_atomic_file(struct inode *inode)
> >  {
> >  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> > @@ -2518,6 +2544,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
> >  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
> >  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
> >  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> > +int f2fs_pin_file_control(struct inode *inode, bool inc);
> >  
> >  /*
> >   * inode.c
> > diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> > index 1db68f1bcd77..5a3b9a07d72d 100644
> > --- a/fs/f2fs/file.c
> > +++ b/fs/f2fs/file.c
> > @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
> >  	return 0;
> >  }
> >  
> > +int f2fs_pin_file_control(struct inode *inode, bool inc)
> > +{
> > +	struct f2fs_inode_info *fi = F2FS_I(inode);
> > +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> > +
> > +	/* Use i_gc_failures for normal file as a risk signal. */
> > +	if (inc)
> > +		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
> > +
> > +	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
> > +		f2fs_msg(sbi->sb, KERN_WARNING,
> > +			"%s: Enable GC = ino %lx after %x GC trials\n",
> > +			__func__, inode->i_ino, fi->i_gc_failures);
> > +		return -EAGAIN;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int f2fs_ioc_pin_file(struct file *filp)
> > +{
> > +	struct inode *inode = file_inode(filp);
> > +	int ret;
> > +
> > +	if (!inode_owner_or_capable(inode))
> > +		return -EACCES;
> > +
> > +	if (!S_ISREG(inode->i_mode))
> > +		return -EINVAL;
> > +
> > +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> > +		return -EROFS;
> > +
> > +	inode_lock(inode);
> > +
> > +	if (f2fs_pin_file_control(inode, false)) {
> > +		ret = -EAGAIN;
> > +		goto out;
> > +	}
> > +
> > +	ret = f2fs_convert_inline_inode(inode);
> > +	if (ret)
> > +		goto out;
> > +
> > +	set_inode_flag(inode, FI_PIN_FILE);
> > +	ret = F2FS_I(inode)->i_gc_failures;
> > +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> > +out:
> > +	inode_unlock(inode);
> > +	return ret;
> > +}
> > +
> >  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >  {
> >  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> > @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >  		return f2fs_ioc_fsgetxattr(filp, arg);
> >  	case F2FS_IOC_FSSETXATTR:
> >  		return f2fs_ioc_fssetxattr(filp, arg);
> > +	case F2FS_IOC_PIN_FILE:
> > +		return f2fs_ioc_pin_file(filp);
> >  	default:
> >  		return -ENOTTY;
> >  	}
> > @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >  	case F2FS_IOC_GET_FEATURES:
> >  	case F2FS_IOC_FSGETXATTR:
> >  	case F2FS_IOC_FSSETXATTR:
> > +	case F2FS_IOC_PIN_FILE:
> >  		break;
> >  	default:
> >  		return -ENOIOCTLCMD;
> > diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> > index 5d5bba462f26..9bffef153a12 100644
> > --- a/fs/f2fs/gc.c
> > +++ b/fs/f2fs/gc.c
> > @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
> >  	if (f2fs_is_atomic_file(inode))
> >  		goto out;
> >  
> > +	if (f2fs_is_pinned_file(inode)) {
> > +		f2fs_pin_file_control(inode, true);
> > +		goto out;
> > +	}
> > +
> >  	set_new_dnode(&dn, inode, NULL, NULL, 0);
> >  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
> >  	if (err)
> > @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
> >  
> >  	if (f2fs_is_atomic_file(inode))
> >  		goto out;
> > +	if (f2fs_is_pinned_file(inode)) {
> > +		if (gc_type == FG_GC)
> > +			f2fs_pin_file_control(inode, true);
> > +		goto out;
> > +	}
> >  
> >  	if (gc_type == BG_GC) {
> >  		if (PageWriteback(page))
> > @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
> >  
> >  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
> >  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> > +	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
> >  
> >  	/* give warm/cold data area from slower device */
> >  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> > diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> > index 9325191fab2d..33310165cb78 100644
> > --- a/fs/f2fs/gc.h
> > +++ b/fs/f2fs/gc.h
> > @@ -20,6 +20,8 @@
> >  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
> >  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
> >  
> > +#define DEF_GC_FAILED_PINNED_FILES	1024
> > +
> >  /* Search max. number of dirty segments to select a victim segment */
> >  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
> >  
> > diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> > index 581c7ce23c22..0e3253d7cb10 100644
> > --- a/fs/f2fs/sysfs.c
> > +++ b/fs/f2fs/sysfs.c
> > @@ -303,6 +303,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
> >  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
> >  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
> >  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> > +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
> >  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
> >  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> > @@ -351,6 +352,7 @@ static struct attribute *f2fs_attrs[] = {
> >  	ATTR_LIST(idle_interval),
> >  	ATTR_LIST(iostat_enable),
> >  	ATTR_LIST(readdir_ra),
> > +	ATTR_LIST(gc_pin_file_thresh),
> >  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >  	ATTR_LIST(inject_rate),
> >  	ATTR_LIST(inject_type),
> > diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> > index 43e98d30d2df..cfb522e6affc 100644
> > --- a/include/linux/f2fs_fs.h
> > +++ b/include/linux/f2fs_fs.h
> > @@ -212,6 +212,7 @@ struct f2fs_extent {
> >  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
> >  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
> >  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> > +#define F2FS_PIN_FILE		0x40	/* file should not be gced */
> >  
> >  struct f2fs_inode {
> >  	__le16 i_mode;			/* file mode */
> > @@ -229,7 +230,13 @@ struct f2fs_inode {
> >  	__le32 i_ctime_nsec;		/* change time in nano scale */
> >  	__le32 i_mtime_nsec;		/* modification time in nano scale */
> >  	__le32 i_generation;		/* file version (for NFS) */
> > -	__le32 i_current_depth;		/* only for directory depth */
> > +	union {
> > +		__le32 i_current_depth;	/* only for directory depth */
> > +		__le16 i_gc_failures;	/*
> > +					 * # of gc failures on pinned file.
> > +					 * only for regular files.
> > +					 */
> > +	};
> >  	__le32 i_xattr_nid;		/* nid to save xattr */
> >  	__le32 i_flags;			/* file attributes */
> >  	__le32 i_pino;			/* parent inode number */
> > 

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] f2fs: add an ioctl to disable GC for specific file
  2018-01-01  1:07         ` Jaegeuk Kim
@ 2018-01-02  3:29           ` Chao Yu
  -1 siblings, 0 replies; 27+ messages in thread
From: Chao Yu @ 2018-01-02  3:29 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: linux-kernel, linux-f2fs-devel

On 2018/1/1 9:07, Jaegeuk Kim wrote:
> On 12/29, Chao Yu wrote:
>> On 2017/12/28 11:40, Jaegeuk Kim wrote:
>>> This patch gives a flag to disable GC on given file, which would be useful, when
>>> user wants to keep its block map. It also conducts in-place-update for dontmove
>>> file.
>>
>> One question, we may encounter out-of-space during foreground GC if there
>> is too many data blocks of pinned files as they are removable.
>>
>> Do we need to record all segments which contain block of pinned file? and
>> then trigger foreground GC more early by adjust threshold in
>> has_not_enough_free_secs.
> 
> This should be used only when application knows how this works. If not, I wanted
> to just unset the flag as much as possible, as foreground GCs have been done for

Well, how can we unset that flag? do we need an extra ioctl interface like
F2FS_IOC_UNPIN_FILE? so that user can keep its data in unpinned file by
calling that interface. Or user can only drop the flag by unlinking pinned
file?

Thanks,

> pinned file too frequently. I expect the usecase must be quite unusual with just
> small number of files in short period likewise atomic files.
> 
> Thanks,
> 
>>
>> Thanks,
>>
>>>
>>> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
>>> ---
>>>
>>> Change log from v2:
>>>  - change the naming from dontmove to pin_file
>>>  - do in-place-update for pinned files
>>>  - use union for i_current_depth with i_gc_failures
>>>  - pass increasing i_gc_failures
>>>
>>>  fs/f2fs/data.c          |  2 ++
>>>  fs/f2fs/f2fs.h          | 29 +++++++++++++++++++++++++-
>>>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>  fs/f2fs/gc.c            | 11 ++++++++++
>>>  fs/f2fs/gc.h            |  2 ++
>>>  fs/f2fs/sysfs.c         |  2 ++
>>>  include/linux/f2fs_fs.h |  9 ++++++++-
>>>  7 files changed, 107 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
>>> index 7aca6ccd01f6..b9fab6186f28 100644
>>> --- a/fs/f2fs/data.c
>>> +++ b/fs/f2fs/data.c
>>> @@ -1394,6 +1394,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
>>>  {
>>>  	struct inode *inode = fio->page->mapping->host;
>>>  
>>> +	if (f2fs_is_pinned_file(inode))
>>> +		return true;
>>>  	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
>>>  		return false;
>>>  	if (is_cold_data(fio->page))
>>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>>> index 65a51b941146..398ed95d4036 100644
>>> --- a/fs/f2fs/f2fs.h
>>> +++ b/fs/f2fs/f2fs.h
>>> @@ -376,6 +376,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
>>>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
>>>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
>>>  
>>> +#define F2FS_IOC_PIN_FILE		_IO(F2FS_IOCTL_MAGIC, 13)
>>> +
>>>  struct f2fs_gc_range {
>>>  	u32 sync;
>>>  	u64 start;
>>> @@ -586,7 +588,10 @@ struct f2fs_inode_info {
>>>  	unsigned long i_flags;		/* keep an inode flags for ioctl */
>>>  	unsigned char i_advise;		/* use to give file attribute hints */
>>>  	unsigned char i_dir_level;	/* use for dentry level for large dir */
>>> -	unsigned int i_current_depth;	/* use only in directory structure */
>>> +	union {
>>> +		unsigned int i_current_depth;	/* only for directory depth */
>>> +		unsigned short i_gc_failures;	/* only for regular file */
>>> +	};
>>>  	unsigned int i_pino;		/* parent inode number */
>>>  	umode_t i_acl_mode;		/* keep file acl mode temporarily */
>>>  
>>> @@ -1130,6 +1135,9 @@ struct f2fs_sb_info {
>>>  	/* threshold for converting bg victims for fg */
>>>  	u64 fggc_threshold;
>>>  
>>> +	/* threshold for gc trials on pinned files */
>>> +	u64 gc_pin_file_threshold;
>>> +
>>>  	/* maximum # of trials to find a victim segment for SSR and GC */
>>>  	unsigned int max_victim_search;
>>>  
>>> @@ -2119,6 +2127,7 @@ enum {
>>>  	FI_HOT_DATA,		/* indicate file is hot */
>>>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
>>>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
>>> +	FI_PIN_FILE,		/* indicate file should not be gced */
>>>  };
>>>  
>>>  static inline void __mark_inode_dirty_flag(struct inode *inode,
>>> @@ -2132,6 +2141,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
>>>  			return;
>>>  	case FI_DATA_EXIST:
>>>  	case FI_INLINE_DOTS:
>>> +	case FI_PIN_FILE:
>>>  		f2fs_mark_inode_dirty_sync(inode, true);
>>>  	}
>>>  }
>>> @@ -2212,6 +2222,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
>>>  	f2fs_mark_inode_dirty_sync(inode, true);
>>>  }
>>>  
>>> +static inline void f2fs_i_gc_failures_write(struct inode *inode,
>>> +					unsigned int count)
>>> +{
>>> +	F2FS_I(inode)->i_gc_failures = count;
>>> +	f2fs_mark_inode_dirty_sync(inode, true);
>>> +}
>>> +
>>>  static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
>>>  {
>>>  	F2FS_I(inode)->i_xattr_nid = xnid;
>>> @@ -2240,6 +2257,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
>>>  		set_bit(FI_INLINE_DOTS, &fi->flags);
>>>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
>>>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
>>> +	if (ri->i_inline & F2FS_PIN_FILE)
>>> +		set_bit(FI_PIN_FILE, &fi->flags);
>>>  }
>>>  
>>>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>>> @@ -2258,6 +2277,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>>>  		ri->i_inline |= F2FS_INLINE_DOTS;
>>>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
>>>  		ri->i_inline |= F2FS_EXTRA_ATTR;
>>> +	if (is_inode_flag_set(inode, FI_PIN_FILE))
>>> +		ri->i_inline |= F2FS_PIN_FILE;
>>>  }
>>>  
>>>  static inline int f2fs_has_extra_attr(struct inode *inode)
>>> @@ -2303,6 +2324,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
>>>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
>>>  }
>>>  
>>> +static inline bool f2fs_is_pinned_file(struct inode *inode)
>>> +{
>>> +	return is_inode_flag_set(inode, FI_PIN_FILE);
>>> +}
>>> +
>>>  static inline bool f2fs_is_atomic_file(struct inode *inode)
>>>  {
>>>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
>>> @@ -2518,6 +2544,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
>>>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
>>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
>>>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
>>> +int f2fs_pin_file_control(struct inode *inode, bool inc);
>>>  
>>>  /*
>>>   * inode.c
>>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
>>> index 1db68f1bcd77..5a3b9a07d72d 100644
>>> --- a/fs/f2fs/file.c
>>> +++ b/fs/f2fs/file.c
>>> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
>>>  	return 0;
>>>  }
>>>  
>>> +int f2fs_pin_file_control(struct inode *inode, bool inc)
>>> +{
>>> +	struct f2fs_inode_info *fi = F2FS_I(inode);
>>> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>>> +
>>> +	/* Use i_gc_failures for normal file as a risk signal. */
>>> +	if (inc)
>>> +		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
>>> +
>>> +	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
>>> +		f2fs_msg(sbi->sb, KERN_WARNING,
>>> +			"%s: Enable GC = ino %lx after %x GC trials\n",
>>> +			__func__, inode->i_ino, fi->i_gc_failures);
>>> +		return -EAGAIN;
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +static int f2fs_ioc_pin_file(struct file *filp)
>>> +{
>>> +	struct inode *inode = file_inode(filp);
>>> +	int ret;
>>> +
>>> +	if (!inode_owner_or_capable(inode))
>>> +		return -EACCES;
>>> +
>>> +	if (!S_ISREG(inode->i_mode))
>>> +		return -EINVAL;
>>> +
>>> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
>>> +		return -EROFS;
>>> +
>>> +	inode_lock(inode);
>>> +
>>> +	if (f2fs_pin_file_control(inode, false)) {
>>> +		ret = -EAGAIN;
>>> +		goto out;
>>> +	}
>>> +
>>> +	ret = f2fs_convert_inline_inode(inode);
>>> +	if (ret)
>>> +		goto out;
>>> +
>>> +	set_inode_flag(inode, FI_PIN_FILE);
>>> +	ret = F2FS_I(inode)->i_gc_failures;
>>> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>>> +out:
>>> +	inode_unlock(inode);
>>> +	return ret;
>>> +}
>>> +
>>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>>  {
>>>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
>>> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>>  		return f2fs_ioc_fsgetxattr(filp, arg);
>>>  	case F2FS_IOC_FSSETXATTR:
>>>  		return f2fs_ioc_fssetxattr(filp, arg);
>>> +	case F2FS_IOC_PIN_FILE:
>>> +		return f2fs_ioc_pin_file(filp);
>>>  	default:
>>>  		return -ENOTTY;
>>>  	}
>>> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>>  	case F2FS_IOC_GET_FEATURES:
>>>  	case F2FS_IOC_FSGETXATTR:
>>>  	case F2FS_IOC_FSSETXATTR:
>>> +	case F2FS_IOC_PIN_FILE:
>>>  		break;
>>>  	default:
>>>  		return -ENOIOCTLCMD;
>>> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
>>> index 5d5bba462f26..9bffef153a12 100644
>>> --- a/fs/f2fs/gc.c
>>> +++ b/fs/f2fs/gc.c
>>> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
>>>  	if (f2fs_is_atomic_file(inode))
>>>  		goto out;
>>>  
>>> +	if (f2fs_is_pinned_file(inode)) {
>>> +		f2fs_pin_file_control(inode, true);
>>> +		goto out;
>>> +	}
>>> +
>>>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
>>>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
>>>  	if (err)
>>> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
>>>  
>>>  	if (f2fs_is_atomic_file(inode))
>>>  		goto out;
>>> +	if (f2fs_is_pinned_file(inode)) {
>>> +		if (gc_type == FG_GC)
>>> +			f2fs_pin_file_control(inode, true);
>>> +		goto out;
>>> +	}
>>>  
>>>  	if (gc_type == BG_GC) {
>>>  		if (PageWriteback(page))
>>> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
>>>  
>>>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
>>>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
>>> +	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
>>>  
>>>  	/* give warm/cold data area from slower device */
>>>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
>>> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
>>> index 9325191fab2d..33310165cb78 100644
>>> --- a/fs/f2fs/gc.h
>>> +++ b/fs/f2fs/gc.h
>>> @@ -20,6 +20,8 @@
>>>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
>>>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
>>>  
>>> +#define DEF_GC_FAILED_PINNED_FILES	1024
>>> +
>>>  /* Search max. number of dirty segments to select a victim segment */
>>>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
>>>  
>>> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
>>> index 581c7ce23c22..0e3253d7cb10 100644
>>> --- a/fs/f2fs/sysfs.c
>>> +++ b/fs/f2fs/sysfs.c
>>> @@ -303,6 +303,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
>>> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
>>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
>>>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
>>> @@ -351,6 +352,7 @@ static struct attribute *f2fs_attrs[] = {
>>>  	ATTR_LIST(idle_interval),
>>>  	ATTR_LIST(iostat_enable),
>>>  	ATTR_LIST(readdir_ra),
>>> +	ATTR_LIST(gc_pin_file_thresh),
>>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>>  	ATTR_LIST(inject_rate),
>>>  	ATTR_LIST(inject_type),
>>> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
>>> index 43e98d30d2df..cfb522e6affc 100644
>>> --- a/include/linux/f2fs_fs.h
>>> +++ b/include/linux/f2fs_fs.h
>>> @@ -212,6 +212,7 @@ struct f2fs_extent {
>>>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
>>>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
>>>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
>>> +#define F2FS_PIN_FILE		0x40	/* file should not be gced */
>>>  
>>>  struct f2fs_inode {
>>>  	__le16 i_mode;			/* file mode */
>>> @@ -229,7 +230,13 @@ struct f2fs_inode {
>>>  	__le32 i_ctime_nsec;		/* change time in nano scale */
>>>  	__le32 i_mtime_nsec;		/* modification time in nano scale */
>>>  	__le32 i_generation;		/* file version (for NFS) */
>>> -	__le32 i_current_depth;		/* only for directory depth */
>>> +	union {
>>> +		__le32 i_current_depth;	/* only for directory depth */
>>> +		__le16 i_gc_failures;	/*
>>> +					 * # of gc failures on pinned file.
>>> +					 * only for regular files.
>>> +					 */
>>> +	};
>>>  	__le32 i_xattr_nid;		/* nid to save xattr */
>>>  	__le32 i_flags;			/* file attributes */
>>>  	__le32 i_pino;			/* parent inode number */
>>>
> 
> .
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] f2fs: add an ioctl to disable GC for specific file
@ 2018-01-02  3:29           ` Chao Yu
  0 siblings, 0 replies; 27+ messages in thread
From: Chao Yu @ 2018-01-02  3:29 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: linux-kernel, linux-f2fs-devel

On 2018/1/1 9:07, Jaegeuk Kim wrote:
> On 12/29, Chao Yu wrote:
>> On 2017/12/28 11:40, Jaegeuk Kim wrote:
>>> This patch gives a flag to disable GC on given file, which would be useful, when
>>> user wants to keep its block map. It also conducts in-place-update for dontmove
>>> file.
>>
>> One question, we may encounter out-of-space during foreground GC if there
>> is too many data blocks of pinned files as they are removable.
>>
>> Do we need to record all segments which contain block of pinned file? and
>> then trigger foreground GC more early by adjust threshold in
>> has_not_enough_free_secs.
> 
> This should be used only when application knows how this works. If not, I wanted
> to just unset the flag as much as possible, as foreground GCs have been done for

Well, how can we unset that flag? do we need an extra ioctl interface like
F2FS_IOC_UNPIN_FILE? so that user can keep its data in unpinned file by
calling that interface. Or user can only drop the flag by unlinking pinned
file?

Thanks,

> pinned file too frequently. I expect the usecase must be quite unusual with just
> small number of files in short period likewise atomic files.
> 
> Thanks,
> 
>>
>> Thanks,
>>
>>>
>>> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
>>> ---
>>>
>>> Change log from v2:
>>>  - change the naming from dontmove to pin_file
>>>  - do in-place-update for pinned files
>>>  - use union for i_current_depth with i_gc_failures
>>>  - pass increasing i_gc_failures
>>>
>>>  fs/f2fs/data.c          |  2 ++
>>>  fs/f2fs/f2fs.h          | 29 +++++++++++++++++++++++++-
>>>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>  fs/f2fs/gc.c            | 11 ++++++++++
>>>  fs/f2fs/gc.h            |  2 ++
>>>  fs/f2fs/sysfs.c         |  2 ++
>>>  include/linux/f2fs_fs.h |  9 ++++++++-
>>>  7 files changed, 107 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
>>> index 7aca6ccd01f6..b9fab6186f28 100644
>>> --- a/fs/f2fs/data.c
>>> +++ b/fs/f2fs/data.c
>>> @@ -1394,6 +1394,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
>>>  {
>>>  	struct inode *inode = fio->page->mapping->host;
>>>  
>>> +	if (f2fs_is_pinned_file(inode))
>>> +		return true;
>>>  	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
>>>  		return false;
>>>  	if (is_cold_data(fio->page))
>>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>>> index 65a51b941146..398ed95d4036 100644
>>> --- a/fs/f2fs/f2fs.h
>>> +++ b/fs/f2fs/f2fs.h
>>> @@ -376,6 +376,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
>>>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
>>>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
>>>  
>>> +#define F2FS_IOC_PIN_FILE		_IO(F2FS_IOCTL_MAGIC, 13)
>>> +
>>>  struct f2fs_gc_range {
>>>  	u32 sync;
>>>  	u64 start;
>>> @@ -586,7 +588,10 @@ struct f2fs_inode_info {
>>>  	unsigned long i_flags;		/* keep an inode flags for ioctl */
>>>  	unsigned char i_advise;		/* use to give file attribute hints */
>>>  	unsigned char i_dir_level;	/* use for dentry level for large dir */
>>> -	unsigned int i_current_depth;	/* use only in directory structure */
>>> +	union {
>>> +		unsigned int i_current_depth;	/* only for directory depth */
>>> +		unsigned short i_gc_failures;	/* only for regular file */
>>> +	};
>>>  	unsigned int i_pino;		/* parent inode number */
>>>  	umode_t i_acl_mode;		/* keep file acl mode temporarily */
>>>  
>>> @@ -1130,6 +1135,9 @@ struct f2fs_sb_info {
>>>  	/* threshold for converting bg victims for fg */
>>>  	u64 fggc_threshold;
>>>  
>>> +	/* threshold for gc trials on pinned files */
>>> +	u64 gc_pin_file_threshold;
>>> +
>>>  	/* maximum # of trials to find a victim segment for SSR and GC */
>>>  	unsigned int max_victim_search;
>>>  
>>> @@ -2119,6 +2127,7 @@ enum {
>>>  	FI_HOT_DATA,		/* indicate file is hot */
>>>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
>>>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
>>> +	FI_PIN_FILE,		/* indicate file should not be gced */
>>>  };
>>>  
>>>  static inline void __mark_inode_dirty_flag(struct inode *inode,
>>> @@ -2132,6 +2141,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
>>>  			return;
>>>  	case FI_DATA_EXIST:
>>>  	case FI_INLINE_DOTS:
>>> +	case FI_PIN_FILE:
>>>  		f2fs_mark_inode_dirty_sync(inode, true);
>>>  	}
>>>  }
>>> @@ -2212,6 +2222,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
>>>  	f2fs_mark_inode_dirty_sync(inode, true);
>>>  }
>>>  
>>> +static inline void f2fs_i_gc_failures_write(struct inode *inode,
>>> +					unsigned int count)
>>> +{
>>> +	F2FS_I(inode)->i_gc_failures = count;
>>> +	f2fs_mark_inode_dirty_sync(inode, true);
>>> +}
>>> +
>>>  static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
>>>  {
>>>  	F2FS_I(inode)->i_xattr_nid = xnid;
>>> @@ -2240,6 +2257,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
>>>  		set_bit(FI_INLINE_DOTS, &fi->flags);
>>>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
>>>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
>>> +	if (ri->i_inline & F2FS_PIN_FILE)
>>> +		set_bit(FI_PIN_FILE, &fi->flags);
>>>  }
>>>  
>>>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>>> @@ -2258,6 +2277,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>>>  		ri->i_inline |= F2FS_INLINE_DOTS;
>>>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
>>>  		ri->i_inline |= F2FS_EXTRA_ATTR;
>>> +	if (is_inode_flag_set(inode, FI_PIN_FILE))
>>> +		ri->i_inline |= F2FS_PIN_FILE;
>>>  }
>>>  
>>>  static inline int f2fs_has_extra_attr(struct inode *inode)
>>> @@ -2303,6 +2324,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
>>>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
>>>  }
>>>  
>>> +static inline bool f2fs_is_pinned_file(struct inode *inode)
>>> +{
>>> +	return is_inode_flag_set(inode, FI_PIN_FILE);
>>> +}
>>> +
>>>  static inline bool f2fs_is_atomic_file(struct inode *inode)
>>>  {
>>>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
>>> @@ -2518,6 +2544,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
>>>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
>>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
>>>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
>>> +int f2fs_pin_file_control(struct inode *inode, bool inc);
>>>  
>>>  /*
>>>   * inode.c
>>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
>>> index 1db68f1bcd77..5a3b9a07d72d 100644
>>> --- a/fs/f2fs/file.c
>>> +++ b/fs/f2fs/file.c
>>> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
>>>  	return 0;
>>>  }
>>>  
>>> +int f2fs_pin_file_control(struct inode *inode, bool inc)
>>> +{
>>> +	struct f2fs_inode_info *fi = F2FS_I(inode);
>>> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>>> +
>>> +	/* Use i_gc_failures for normal file as a risk signal. */
>>> +	if (inc)
>>> +		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
>>> +
>>> +	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
>>> +		f2fs_msg(sbi->sb, KERN_WARNING,
>>> +			"%s: Enable GC = ino %lx after %x GC trials\n",
>>> +			__func__, inode->i_ino, fi->i_gc_failures);
>>> +		return -EAGAIN;
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +static int f2fs_ioc_pin_file(struct file *filp)
>>> +{
>>> +	struct inode *inode = file_inode(filp);
>>> +	int ret;
>>> +
>>> +	if (!inode_owner_or_capable(inode))
>>> +		return -EACCES;
>>> +
>>> +	if (!S_ISREG(inode->i_mode))
>>> +		return -EINVAL;
>>> +
>>> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
>>> +		return -EROFS;
>>> +
>>> +	inode_lock(inode);
>>> +
>>> +	if (f2fs_pin_file_control(inode, false)) {
>>> +		ret = -EAGAIN;
>>> +		goto out;
>>> +	}
>>> +
>>> +	ret = f2fs_convert_inline_inode(inode);
>>> +	if (ret)
>>> +		goto out;
>>> +
>>> +	set_inode_flag(inode, FI_PIN_FILE);
>>> +	ret = F2FS_I(inode)->i_gc_failures;
>>> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>>> +out:
>>> +	inode_unlock(inode);
>>> +	return ret;
>>> +}
>>> +
>>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>>  {
>>>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
>>> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>>  		return f2fs_ioc_fsgetxattr(filp, arg);
>>>  	case F2FS_IOC_FSSETXATTR:
>>>  		return f2fs_ioc_fssetxattr(filp, arg);
>>> +	case F2FS_IOC_PIN_FILE:
>>> +		return f2fs_ioc_pin_file(filp);
>>>  	default:
>>>  		return -ENOTTY;
>>>  	}
>>> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>>  	case F2FS_IOC_GET_FEATURES:
>>>  	case F2FS_IOC_FSGETXATTR:
>>>  	case F2FS_IOC_FSSETXATTR:
>>> +	case F2FS_IOC_PIN_FILE:
>>>  		break;
>>>  	default:
>>>  		return -ENOIOCTLCMD;
>>> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
>>> index 5d5bba462f26..9bffef153a12 100644
>>> --- a/fs/f2fs/gc.c
>>> +++ b/fs/f2fs/gc.c
>>> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
>>>  	if (f2fs_is_atomic_file(inode))
>>>  		goto out;
>>>  
>>> +	if (f2fs_is_pinned_file(inode)) {
>>> +		f2fs_pin_file_control(inode, true);
>>> +		goto out;
>>> +	}
>>> +
>>>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
>>>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
>>>  	if (err)
>>> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
>>>  
>>>  	if (f2fs_is_atomic_file(inode))
>>>  		goto out;
>>> +	if (f2fs_is_pinned_file(inode)) {
>>> +		if (gc_type == FG_GC)
>>> +			f2fs_pin_file_control(inode, true);
>>> +		goto out;
>>> +	}
>>>  
>>>  	if (gc_type == BG_GC) {
>>>  		if (PageWriteback(page))
>>> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
>>>  
>>>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
>>>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
>>> +	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
>>>  
>>>  	/* give warm/cold data area from slower device */
>>>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
>>> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
>>> index 9325191fab2d..33310165cb78 100644
>>> --- a/fs/f2fs/gc.h
>>> +++ b/fs/f2fs/gc.h
>>> @@ -20,6 +20,8 @@
>>>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
>>>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
>>>  
>>> +#define DEF_GC_FAILED_PINNED_FILES	1024
>>> +
>>>  /* Search max. number of dirty segments to select a victim segment */
>>>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
>>>  
>>> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
>>> index 581c7ce23c22..0e3253d7cb10 100644
>>> --- a/fs/f2fs/sysfs.c
>>> +++ b/fs/f2fs/sysfs.c
>>> @@ -303,6 +303,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
>>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
>>> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
>>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
>>>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
>>> @@ -351,6 +352,7 @@ static struct attribute *f2fs_attrs[] = {
>>>  	ATTR_LIST(idle_interval),
>>>  	ATTR_LIST(iostat_enable),
>>>  	ATTR_LIST(readdir_ra),
>>> +	ATTR_LIST(gc_pin_file_thresh),
>>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>>  	ATTR_LIST(inject_rate),
>>>  	ATTR_LIST(inject_type),
>>> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
>>> index 43e98d30d2df..cfb522e6affc 100644
>>> --- a/include/linux/f2fs_fs.h
>>> +++ b/include/linux/f2fs_fs.h
>>> @@ -212,6 +212,7 @@ struct f2fs_extent {
>>>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
>>>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
>>>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
>>> +#define F2FS_PIN_FILE		0x40	/* file should not be gced */
>>>  
>>>  struct f2fs_inode {
>>>  	__le16 i_mode;			/* file mode */
>>> @@ -229,7 +230,13 @@ struct f2fs_inode {
>>>  	__le32 i_ctime_nsec;		/* change time in nano scale */
>>>  	__le32 i_mtime_nsec;		/* modification time in nano scale */
>>>  	__le32 i_generation;		/* file version (for NFS) */
>>> -	__le32 i_current_depth;		/* only for directory depth */
>>> +	union {
>>> +		__le32 i_current_depth;	/* only for directory depth */
>>> +		__le16 i_gc_failures;	/*
>>> +					 * # of gc failures on pinned file.
>>> +					 * only for regular files.
>>> +					 */
>>> +	};
>>>  	__le32 i_xattr_nid;		/* nid to save xattr */
>>>  	__le32 i_flags;			/* file attributes */
>>>  	__le32 i_pino;			/* parent inode number */
>>>
> 
> .
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v3] f2fs: add an ioctl to disable GC for specific file
  2017-12-28  3:40     ` Jaegeuk Kim
  (?)
  (?)
@ 2018-01-03  3:21     ` Jaegeuk Kim
  2018-01-03  7:34         ` Chao Yu
  2018-01-16 23:57         ` Jaegeuk Kim
  -1 siblings, 2 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2018-01-03  3:21 UTC (permalink / raw)
  To: linux-kernel, linux-f2fs-devel

This patch gives a flag to disable GC on given file, which would be useful, when
user wants to keep its block map. It also conducts in-place-update for dontmove
file.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---

Change log from v2:
 - modify ioctl to allow users unpin the file

 fs/f2fs/data.c          |  2 ++
 fs/f2fs/f2fs.h          | 28 +++++++++++++++++++++-
 fs/f2fs/file.c          | 64 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/f2fs/gc.c            | 11 +++++++++
 fs/f2fs/gc.h            |  2 ++
 fs/f2fs/sysfs.c         |  2 ++
 include/linux/f2fs_fs.h |  9 ++++++-
 7 files changed, 116 insertions(+), 2 deletions(-)

diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 449b0aaa3905..45f65a5b9871 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1395,6 +1395,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
 {
 	struct inode *inode = fio->page->mapping->host;
 
+	if (f2fs_is_pinned_file(inode))
+		return true;
 	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
 		return false;
 	if (is_cold_data(fio->page))
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index a0e8eec23125..f4b7d73695a7 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -350,6 +350,7 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
 #define F2FS_IOC_GARBAGE_COLLECT_RANGE	_IOW(F2FS_IOCTL_MAGIC, 11,	\
 						struct f2fs_gc_range)
 #define F2FS_IOC_GET_FEATURES		_IOR(F2FS_IOCTL_MAGIC, 12, __u32)
+#define F2FS_IOC_SET_PIN_FILE		_IOW(F2FS_IOCTL_MAGIC, 13, __u32)
 
 #define F2FS_IOC_SET_ENCRYPTION_POLICY	FS_IOC_SET_ENCRYPTION_POLICY
 #define F2FS_IOC_GET_ENCRYPTION_POLICY	FS_IOC_GET_ENCRYPTION_POLICY
@@ -587,7 +588,10 @@ struct f2fs_inode_info {
 	unsigned long i_flags;		/* keep an inode flags for ioctl */
 	unsigned char i_advise;		/* use to give file attribute hints */
 	unsigned char i_dir_level;	/* use for dentry level for large dir */
-	unsigned int i_current_depth;	/* use only in directory structure */
+	union {
+		unsigned int i_current_depth;	/* only for directory depth */
+		unsigned short i_gc_failures;	/* only for regular file */
+	};
 	unsigned int i_pino;		/* parent inode number */
 	umode_t i_acl_mode;		/* keep file acl mode temporarily */
 
@@ -1133,6 +1137,9 @@ struct f2fs_sb_info {
 	/* threshold for converting bg victims for fg */
 	u64 fggc_threshold;
 
+	/* threshold for gc trials on pinned files */
+	u64 gc_pin_file_threshold;
+
 	/* maximum # of trials to find a victim segment for SSR and GC */
 	unsigned int max_victim_search;
 
@@ -2124,6 +2131,7 @@ enum {
 	FI_HOT_DATA,		/* indicate file is hot */
 	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
 	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
+	FI_PIN_FILE,		/* indicate file should not be gced */
 };
 
 static inline void __mark_inode_dirty_flag(struct inode *inode,
@@ -2137,6 +2145,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
 			return;
 	case FI_DATA_EXIST:
 	case FI_INLINE_DOTS:
+	case FI_PIN_FILE:
 		f2fs_mark_inode_dirty_sync(inode, true);
 	}
 }
@@ -2217,6 +2226,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
 	f2fs_mark_inode_dirty_sync(inode, true);
 }
 
+static inline void f2fs_i_gc_failures_write(struct inode *inode,
+					unsigned int count)
+{
+	F2FS_I(inode)->i_gc_failures = count;
+	f2fs_mark_inode_dirty_sync(inode, true);
+}
+
 static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
 {
 	F2FS_I(inode)->i_xattr_nid = xnid;
@@ -2245,6 +2261,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
 		set_bit(FI_INLINE_DOTS, &fi->flags);
 	if (ri->i_inline & F2FS_EXTRA_ATTR)
 		set_bit(FI_EXTRA_ATTR, &fi->flags);
+	if (ri->i_inline & F2FS_PIN_FILE)
+		set_bit(FI_PIN_FILE, &fi->flags);
 }
 
 static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
@@ -2263,6 +2281,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
 		ri->i_inline |= F2FS_INLINE_DOTS;
 	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
 		ri->i_inline |= F2FS_EXTRA_ATTR;
+	if (is_inode_flag_set(inode, FI_PIN_FILE))
+		ri->i_inline |= F2FS_PIN_FILE;
 }
 
 static inline int f2fs_has_extra_attr(struct inode *inode)
@@ -2308,6 +2328,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
 	return is_inode_flag_set(inode, FI_INLINE_DOTS);
 }
 
+static inline bool f2fs_is_pinned_file(struct inode *inode)
+{
+	return is_inode_flag_set(inode, FI_PIN_FILE);
+}
+
 static inline bool f2fs_is_atomic_file(struct inode *inode)
 {
 	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
@@ -2523,6 +2548,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
 void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
 long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+int f2fs_pin_file_control(struct inode *inode, bool inc);
 
 /*
  * inode.c
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 322aab9d91b5..a7ee659c5a61 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -2669,6 +2669,67 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
 	return 0;
 }
 
+int f2fs_pin_file_control(struct inode *inode, bool inc)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+	/* Use i_gc_failures for normal file as a risk signal. */
+	if (inc)
+		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
+
+	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
+		f2fs_msg(sbi->sb, KERN_WARNING,
+			"%s: Enable GC = ino %lx after %x GC trials\n",
+			__func__, inode->i_ino, fi->i_gc_failures);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
+{
+	struct inode *inode = file_inode(filp);
+	__u32 pin;
+	int ret = 0;
+
+	if (!inode_owner_or_capable(inode))
+		return -EACCES;
+
+	if (get_user(pin, (__u32 __user *)arg))
+		return -EFAULT;
+
+	if (!S_ISREG(inode->i_mode))
+		return -EINVAL;
+
+	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
+		return -EROFS;
+
+	inode_lock(inode);
+
+	if (!pin) {
+		clear_inode_flag(inode, FI_PIN_FILE);
+		F2FS_I(inode)->i_gc_failures = 0;
+		goto done;
+	}
+
+	if (f2fs_pin_file_control(inode, false)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+	ret = f2fs_convert_inline_inode(inode);
+	if (ret)
+		goto out;
+
+	set_inode_flag(inode, FI_PIN_FILE);
+	ret = F2FS_I(inode)->i_gc_failures;
+done:
+	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
+out:
+	inode_unlock(inode);
+	return ret;
+}
+
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
@@ -2719,6 +2780,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		return f2fs_ioc_fsgetxattr(filp, arg);
 	case F2FS_IOC_FSSETXATTR:
 		return f2fs_ioc_fssetxattr(filp, arg);
+	case F2FS_IOC_SET_PIN_FILE:
+		return f2fs_ioc_set_pin_file(filp, arg);
 	default:
 		return -ENOTTY;
 	}
@@ -2794,6 +2857,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case F2FS_IOC_GET_FEATURES:
 	case F2FS_IOC_FSGETXATTR:
 	case F2FS_IOC_FSSETXATTR:
+	case F2FS_IOC_SET_PIN_FILE:
 		break;
 	default:
 		return -ENOIOCTLCMD;
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 5d5bba462f26..9bffef153a12 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
 	if (f2fs_is_atomic_file(inode))
 		goto out;
 
+	if (f2fs_is_pinned_file(inode)) {
+		f2fs_pin_file_control(inode, true);
+		goto out;
+	}
+
 	set_new_dnode(&dn, inode, NULL, NULL, 0);
 	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
 	if (err)
@@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
 
 	if (f2fs_is_atomic_file(inode))
 		goto out;
+	if (f2fs_is_pinned_file(inode)) {
+		if (gc_type == FG_GC)
+			f2fs_pin_file_control(inode, true);
+		goto out;
+	}
 
 	if (gc_type == BG_GC) {
 		if (PageWriteback(page))
@@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
 
 	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
 				BLKS_PER_SEC(sbi), (main_count - resv_count));
+	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
 
 	/* give warm/cold data area from slower device */
 	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
index 9325191fab2d..33310165cb78 100644
--- a/fs/f2fs/gc.h
+++ b/fs/f2fs/gc.h
@@ -20,6 +20,8 @@
 #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
 #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
 
+#define DEF_GC_FAILED_PINNED_FILES	1024
+
 /* Search max. number of dirty segments to select a victim segment */
 #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
 
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index ab6028c332aa..41887e6ec1b3 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -301,6 +301,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
+F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
 F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
@@ -349,6 +350,7 @@ static struct attribute *f2fs_attrs[] = {
 	ATTR_LIST(idle_interval),
 	ATTR_LIST(iostat_enable),
 	ATTR_LIST(readdir_ra),
+	ATTR_LIST(gc_pin_file_thresh),
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 	ATTR_LIST(inject_rate),
 	ATTR_LIST(inject_type),
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 43e98d30d2df..cfb522e6affc 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -212,6 +212,7 @@ struct f2fs_extent {
 #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
 #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
 #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
+#define F2FS_PIN_FILE		0x40	/* file should not be gced */
 
 struct f2fs_inode {
 	__le16 i_mode;			/* file mode */
@@ -229,7 +230,13 @@ struct f2fs_inode {
 	__le32 i_ctime_nsec;		/* change time in nano scale */
 	__le32 i_mtime_nsec;		/* modification time in nano scale */
 	__le32 i_generation;		/* file version (for NFS) */
-	__le32 i_current_depth;		/* only for directory depth */
+	union {
+		__le32 i_current_depth;	/* only for directory depth */
+		__le16 i_gc_failures;	/*
+					 * # of gc failures on pinned file.
+					 * only for regular files.
+					 */
+	};
 	__le32 i_xattr_nid;		/* nid to save xattr */
 	__le32 i_flags;			/* file attributes */
 	__le32 i_pino;			/* parent inode number */
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] f2fs: add an ioctl to disable GC for specific file
  2018-01-02  3:29           ` Chao Yu
  (?)
@ 2018-01-03  3:22           ` Jaegeuk Kim
  -1 siblings, 0 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2018-01-03  3:22 UTC (permalink / raw)
  To: Chao Yu; +Cc: linux-kernel, linux-f2fs-devel

On 01/02, Chao Yu wrote:
> On 2018/1/1 9:07, Jaegeuk Kim wrote:
> > On 12/29, Chao Yu wrote:
> >> On 2017/12/28 11:40, Jaegeuk Kim wrote:
> >>> This patch gives a flag to disable GC on given file, which would be useful, when
> >>> user wants to keep its block map. It also conducts in-place-update for dontmove
> >>> file.
> >>
> >> One question, we may encounter out-of-space during foreground GC if there
> >> is too many data blocks of pinned files as they are removable.
> >>
> >> Do we need to record all segments which contain block of pinned file? and
> >> then trigger foreground GC more early by adjust threshold in
> >> has_not_enough_free_secs.
> > 
> > This should be used only when application knows how this works. If not, I wanted
> > to just unset the flag as much as possible, as foreground GCs have been done for
> 
> Well, how can we unset that flag? do we need an extra ioctl interface like
> F2FS_IOC_UNPIN_FILE? so that user can keep its data in unpinned file by
> calling that interface. Or user can only drop the flag by unlinking pinned
> file?

I've modified ioctl to get this done.

Thanks,

> 
> Thanks,
> 
> > pinned file too frequently. I expect the usecase must be quite unusual with just
> > small number of files in short period likewise atomic files.
> > 
> > Thanks,
> > 
> >>
> >> Thanks,
> >>
> >>>
> >>> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> >>> ---
> >>>
> >>> Change log from v2:
> >>>  - change the naming from dontmove to pin_file
> >>>  - do in-place-update for pinned files
> >>>  - use union for i_current_depth with i_gc_failures
> >>>  - pass increasing i_gc_failures
> >>>
> >>>  fs/f2fs/data.c          |  2 ++
> >>>  fs/f2fs/f2fs.h          | 29 +++++++++++++++++++++++++-
> >>>  fs/f2fs/file.c          | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
> >>>  fs/f2fs/gc.c            | 11 ++++++++++
> >>>  fs/f2fs/gc.h            |  2 ++
> >>>  fs/f2fs/sysfs.c         |  2 ++
> >>>  include/linux/f2fs_fs.h |  9 ++++++++-
> >>>  7 files changed, 107 insertions(+), 2 deletions(-)
> >>>
> >>> diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
> >>> index 7aca6ccd01f6..b9fab6186f28 100644
> >>> --- a/fs/f2fs/data.c
> >>> +++ b/fs/f2fs/data.c
> >>> @@ -1394,6 +1394,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
> >>>  {
> >>>  	struct inode *inode = fio->page->mapping->host;
> >>>  
> >>> +	if (f2fs_is_pinned_file(inode))
> >>> +		return true;
> >>>  	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
> >>>  		return false;
> >>>  	if (is_cold_data(fio->page))
> >>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> >>> index 65a51b941146..398ed95d4036 100644
> >>> --- a/fs/f2fs/f2fs.h
> >>> +++ b/fs/f2fs/f2fs.h
> >>> @@ -376,6 +376,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
> >>>  #define F2FS_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
> >>>  #define F2FS_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
> >>>  
> >>> +#define F2FS_IOC_PIN_FILE		_IO(F2FS_IOCTL_MAGIC, 13)
> >>> +
> >>>  struct f2fs_gc_range {
> >>>  	u32 sync;
> >>>  	u64 start;
> >>> @@ -586,7 +588,10 @@ struct f2fs_inode_info {
> >>>  	unsigned long i_flags;		/* keep an inode flags for ioctl */
> >>>  	unsigned char i_advise;		/* use to give file attribute hints */
> >>>  	unsigned char i_dir_level;	/* use for dentry level for large dir */
> >>> -	unsigned int i_current_depth;	/* use only in directory structure */
> >>> +	union {
> >>> +		unsigned int i_current_depth;	/* only for directory depth */
> >>> +		unsigned short i_gc_failures;	/* only for regular file */
> >>> +	};
> >>>  	unsigned int i_pino;		/* parent inode number */
> >>>  	umode_t i_acl_mode;		/* keep file acl mode temporarily */
> >>>  
> >>> @@ -1130,6 +1135,9 @@ struct f2fs_sb_info {
> >>>  	/* threshold for converting bg victims for fg */
> >>>  	u64 fggc_threshold;
> >>>  
> >>> +	/* threshold for gc trials on pinned files */
> >>> +	u64 gc_pin_file_threshold;
> >>> +
> >>>  	/* maximum # of trials to find a victim segment for SSR and GC */
> >>>  	unsigned int max_victim_search;
> >>>  
> >>> @@ -2119,6 +2127,7 @@ enum {
> >>>  	FI_HOT_DATA,		/* indicate file is hot */
> >>>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
> >>>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> >>> +	FI_PIN_FILE,		/* indicate file should not be gced */
> >>>  };
> >>>  
> >>>  static inline void __mark_inode_dirty_flag(struct inode *inode,
> >>> @@ -2132,6 +2141,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
> >>>  			return;
> >>>  	case FI_DATA_EXIST:
> >>>  	case FI_INLINE_DOTS:
> >>> +	case FI_PIN_FILE:
> >>>  		f2fs_mark_inode_dirty_sync(inode, true);
> >>>  	}
> >>>  }
> >>> @@ -2212,6 +2222,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
> >>>  	f2fs_mark_inode_dirty_sync(inode, true);
> >>>  }
> >>>  
> >>> +static inline void f2fs_i_gc_failures_write(struct inode *inode,
> >>> +					unsigned int count)
> >>> +{
> >>> +	F2FS_I(inode)->i_gc_failures = count;
> >>> +	f2fs_mark_inode_dirty_sync(inode, true);
> >>> +}
> >>> +
> >>>  static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
> >>>  {
> >>>  	F2FS_I(inode)->i_xattr_nid = xnid;
> >>> @@ -2240,6 +2257,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
> >>>  		set_bit(FI_INLINE_DOTS, &fi->flags);
> >>>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
> >>>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> >>> +	if (ri->i_inline & F2FS_PIN_FILE)
> >>> +		set_bit(FI_PIN_FILE, &fi->flags);
> >>>  }
> >>>  
> >>>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> >>> @@ -2258,6 +2277,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> >>>  		ri->i_inline |= F2FS_INLINE_DOTS;
> >>>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
> >>>  		ri->i_inline |= F2FS_EXTRA_ATTR;
> >>> +	if (is_inode_flag_set(inode, FI_PIN_FILE))
> >>> +		ri->i_inline |= F2FS_PIN_FILE;
> >>>  }
> >>>  
> >>>  static inline int f2fs_has_extra_attr(struct inode *inode)
> >>> @@ -2303,6 +2324,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
> >>>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
> >>>  }
> >>>  
> >>> +static inline bool f2fs_is_pinned_file(struct inode *inode)
> >>> +{
> >>> +	return is_inode_flag_set(inode, FI_PIN_FILE);
> >>> +}
> >>> +
> >>>  static inline bool f2fs_is_atomic_file(struct inode *inode)
> >>>  {
> >>>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> >>> @@ -2518,6 +2544,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
> >>>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
> >>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
> >>>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> >>> +int f2fs_pin_file_control(struct inode *inode, bool inc);
> >>>  
> >>>  /*
> >>>   * inode.c
> >>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> >>> index 1db68f1bcd77..5a3b9a07d72d 100644
> >>> --- a/fs/f2fs/file.c
> >>> +++ b/fs/f2fs/file.c
> >>> @@ -2666,6 +2666,57 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
> >>>  	return 0;
> >>>  }
> >>>  
> >>> +int f2fs_pin_file_control(struct inode *inode, bool inc)
> >>> +{
> >>> +	struct f2fs_inode_info *fi = F2FS_I(inode);
> >>> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> >>> +
> >>> +	/* Use i_gc_failures for normal file as a risk signal. */
> >>> +	if (inc)
> >>> +		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
> >>> +
> >>> +	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
> >>> +		f2fs_msg(sbi->sb, KERN_WARNING,
> >>> +			"%s: Enable GC = ino %lx after %x GC trials\n",
> >>> +			__func__, inode->i_ino, fi->i_gc_failures);
> >>> +		return -EAGAIN;
> >>> +	}
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static int f2fs_ioc_pin_file(struct file *filp)
> >>> +{
> >>> +	struct inode *inode = file_inode(filp);
> >>> +	int ret;
> >>> +
> >>> +	if (!inode_owner_or_capable(inode))
> >>> +		return -EACCES;
> >>> +
> >>> +	if (!S_ISREG(inode->i_mode))
> >>> +		return -EINVAL;
> >>> +
> >>> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> >>> +		return -EROFS;
> >>> +
> >>> +	inode_lock(inode);
> >>> +
> >>> +	if (f2fs_pin_file_control(inode, false)) {
> >>> +		ret = -EAGAIN;
> >>> +		goto out;
> >>> +	}
> >>> +
> >>> +	ret = f2fs_convert_inline_inode(inode);
> >>> +	if (ret)
> >>> +		goto out;
> >>> +
> >>> +	set_inode_flag(inode, FI_PIN_FILE);
> >>> +	ret = F2FS_I(inode)->i_gc_failures;
> >>> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> >>> +out:
> >>> +	inode_unlock(inode);
> >>> +	return ret;
> >>> +}
> >>> +
> >>>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >>>  {
> >>>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> >>> @@ -2716,6 +2767,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >>>  		return f2fs_ioc_fsgetxattr(filp, arg);
> >>>  	case F2FS_IOC_FSSETXATTR:
> >>>  		return f2fs_ioc_fssetxattr(filp, arg);
> >>> +	case F2FS_IOC_PIN_FILE:
> >>> +		return f2fs_ioc_pin_file(filp);
> >>>  	default:
> >>>  		return -ENOTTY;
> >>>  	}
> >>> @@ -2791,6 +2844,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >>>  	case F2FS_IOC_GET_FEATURES:
> >>>  	case F2FS_IOC_FSGETXATTR:
> >>>  	case F2FS_IOC_FSSETXATTR:
> >>> +	case F2FS_IOC_PIN_FILE:
> >>>  		break;
> >>>  	default:
> >>>  		return -ENOIOCTLCMD;
> >>> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> >>> index 5d5bba462f26..9bffef153a12 100644
> >>> --- a/fs/f2fs/gc.c
> >>> +++ b/fs/f2fs/gc.c
> >>> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
> >>>  	if (f2fs_is_atomic_file(inode))
> >>>  		goto out;
> >>>  
> >>> +	if (f2fs_is_pinned_file(inode)) {
> >>> +		f2fs_pin_file_control(inode, true);
> >>> +		goto out;
> >>> +	}
> >>> +
> >>>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
> >>>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
> >>>  	if (err)
> >>> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
> >>>  
> >>>  	if (f2fs_is_atomic_file(inode))
> >>>  		goto out;
> >>> +	if (f2fs_is_pinned_file(inode)) {
> >>> +		if (gc_type == FG_GC)
> >>> +			f2fs_pin_file_control(inode, true);
> >>> +		goto out;
> >>> +	}
> >>>  
> >>>  	if (gc_type == BG_GC) {
> >>>  		if (PageWriteback(page))
> >>> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
> >>>  
> >>>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
> >>>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> >>> +	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
> >>>  
> >>>  	/* give warm/cold data area from slower device */
> >>>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> >>> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> >>> index 9325191fab2d..33310165cb78 100644
> >>> --- a/fs/f2fs/gc.h
> >>> +++ b/fs/f2fs/gc.h
> >>> @@ -20,6 +20,8 @@
> >>>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
> >>>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
> >>>  
> >>> +#define DEF_GC_FAILED_PINNED_FILES	1024
> >>> +
> >>>  /* Search max. number of dirty segments to select a victim segment */
> >>>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
> >>>  
> >>> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> >>> index 581c7ce23c22..0e3253d7cb10 100644
> >>> --- a/fs/f2fs/sysfs.c
> >>> +++ b/fs/f2fs/sysfs.c
> >>> @@ -303,6 +303,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
> >>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
> >>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
> >>>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> >>> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
> >>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >>>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
> >>>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> >>> @@ -351,6 +352,7 @@ static struct attribute *f2fs_attrs[] = {
> >>>  	ATTR_LIST(idle_interval),
> >>>  	ATTR_LIST(iostat_enable),
> >>>  	ATTR_LIST(readdir_ra),
> >>> +	ATTR_LIST(gc_pin_file_thresh),
> >>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >>>  	ATTR_LIST(inject_rate),
> >>>  	ATTR_LIST(inject_type),
> >>> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> >>> index 43e98d30d2df..cfb522e6affc 100644
> >>> --- a/include/linux/f2fs_fs.h
> >>> +++ b/include/linux/f2fs_fs.h
> >>> @@ -212,6 +212,7 @@ struct f2fs_extent {
> >>>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
> >>>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
> >>>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> >>> +#define F2FS_PIN_FILE		0x40	/* file should not be gced */
> >>>  
> >>>  struct f2fs_inode {
> >>>  	__le16 i_mode;			/* file mode */
> >>> @@ -229,7 +230,13 @@ struct f2fs_inode {
> >>>  	__le32 i_ctime_nsec;		/* change time in nano scale */
> >>>  	__le32 i_mtime_nsec;		/* modification time in nano scale */
> >>>  	__le32 i_generation;		/* file version (for NFS) */
> >>> -	__le32 i_current_depth;		/* only for directory depth */
> >>> +	union {
> >>> +		__le32 i_current_depth;	/* only for directory depth */
> >>> +		__le16 i_gc_failures;	/*
> >>> +					 * # of gc failures on pinned file.
> >>> +					 * only for regular files.
> >>> +					 */
> >>> +	};
> >>>  	__le32 i_xattr_nid;		/* nid to save xattr */
> >>>  	__le32 i_flags;			/* file attributes */
> >>>  	__le32 i_pino;			/* parent inode number */
> >>>
> > 
> > .
> > 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v3] f2fs: add an ioctl to disable GC for specific file
  2018-01-03  3:21     ` [PATCH v3] " Jaegeuk Kim
@ 2018-01-03  7:34         ` Chao Yu
  2018-01-16 23:57         ` Jaegeuk Kim
  1 sibling, 0 replies; 27+ messages in thread
From: Chao Yu @ 2018-01-03  7:34 UTC (permalink / raw)
  To: Jaegeuk Kim, linux-kernel, linux-f2fs-devel

On 2018/1/3 11:21, Jaegeuk Kim wrote:
> This patch gives a flag to disable GC on given file, which would be useful, when
> user wants to keep its block map. It also conducts in-place-update for dontmove
> file.
> 
> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> ---
> 
> Change log from v2:
>  - modify ioctl to allow users unpin the file
> 
>  fs/f2fs/data.c          |  2 ++
>  fs/f2fs/f2fs.h          | 28 +++++++++++++++++++++-
>  fs/f2fs/file.c          | 64 +++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/f2fs/gc.c            | 11 +++++++++
>  fs/f2fs/gc.h            |  2 ++
>  fs/f2fs/sysfs.c         |  2 ++
>  include/linux/f2fs_fs.h |  9 ++++++-
>  7 files changed, 116 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
> index 449b0aaa3905..45f65a5b9871 100644
> --- a/fs/f2fs/data.c
> +++ b/fs/f2fs/data.c
> @@ -1395,6 +1395,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
>  {
>  	struct inode *inode = fio->page->mapping->host;
>  
> +	if (f2fs_is_pinned_file(inode))
> +		return true;
>  	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
>  		return false;
>  	if (is_cold_data(fio->page))
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index a0e8eec23125..f4b7d73695a7 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -350,6 +350,7 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
>  #define F2FS_IOC_GARBAGE_COLLECT_RANGE	_IOW(F2FS_IOCTL_MAGIC, 11,	\
>  						struct f2fs_gc_range)
>  #define F2FS_IOC_GET_FEATURES		_IOR(F2FS_IOCTL_MAGIC, 12, __u32)
> +#define F2FS_IOC_SET_PIN_FILE		_IOW(F2FS_IOCTL_MAGIC, 13, __u32)
>  
>  #define F2FS_IOC_SET_ENCRYPTION_POLICY	FS_IOC_SET_ENCRYPTION_POLICY
>  #define F2FS_IOC_GET_ENCRYPTION_POLICY	FS_IOC_GET_ENCRYPTION_POLICY
> @@ -587,7 +588,10 @@ struct f2fs_inode_info {
>  	unsigned long i_flags;		/* keep an inode flags for ioctl */
>  	unsigned char i_advise;		/* use to give file attribute hints */
>  	unsigned char i_dir_level;	/* use for dentry level for large dir */
> -	unsigned int i_current_depth;	/* use only in directory structure */
> +	union {
> +		unsigned int i_current_depth;	/* only for directory depth */
> +		unsigned short i_gc_failures;	/* only for regular file */
> +	};
>  	unsigned int i_pino;		/* parent inode number */
>  	umode_t i_acl_mode;		/* keep file acl mode temporarily */
>  
> @@ -1133,6 +1137,9 @@ struct f2fs_sb_info {
>  	/* threshold for converting bg victims for fg */
>  	u64 fggc_threshold;
>  
> +	/* threshold for gc trials on pinned files */
> +	u64 gc_pin_file_threshold;
> +
>  	/* maximum # of trials to find a victim segment for SSR and GC */
>  	unsigned int max_victim_search;
>  
> @@ -2124,6 +2131,7 @@ enum {
>  	FI_HOT_DATA,		/* indicate file is hot */
>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> +	FI_PIN_FILE,		/* indicate file should not be gced */
>  };
>  
>  static inline void __mark_inode_dirty_flag(struct inode *inode,
> @@ -2137,6 +2145,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
>  			return;
>  	case FI_DATA_EXIST:
>  	case FI_INLINE_DOTS:
> +	case FI_PIN_FILE:
>  		f2fs_mark_inode_dirty_sync(inode, true);
>  	}
>  }
> @@ -2217,6 +2226,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
>  	f2fs_mark_inode_dirty_sync(inode, true);
>  }
>  
> +static inline void f2fs_i_gc_failures_write(struct inode *inode,
> +					unsigned int count)
> +{
> +	F2FS_I(inode)->i_gc_failures = count;
> +	f2fs_mark_inode_dirty_sync(inode, true);
> +}
> +
>  static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
>  {
>  	F2FS_I(inode)->i_xattr_nid = xnid;
> @@ -2245,6 +2261,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
>  		set_bit(FI_INLINE_DOTS, &fi->flags);
>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> +	if (ri->i_inline & F2FS_PIN_FILE)
> +		set_bit(FI_PIN_FILE, &fi->flags);
>  }
>  
>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> @@ -2263,6 +2281,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>  		ri->i_inline |= F2FS_INLINE_DOTS;
>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
>  		ri->i_inline |= F2FS_EXTRA_ATTR;
> +	if (is_inode_flag_set(inode, FI_PIN_FILE))
> +		ri->i_inline |= F2FS_PIN_FILE;
>  }
>  
>  static inline int f2fs_has_extra_attr(struct inode *inode)
> @@ -2308,6 +2328,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
>  }
>  
> +static inline bool f2fs_is_pinned_file(struct inode *inode)
> +{
> +	return is_inode_flag_set(inode, FI_PIN_FILE);
> +}
> +
>  static inline bool f2fs_is_atomic_file(struct inode *inode)
>  {
>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> @@ -2523,6 +2548,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> +int f2fs_pin_file_control(struct inode *inode, bool inc);
>  
>  /*
>   * inode.c
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 322aab9d91b5..a7ee659c5a61 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -2669,6 +2669,67 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
>  	return 0;
>  }
>  
> +int f2fs_pin_file_control(struct inode *inode, bool inc)
> +{
> +	struct f2fs_inode_info *fi = F2FS_I(inode);
> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> +
> +	/* Use i_gc_failures for normal file as a risk signal. */
> +	if (inc)
> +		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
> +
> +	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
> +		f2fs_msg(sbi->sb, KERN_WARNING,
> +			"%s: Enable GC = ino %lx after %x GC trials\n",
> +			__func__, inode->i_ino, fi->i_gc_failures);
> +		return -EAGAIN;
> +	}
> +	return 0;
> +}
> +
> +static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
> +{
> +	struct inode *inode = file_inode(filp);
> +	__u32 pin;
> +	int ret = 0;
> +
> +	if (!inode_owner_or_capable(inode))
> +		return -EACCES;
> +
> +	if (get_user(pin, (__u32 __user *)arg))
> +		return -EFAULT;
> +
> +	if (!S_ISREG(inode->i_mode))
> +		return -EINVAL;
> +
> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> +		return -EROFS;

mnt_want_write_file(filp)?

> +
> +	inode_lock(inode);
> +
> +	if (!pin) {
> +		clear_inode_flag(inode, FI_PIN_FILE);
> +		F2FS_I(inode)->i_gc_failures = 0;
> +		goto done;
> +	}
> +
> +	if (f2fs_pin_file_control(inode, false)) {
> +		ret = -EAGAIN;
> +		goto out;
> +	}
> +	ret = f2fs_convert_inline_inode(inode);
> +	if (ret)
> +		goto out;
> +
> +	set_inode_flag(inode, FI_PIN_FILE);
> +	ret = F2FS_I(inode)->i_gc_failures;
> +done:
> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> +out:
> +	inode_unlock(inode);

mnt_drop_write_file(filp)?

Other part looks good to me. ;)

Reviewed-by: Chao Yu <yuchao0@huawei.com>

Thanks,

> +	return ret;
> +}
> +
>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  {
>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> @@ -2719,6 +2780,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  		return f2fs_ioc_fsgetxattr(filp, arg);
>  	case F2FS_IOC_FSSETXATTR:
>  		return f2fs_ioc_fssetxattr(filp, arg);
> +	case F2FS_IOC_SET_PIN_FILE:
> +		return f2fs_ioc_set_pin_file(filp, arg);
>  	default:
>  		return -ENOTTY;
>  	}
> @@ -2794,6 +2857,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  	case F2FS_IOC_GET_FEATURES:
>  	case F2FS_IOC_FSGETXATTR:
>  	case F2FS_IOC_FSSETXATTR:
> +	case F2FS_IOC_SET_PIN_FILE:
>  		break;
>  	default:
>  		return -ENOIOCTLCMD;
> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> index 5d5bba462f26..9bffef153a12 100644
> --- a/fs/f2fs/gc.c
> +++ b/fs/f2fs/gc.c
> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
>  	if (f2fs_is_atomic_file(inode))
>  		goto out;
>  
> +	if (f2fs_is_pinned_file(inode)) {
> +		f2fs_pin_file_control(inode, true);
> +		goto out;
> +	}
> +
>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
>  	if (err)
> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
>  
>  	if (f2fs_is_atomic_file(inode))
>  		goto out;
> +	if (f2fs_is_pinned_file(inode)) {
> +		if (gc_type == FG_GC)
> +			f2fs_pin_file_control(inode, true);
> +		goto out;
> +	}
>  
>  	if (gc_type == BG_GC) {
>  		if (PageWriteback(page))
> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
>  
>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> +	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
>  
>  	/* give warm/cold data area from slower device */
>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> index 9325191fab2d..33310165cb78 100644
> --- a/fs/f2fs/gc.h
> +++ b/fs/f2fs/gc.h
> @@ -20,6 +20,8 @@
>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
>  
> +#define DEF_GC_FAILED_PINNED_FILES	1024
> +
>  /* Search max. number of dirty segments to select a victim segment */
>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
>  
> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> index ab6028c332aa..41887e6ec1b3 100644
> --- a/fs/f2fs/sysfs.c
> +++ b/fs/f2fs/sysfs.c
> @@ -301,6 +301,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> @@ -349,6 +350,7 @@ static struct attribute *f2fs_attrs[] = {
>  	ATTR_LIST(idle_interval),
>  	ATTR_LIST(iostat_enable),
>  	ATTR_LIST(readdir_ra),
> +	ATTR_LIST(gc_pin_file_thresh),
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  	ATTR_LIST(inject_rate),
>  	ATTR_LIST(inject_type),
> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> index 43e98d30d2df..cfb522e6affc 100644
> --- a/include/linux/f2fs_fs.h
> +++ b/include/linux/f2fs_fs.h
> @@ -212,6 +212,7 @@ struct f2fs_extent {
>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> +#define F2FS_PIN_FILE		0x40	/* file should not be gced */
>  
>  struct f2fs_inode {
>  	__le16 i_mode;			/* file mode */
> @@ -229,7 +230,13 @@ struct f2fs_inode {
>  	__le32 i_ctime_nsec;		/* change time in nano scale */
>  	__le32 i_mtime_nsec;		/* modification time in nano scale */
>  	__le32 i_generation;		/* file version (for NFS) */
> -	__le32 i_current_depth;		/* only for directory depth */
> +	union {
> +		__le32 i_current_depth;	/* only for directory depth */
> +		__le16 i_gc_failures;	/*
> +					 * # of gc failures on pinned file.
> +					 * only for regular files.
> +					 */
> +	};
>  	__le32 i_xattr_nid;		/* nid to save xattr */
>  	__le32 i_flags;			/* file attributes */
>  	__le32 i_pino;			/* parent inode number */
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v3] f2fs: add an ioctl to disable GC for specific file
@ 2018-01-03  7:34         ` Chao Yu
  0 siblings, 0 replies; 27+ messages in thread
From: Chao Yu @ 2018-01-03  7:34 UTC (permalink / raw)
  To: Jaegeuk Kim, linux-kernel, linux-f2fs-devel

On 2018/1/3 11:21, Jaegeuk Kim wrote:
> This patch gives a flag to disable GC on given file, which would be useful, when
> user wants to keep its block map. It also conducts in-place-update for dontmove
> file.
> 
> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> ---
> 
> Change log from v2:
>  - modify ioctl to allow users unpin the file
> 
>  fs/f2fs/data.c          |  2 ++
>  fs/f2fs/f2fs.h          | 28 +++++++++++++++++++++-
>  fs/f2fs/file.c          | 64 +++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/f2fs/gc.c            | 11 +++++++++
>  fs/f2fs/gc.h            |  2 ++
>  fs/f2fs/sysfs.c         |  2 ++
>  include/linux/f2fs_fs.h |  9 ++++++-
>  7 files changed, 116 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
> index 449b0aaa3905..45f65a5b9871 100644
> --- a/fs/f2fs/data.c
> +++ b/fs/f2fs/data.c
> @@ -1395,6 +1395,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
>  {
>  	struct inode *inode = fio->page->mapping->host;
>  
> +	if (f2fs_is_pinned_file(inode))
> +		return true;
>  	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
>  		return false;
>  	if (is_cold_data(fio->page))
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index a0e8eec23125..f4b7d73695a7 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -350,6 +350,7 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
>  #define F2FS_IOC_GARBAGE_COLLECT_RANGE	_IOW(F2FS_IOCTL_MAGIC, 11,	\
>  						struct f2fs_gc_range)
>  #define F2FS_IOC_GET_FEATURES		_IOR(F2FS_IOCTL_MAGIC, 12, __u32)
> +#define F2FS_IOC_SET_PIN_FILE		_IOW(F2FS_IOCTL_MAGIC, 13, __u32)
>  
>  #define F2FS_IOC_SET_ENCRYPTION_POLICY	FS_IOC_SET_ENCRYPTION_POLICY
>  #define F2FS_IOC_GET_ENCRYPTION_POLICY	FS_IOC_GET_ENCRYPTION_POLICY
> @@ -587,7 +588,10 @@ struct f2fs_inode_info {
>  	unsigned long i_flags;		/* keep an inode flags for ioctl */
>  	unsigned char i_advise;		/* use to give file attribute hints */
>  	unsigned char i_dir_level;	/* use for dentry level for large dir */
> -	unsigned int i_current_depth;	/* use only in directory structure */
> +	union {
> +		unsigned int i_current_depth;	/* only for directory depth */
> +		unsigned short i_gc_failures;	/* only for regular file */
> +	};
>  	unsigned int i_pino;		/* parent inode number */
>  	umode_t i_acl_mode;		/* keep file acl mode temporarily */
>  
> @@ -1133,6 +1137,9 @@ struct f2fs_sb_info {
>  	/* threshold for converting bg victims for fg */
>  	u64 fggc_threshold;
>  
> +	/* threshold for gc trials on pinned files */
> +	u64 gc_pin_file_threshold;
> +
>  	/* maximum # of trials to find a victim segment for SSR and GC */
>  	unsigned int max_victim_search;
>  
> @@ -2124,6 +2131,7 @@ enum {
>  	FI_HOT_DATA,		/* indicate file is hot */
>  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
>  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> +	FI_PIN_FILE,		/* indicate file should not be gced */
>  };
>  
>  static inline void __mark_inode_dirty_flag(struct inode *inode,
> @@ -2137,6 +2145,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
>  			return;
>  	case FI_DATA_EXIST:
>  	case FI_INLINE_DOTS:
> +	case FI_PIN_FILE:
>  		f2fs_mark_inode_dirty_sync(inode, true);
>  	}
>  }
> @@ -2217,6 +2226,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
>  	f2fs_mark_inode_dirty_sync(inode, true);
>  }
>  
> +static inline void f2fs_i_gc_failures_write(struct inode *inode,
> +					unsigned int count)
> +{
> +	F2FS_I(inode)->i_gc_failures = count;
> +	f2fs_mark_inode_dirty_sync(inode, true);
> +}
> +
>  static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
>  {
>  	F2FS_I(inode)->i_xattr_nid = xnid;
> @@ -2245,6 +2261,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
>  		set_bit(FI_INLINE_DOTS, &fi->flags);
>  	if (ri->i_inline & F2FS_EXTRA_ATTR)
>  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> +	if (ri->i_inline & F2FS_PIN_FILE)
> +		set_bit(FI_PIN_FILE, &fi->flags);
>  }
>  
>  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> @@ -2263,6 +2281,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
>  		ri->i_inline |= F2FS_INLINE_DOTS;
>  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
>  		ri->i_inline |= F2FS_EXTRA_ATTR;
> +	if (is_inode_flag_set(inode, FI_PIN_FILE))
> +		ri->i_inline |= F2FS_PIN_FILE;
>  }
>  
>  static inline int f2fs_has_extra_attr(struct inode *inode)
> @@ -2308,6 +2328,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
>  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
>  }
>  
> +static inline bool f2fs_is_pinned_file(struct inode *inode)
> +{
> +	return is_inode_flag_set(inode, FI_PIN_FILE);
> +}
> +
>  static inline bool f2fs_is_atomic_file(struct inode *inode)
>  {
>  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> @@ -2523,6 +2548,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
>  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
>  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> +int f2fs_pin_file_control(struct inode *inode, bool inc);
>  
>  /*
>   * inode.c
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 322aab9d91b5..a7ee659c5a61 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -2669,6 +2669,67 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
>  	return 0;
>  }
>  
> +int f2fs_pin_file_control(struct inode *inode, bool inc)
> +{
> +	struct f2fs_inode_info *fi = F2FS_I(inode);
> +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> +
> +	/* Use i_gc_failures for normal file as a risk signal. */
> +	if (inc)
> +		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
> +
> +	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
> +		f2fs_msg(sbi->sb, KERN_WARNING,
> +			"%s: Enable GC = ino %lx after %x GC trials\n",
> +			__func__, inode->i_ino, fi->i_gc_failures);
> +		return -EAGAIN;
> +	}
> +	return 0;
> +}
> +
> +static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
> +{
> +	struct inode *inode = file_inode(filp);
> +	__u32 pin;
> +	int ret = 0;
> +
> +	if (!inode_owner_or_capable(inode))
> +		return -EACCES;
> +
> +	if (get_user(pin, (__u32 __user *)arg))
> +		return -EFAULT;
> +
> +	if (!S_ISREG(inode->i_mode))
> +		return -EINVAL;
> +
> +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> +		return -EROFS;

mnt_want_write_file(filp)?

> +
> +	inode_lock(inode);
> +
> +	if (!pin) {
> +		clear_inode_flag(inode, FI_PIN_FILE);
> +		F2FS_I(inode)->i_gc_failures = 0;
> +		goto done;
> +	}
> +
> +	if (f2fs_pin_file_control(inode, false)) {
> +		ret = -EAGAIN;
> +		goto out;
> +	}
> +	ret = f2fs_convert_inline_inode(inode);
> +	if (ret)
> +		goto out;
> +
> +	set_inode_flag(inode, FI_PIN_FILE);
> +	ret = F2FS_I(inode)->i_gc_failures;
> +done:
> +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> +out:
> +	inode_unlock(inode);

mnt_drop_write_file(filp)?

Other part looks good to me. ;)

Reviewed-by: Chao Yu <yuchao0@huawei.com>

Thanks,

> +	return ret;
> +}
> +
>  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  {
>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> @@ -2719,6 +2780,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  		return f2fs_ioc_fsgetxattr(filp, arg);
>  	case F2FS_IOC_FSSETXATTR:
>  		return f2fs_ioc_fssetxattr(filp, arg);
> +	case F2FS_IOC_SET_PIN_FILE:
> +		return f2fs_ioc_set_pin_file(filp, arg);
>  	default:
>  		return -ENOTTY;
>  	}
> @@ -2794,6 +2857,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  	case F2FS_IOC_GET_FEATURES:
>  	case F2FS_IOC_FSGETXATTR:
>  	case F2FS_IOC_FSSETXATTR:
> +	case F2FS_IOC_SET_PIN_FILE:
>  		break;
>  	default:
>  		return -ENOIOCTLCMD;
> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> index 5d5bba462f26..9bffef153a12 100644
> --- a/fs/f2fs/gc.c
> +++ b/fs/f2fs/gc.c
> @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
>  	if (f2fs_is_atomic_file(inode))
>  		goto out;
>  
> +	if (f2fs_is_pinned_file(inode)) {
> +		f2fs_pin_file_control(inode, true);
> +		goto out;
> +	}
> +
>  	set_new_dnode(&dn, inode, NULL, NULL, 0);
>  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
>  	if (err)
> @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
>  
>  	if (f2fs_is_atomic_file(inode))
>  		goto out;
> +	if (f2fs_is_pinned_file(inode)) {
> +		if (gc_type == FG_GC)
> +			f2fs_pin_file_control(inode, true);
> +		goto out;
> +	}
>  
>  	if (gc_type == BG_GC) {
>  		if (PageWriteback(page))
> @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
>  
>  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
>  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> +	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
>  
>  	/* give warm/cold data area from slower device */
>  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> index 9325191fab2d..33310165cb78 100644
> --- a/fs/f2fs/gc.h
> +++ b/fs/f2fs/gc.h
> @@ -20,6 +20,8 @@
>  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
>  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
>  
> +#define DEF_GC_FAILED_PINNED_FILES	1024
> +
>  /* Search max. number of dirty segments to select a victim segment */
>  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
>  
> diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> index ab6028c332aa..41887e6ec1b3 100644
> --- a/fs/f2fs/sysfs.c
> +++ b/fs/f2fs/sysfs.c
> @@ -301,6 +301,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
>  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
>  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> @@ -349,6 +350,7 @@ static struct attribute *f2fs_attrs[] = {
>  	ATTR_LIST(idle_interval),
>  	ATTR_LIST(iostat_enable),
>  	ATTR_LIST(readdir_ra),
> +	ATTR_LIST(gc_pin_file_thresh),
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  	ATTR_LIST(inject_rate),
>  	ATTR_LIST(inject_type),
> diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> index 43e98d30d2df..cfb522e6affc 100644
> --- a/include/linux/f2fs_fs.h
> +++ b/include/linux/f2fs_fs.h
> @@ -212,6 +212,7 @@ struct f2fs_extent {
>  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
>  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
>  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> +#define F2FS_PIN_FILE		0x40	/* file should not be gced */
>  
>  struct f2fs_inode {
>  	__le16 i_mode;			/* file mode */
> @@ -229,7 +230,13 @@ struct f2fs_inode {
>  	__le32 i_ctime_nsec;		/* change time in nano scale */
>  	__le32 i_mtime_nsec;		/* modification time in nano scale */
>  	__le32 i_generation;		/* file version (for NFS) */
> -	__le32 i_current_depth;		/* only for directory depth */
> +	union {
> +		__le32 i_current_depth;	/* only for directory depth */
> +		__le16 i_gc_failures;	/*
> +					 * # of gc failures on pinned file.
> +					 * only for regular files.
> +					 */
> +	};
>  	__le32 i_xattr_nid;		/* nid to save xattr */
>  	__le32 i_flags;			/* file attributes */
>  	__le32 i_pino;			/* parent inode number */
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v3] f2fs: add an ioctl to disable GC for specific file
  2018-01-03  7:34         ` Chao Yu
  (?)
@ 2018-01-03 18:59         ` Jaegeuk Kim
  -1 siblings, 0 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2018-01-03 18:59 UTC (permalink / raw)
  To: Chao Yu; +Cc: linux-kernel, linux-f2fs-devel

On 01/03, Chao Yu wrote:
> On 2018/1/3 11:21, Jaegeuk Kim wrote:
> > This patch gives a flag to disable GC on given file, which would be useful, when
> > user wants to keep its block map. It also conducts in-place-update for dontmove
> > file.
> > 
> > Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> > ---
> > 
> > Change log from v2:
> >  - modify ioctl to allow users unpin the file
> > 
> >  fs/f2fs/data.c          |  2 ++
> >  fs/f2fs/f2fs.h          | 28 +++++++++++++++++++++-
> >  fs/f2fs/file.c          | 64 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  fs/f2fs/gc.c            | 11 +++++++++
> >  fs/f2fs/gc.h            |  2 ++
> >  fs/f2fs/sysfs.c         |  2 ++
> >  include/linux/f2fs_fs.h |  9 ++++++-
> >  7 files changed, 116 insertions(+), 2 deletions(-)
> > 
> > diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
> > index 449b0aaa3905..45f65a5b9871 100644
> > --- a/fs/f2fs/data.c
> > +++ b/fs/f2fs/data.c
> > @@ -1395,6 +1395,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
> >  {
> >  	struct inode *inode = fio->page->mapping->host;
> >  
> > +	if (f2fs_is_pinned_file(inode))
> > +		return true;
> >  	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
> >  		return false;
> >  	if (is_cold_data(fio->page))
> > diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> > index a0e8eec23125..f4b7d73695a7 100644
> > --- a/fs/f2fs/f2fs.h
> > +++ b/fs/f2fs/f2fs.h
> > @@ -350,6 +350,7 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
> >  #define F2FS_IOC_GARBAGE_COLLECT_RANGE	_IOW(F2FS_IOCTL_MAGIC, 11,	\
> >  						struct f2fs_gc_range)
> >  #define F2FS_IOC_GET_FEATURES		_IOR(F2FS_IOCTL_MAGIC, 12, __u32)
> > +#define F2FS_IOC_SET_PIN_FILE		_IOW(F2FS_IOCTL_MAGIC, 13, __u32)
> >  
> >  #define F2FS_IOC_SET_ENCRYPTION_POLICY	FS_IOC_SET_ENCRYPTION_POLICY
> >  #define F2FS_IOC_GET_ENCRYPTION_POLICY	FS_IOC_GET_ENCRYPTION_POLICY
> > @@ -587,7 +588,10 @@ struct f2fs_inode_info {
> >  	unsigned long i_flags;		/* keep an inode flags for ioctl */
> >  	unsigned char i_advise;		/* use to give file attribute hints */
> >  	unsigned char i_dir_level;	/* use for dentry level for large dir */
> > -	unsigned int i_current_depth;	/* use only in directory structure */
> > +	union {
> > +		unsigned int i_current_depth;	/* only for directory depth */
> > +		unsigned short i_gc_failures;	/* only for regular file */
> > +	};
> >  	unsigned int i_pino;		/* parent inode number */
> >  	umode_t i_acl_mode;		/* keep file acl mode temporarily */
> >  
> > @@ -1133,6 +1137,9 @@ struct f2fs_sb_info {
> >  	/* threshold for converting bg victims for fg */
> >  	u64 fggc_threshold;
> >  
> > +	/* threshold for gc trials on pinned files */
> > +	u64 gc_pin_file_threshold;
> > +
> >  	/* maximum # of trials to find a victim segment for SSR and GC */
> >  	unsigned int max_victim_search;
> >  
> > @@ -2124,6 +2131,7 @@ enum {
> >  	FI_HOT_DATA,		/* indicate file is hot */
> >  	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
> >  	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
> > +	FI_PIN_FILE,		/* indicate file should not be gced */
> >  };
> >  
> >  static inline void __mark_inode_dirty_flag(struct inode *inode,
> > @@ -2137,6 +2145,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
> >  			return;
> >  	case FI_DATA_EXIST:
> >  	case FI_INLINE_DOTS:
> > +	case FI_PIN_FILE:
> >  		f2fs_mark_inode_dirty_sync(inode, true);
> >  	}
> >  }
> > @@ -2217,6 +2226,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
> >  	f2fs_mark_inode_dirty_sync(inode, true);
> >  }
> >  
> > +static inline void f2fs_i_gc_failures_write(struct inode *inode,
> > +					unsigned int count)
> > +{
> > +	F2FS_I(inode)->i_gc_failures = count;
> > +	f2fs_mark_inode_dirty_sync(inode, true);
> > +}
> > +
> >  static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
> >  {
> >  	F2FS_I(inode)->i_xattr_nid = xnid;
> > @@ -2245,6 +2261,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
> >  		set_bit(FI_INLINE_DOTS, &fi->flags);
> >  	if (ri->i_inline & F2FS_EXTRA_ATTR)
> >  		set_bit(FI_EXTRA_ATTR, &fi->flags);
> > +	if (ri->i_inline & F2FS_PIN_FILE)
> > +		set_bit(FI_PIN_FILE, &fi->flags);
> >  }
> >  
> >  static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> > @@ -2263,6 +2281,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
> >  		ri->i_inline |= F2FS_INLINE_DOTS;
> >  	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
> >  		ri->i_inline |= F2FS_EXTRA_ATTR;
> > +	if (is_inode_flag_set(inode, FI_PIN_FILE))
> > +		ri->i_inline |= F2FS_PIN_FILE;
> >  }
> >  
> >  static inline int f2fs_has_extra_attr(struct inode *inode)
> > @@ -2308,6 +2328,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
> >  	return is_inode_flag_set(inode, FI_INLINE_DOTS);
> >  }
> >  
> > +static inline bool f2fs_is_pinned_file(struct inode *inode)
> > +{
> > +	return is_inode_flag_set(inode, FI_PIN_FILE);
> > +}
> > +
> >  static inline bool f2fs_is_atomic_file(struct inode *inode)
> >  {
> >  	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
> > @@ -2523,6 +2548,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
> >  void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
> >  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
> >  long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> > +int f2fs_pin_file_control(struct inode *inode, bool inc);
> >  
> >  /*
> >   * inode.c
> > diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> > index 322aab9d91b5..a7ee659c5a61 100644
> > --- a/fs/f2fs/file.c
> > +++ b/fs/f2fs/file.c
> > @@ -2669,6 +2669,67 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
> >  	return 0;
> >  }
> >  
> > +int f2fs_pin_file_control(struct inode *inode, bool inc)
> > +{
> > +	struct f2fs_inode_info *fi = F2FS_I(inode);
> > +	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> > +
> > +	/* Use i_gc_failures for normal file as a risk signal. */
> > +	if (inc)
> > +		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
> > +
> > +	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
> > +		f2fs_msg(sbi->sb, KERN_WARNING,
> > +			"%s: Enable GC = ino %lx after %x GC trials\n",
> > +			__func__, inode->i_ino, fi->i_gc_failures);
> > +		return -EAGAIN;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
> > +{
> > +	struct inode *inode = file_inode(filp);
> > +	__u32 pin;
> > +	int ret = 0;
> > +
> > +	if (!inode_owner_or_capable(inode))
> > +		return -EACCES;
> > +
> > +	if (get_user(pin, (__u32 __user *)arg))
> > +		return -EFAULT;
> > +
> > +	if (!S_ISREG(inode->i_mode))
> > +		return -EINVAL;
> > +
> > +	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
> > +		return -EROFS;
> 
> mnt_want_write_file(filp)?
> 
> > +
> > +	inode_lock(inode);
> > +
> > +	if (!pin) {
> > +		clear_inode_flag(inode, FI_PIN_FILE);
> > +		F2FS_I(inode)->i_gc_failures = 0;
> > +		goto done;
> > +	}
> > +
> > +	if (f2fs_pin_file_control(inode, false)) {
> > +		ret = -EAGAIN;
> > +		goto out;
> > +	}
> > +	ret = f2fs_convert_inline_inode(inode);
> > +	if (ret)
> > +		goto out;
> > +
> > +	set_inode_flag(inode, FI_PIN_FILE);
> > +	ret = F2FS_I(inode)->i_gc_failures;
> > +done:
> > +	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> > +out:
> > +	inode_unlock(inode);
> 
> mnt_drop_write_file(filp)?

Done. Thanks. :)

> 
> Other part looks good to me. ;)
> 
> Reviewed-by: Chao Yu <yuchao0@huawei.com>
> 
> Thanks,
> 
> > +	return ret;
> > +}
> > +
> >  long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >  {
> >  	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
> > @@ -2719,6 +2780,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >  		return f2fs_ioc_fsgetxattr(filp, arg);
> >  	case F2FS_IOC_FSSETXATTR:
> >  		return f2fs_ioc_fssetxattr(filp, arg);
> > +	case F2FS_IOC_SET_PIN_FILE:
> > +		return f2fs_ioc_set_pin_file(filp, arg);
> >  	default:
> >  		return -ENOTTY;
> >  	}
> > @@ -2794,6 +2857,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >  	case F2FS_IOC_GET_FEATURES:
> >  	case F2FS_IOC_FSGETXATTR:
> >  	case F2FS_IOC_FSSETXATTR:
> > +	case F2FS_IOC_SET_PIN_FILE:
> >  		break;
> >  	default:
> >  		return -ENOIOCTLCMD;
> > diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> > index 5d5bba462f26..9bffef153a12 100644
> > --- a/fs/f2fs/gc.c
> > +++ b/fs/f2fs/gc.c
> > @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
> >  	if (f2fs_is_atomic_file(inode))
> >  		goto out;
> >  
> > +	if (f2fs_is_pinned_file(inode)) {
> > +		f2fs_pin_file_control(inode, true);
> > +		goto out;
> > +	}
> > +
> >  	set_new_dnode(&dn, inode, NULL, NULL, 0);
> >  	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
> >  	if (err)
> > @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
> >  
> >  	if (f2fs_is_atomic_file(inode))
> >  		goto out;
> > +	if (f2fs_is_pinned_file(inode)) {
> > +		if (gc_type == FG_GC)
> > +			f2fs_pin_file_control(inode, true);
> > +		goto out;
> > +	}
> >  
> >  	if (gc_type == BG_GC) {
> >  		if (PageWriteback(page))
> > @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
> >  
> >  	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
> >  				BLKS_PER_SEC(sbi), (main_count - resv_count));
> > +	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
> >  
> >  	/* give warm/cold data area from slower device */
> >  	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
> > diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
> > index 9325191fab2d..33310165cb78 100644
> > --- a/fs/f2fs/gc.h
> > +++ b/fs/f2fs/gc.h
> > @@ -20,6 +20,8 @@
> >  #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
> >  #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
> >  
> > +#define DEF_GC_FAILED_PINNED_FILES	1024
> > +
> >  /* Search max. number of dirty segments to select a victim segment */
> >  #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
> >  
> > diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
> > index ab6028c332aa..41887e6ec1b3 100644
> > --- a/fs/f2fs/sysfs.c
> > +++ b/fs/f2fs/sysfs.c
> > @@ -301,6 +301,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
> >  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
> >  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
> >  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
> > +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
> >  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >  F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
> >  F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
> > @@ -349,6 +350,7 @@ static struct attribute *f2fs_attrs[] = {
> >  	ATTR_LIST(idle_interval),
> >  	ATTR_LIST(iostat_enable),
> >  	ATTR_LIST(readdir_ra),
> > +	ATTR_LIST(gc_pin_file_thresh),
> >  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >  	ATTR_LIST(inject_rate),
> >  	ATTR_LIST(inject_type),
> > diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
> > index 43e98d30d2df..cfb522e6affc 100644
> > --- a/include/linux/f2fs_fs.h
> > +++ b/include/linux/f2fs_fs.h
> > @@ -212,6 +212,7 @@ struct f2fs_extent {
> >  #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
> >  #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
> >  #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
> > +#define F2FS_PIN_FILE		0x40	/* file should not be gced */
> >  
> >  struct f2fs_inode {
> >  	__le16 i_mode;			/* file mode */
> > @@ -229,7 +230,13 @@ struct f2fs_inode {
> >  	__le32 i_ctime_nsec;		/* change time in nano scale */
> >  	__le32 i_mtime_nsec;		/* modification time in nano scale */
> >  	__le32 i_generation;		/* file version (for NFS) */
> > -	__le32 i_current_depth;		/* only for directory depth */
> > +	union {
> > +		__le32 i_current_depth;	/* only for directory depth */
> > +		__le16 i_gc_failures;	/*
> > +					 * # of gc failures on pinned file.
> > +					 * only for regular files.
> > +					 */
> > +	};
> >  	__le32 i_xattr_nid;		/* nid to save xattr */
> >  	__le32 i_flags;			/* file attributes */
> >  	__le32 i_pino;			/* parent inode number */
> > 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4] f2fs: add an ioctl to disable GC for specific file
  2018-01-03  3:21     ` [PATCH v3] " Jaegeuk Kim
@ 2018-01-16 23:57         ` Jaegeuk Kim
  2018-01-16 23:57         ` Jaegeuk Kim
  1 sibling, 0 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2018-01-16 23:57 UTC (permalink / raw)
  To: linux-kernel, linux-f2fs-devel

This patch gives a flag to disable GC on given file, which would be useful, when
user wants to keep its block map. It also conducts in-place-update for dontmove
file.

Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---

Change log from v3:
 - add F2FS_IOC_GET_PIN_FILE
 - fix to reset i_gc_failures as 1, not 0

 fs/f2fs/data.c          |  2 ++
 fs/f2fs/f2fs.h          | 29 ++++++++++++++++-
 fs/f2fs/file.c          | 82 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/f2fs/gc.c            | 11 +++++++
 fs/f2fs/gc.h            |  2 ++
 fs/f2fs/sysfs.c         |  2 ++
 include/linux/f2fs_fs.h |  9 +++++-
 7 files changed, 135 insertions(+), 2 deletions(-)

diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index cde2a3264b4c..945f15607484 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1479,6 +1479,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
 {
 	struct inode *inode = fio->page->mapping->host;
 
+	if (f2fs_is_pinned_file(inode))
+		return true;
 	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
 		return false;
 	if (is_cold_data(fio->page))
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 2da378c9d7d7..fa36a445ceda 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -356,6 +356,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
 #define F2FS_IOC_GARBAGE_COLLECT_RANGE	_IOW(F2FS_IOCTL_MAGIC, 11,	\
 						struct f2fs_gc_range)
 #define F2FS_IOC_GET_FEATURES		_IOR(F2FS_IOCTL_MAGIC, 12, __u32)
+#define F2FS_IOC_SET_PIN_FILE		_IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#define F2FS_IOC_GET_PIN_FILE		_IOR(F2FS_IOCTL_MAGIC, 14, __u32)
 
 #define F2FS_IOC_SET_ENCRYPTION_POLICY	FS_IOC_SET_ENCRYPTION_POLICY
 #define F2FS_IOC_GET_ENCRYPTION_POLICY	FS_IOC_GET_ENCRYPTION_POLICY
@@ -593,7 +595,10 @@ struct f2fs_inode_info {
 	unsigned long i_flags;		/* keep an inode flags for ioctl */
 	unsigned char i_advise;		/* use to give file attribute hints */
 	unsigned char i_dir_level;	/* use for dentry level for large dir */
-	unsigned int i_current_depth;	/* use only in directory structure */
+	union {
+		unsigned int i_current_depth;	/* only for directory depth */
+		unsigned short i_gc_failures;	/* only for regular file */
+	};
 	unsigned int i_pino;		/* parent inode number */
 	umode_t i_acl_mode;		/* keep file acl mode temporarily */
 
@@ -1142,6 +1147,9 @@ struct f2fs_sb_info {
 	/* threshold for converting bg victims for fg */
 	u64 fggc_threshold;
 
+	/* threshold for gc trials on pinned files */
+	u64 gc_pin_file_threshold;
+
 	/* maximum # of trials to find a victim segment for SSR and GC */
 	unsigned int max_victim_search;
 
@@ -2144,6 +2152,7 @@ enum {
 	FI_HOT_DATA,		/* indicate file is hot */
 	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
 	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
+	FI_PIN_FILE,		/* indicate file should not be gced */
 };
 
 static inline void __mark_inode_dirty_flag(struct inode *inode,
@@ -2158,6 +2167,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
 			return;
 	case FI_DATA_EXIST:
 	case FI_INLINE_DOTS:
+	case FI_PIN_FILE:
 		f2fs_mark_inode_dirty_sync(inode, true);
 	}
 }
@@ -2238,6 +2248,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
 	f2fs_mark_inode_dirty_sync(inode, true);
 }
 
+static inline void f2fs_i_gc_failures_write(struct inode *inode,
+					unsigned int count)
+{
+	F2FS_I(inode)->i_gc_failures = count;
+	f2fs_mark_inode_dirty_sync(inode, true);
+}
+
 static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
 {
 	F2FS_I(inode)->i_xattr_nid = xnid;
@@ -2266,6 +2283,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
 		set_bit(FI_INLINE_DOTS, &fi->flags);
 	if (ri->i_inline & F2FS_EXTRA_ATTR)
 		set_bit(FI_EXTRA_ATTR, &fi->flags);
+	if (ri->i_inline & F2FS_PIN_FILE)
+		set_bit(FI_PIN_FILE, &fi->flags);
 }
 
 static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
@@ -2284,6 +2303,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
 		ri->i_inline |= F2FS_INLINE_DOTS;
 	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
 		ri->i_inline |= F2FS_EXTRA_ATTR;
+	if (is_inode_flag_set(inode, FI_PIN_FILE))
+		ri->i_inline |= F2FS_PIN_FILE;
 }
 
 static inline int f2fs_has_extra_attr(struct inode *inode)
@@ -2329,6 +2350,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
 	return is_inode_flag_set(inode, FI_INLINE_DOTS);
 }
 
+static inline bool f2fs_is_pinned_file(struct inode *inode)
+{
+	return is_inode_flag_set(inode, FI_PIN_FILE);
+}
+
 static inline bool f2fs_is_atomic_file(struct inode *inode)
 {
 	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
@@ -2543,6 +2569,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
 void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
 long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+int f2fs_pin_file_control(struct inode *inode, bool inc);
 
 /*
  * inode.c
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 05779077cbf2..7a50cffe34b9 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -2672,6 +2672,82 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
 	return 0;
 }
 
+int f2fs_pin_file_control(struct inode *inode, bool inc)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+	/* Use i_gc_failures for normal file as a risk signal. */
+	if (inc)
+		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
+
+	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
+		f2fs_msg(sbi->sb, KERN_WARNING,
+			"%s: Enable GC = ino %lx after %x GC trials\n",
+			__func__, inode->i_ino, fi->i_gc_failures);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
+{
+	struct inode *inode = file_inode(filp);
+	__u32 pin;
+	int ret = 0;
+
+	if (!inode_owner_or_capable(inode))
+		return -EACCES;
+
+	if (get_user(pin, (__u32 __user *)arg))
+		return -EFAULT;
+
+	if (!S_ISREG(inode->i_mode))
+		return -EINVAL;
+
+	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
+		return -EROFS;
+
+	ret = mnt_want_write_file(filp);
+	if (ret)
+		return ret;
+
+	inode_lock(inode);
+
+	if (!pin) {
+		clear_inode_flag(inode, FI_PIN_FILE);
+		F2FS_I(inode)->i_gc_failures = 1;
+		goto done;
+	}
+
+	if (f2fs_pin_file_control(inode, false)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+	ret = f2fs_convert_inline_inode(inode);
+	if (ret)
+		goto out;
+
+	set_inode_flag(inode, FI_PIN_FILE);
+	ret = F2FS_I(inode)->i_gc_failures;
+done:
+	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
+out:
+	inode_unlock(inode);
+	mnt_drop_write_file(filp);
+	return ret;
+}
+
+static int f2fs_ioc_get_pin_file(struct file *filp, unsigned long arg)
+{
+	struct inode *inode = file_inode(filp);
+	__u32 pin = 0;
+
+	if (is_inode_flag_set(inode, FI_PIN_FILE))
+		pin = F2FS_I(inode)->i_gc_failures;
+	return put_user(pin, (u32 __user *)arg);
+}
+
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
@@ -2722,6 +2798,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		return f2fs_ioc_fsgetxattr(filp, arg);
 	case F2FS_IOC_FSSETXATTR:
 		return f2fs_ioc_fssetxattr(filp, arg);
+	case F2FS_IOC_GET_PIN_FILE:
+		return f2fs_ioc_get_pin_file(filp, arg);
+	case F2FS_IOC_SET_PIN_FILE:
+		return f2fs_ioc_set_pin_file(filp, arg);
 	default:
 		return -ENOTTY;
 	}
@@ -2797,6 +2877,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case F2FS_IOC_GET_FEATURES:
 	case F2FS_IOC_FSGETXATTR:
 	case F2FS_IOC_FSSETXATTR:
+	case F2FS_IOC_GET_PIN_FILE:
+	case F2FS_IOC_SET_PIN_FILE:
 		break;
 	default:
 		return -ENOIOCTLCMD;
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index d844dcb80570..33e79697e41c 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
 	if (f2fs_is_atomic_file(inode))
 		goto out;
 
+	if (f2fs_is_pinned_file(inode)) {
+		f2fs_pin_file_control(inode, true);
+		goto out;
+	}
+
 	set_new_dnode(&dn, inode, NULL, NULL, 0);
 	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
 	if (err)
@@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
 
 	if (f2fs_is_atomic_file(inode))
 		goto out;
+	if (f2fs_is_pinned_file(inode)) {
+		if (gc_type == FG_GC)
+			f2fs_pin_file_control(inode, true);
+		goto out;
+	}
 
 	if (gc_type == BG_GC) {
 		if (PageWriteback(page))
@@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
 
 	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
 				BLKS_PER_SEC(sbi), (main_count - resv_count));
+	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
 
 	/* give warm/cold data area from slower device */
 	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
index 9325191fab2d..33310165cb78 100644
--- a/fs/f2fs/gc.h
+++ b/fs/f2fs/gc.h
@@ -20,6 +20,8 @@
 #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
 #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
 
+#define DEF_GC_FAILED_PINNED_FILES	1024
+
 /* Search max. number of dirty segments to select a victim segment */
 #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
 
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index ab6028c332aa..41887e6ec1b3 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -301,6 +301,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
+F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
 F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
@@ -349,6 +350,7 @@ static struct attribute *f2fs_attrs[] = {
 	ATTR_LIST(idle_interval),
 	ATTR_LIST(iostat_enable),
 	ATTR_LIST(readdir_ra),
+	ATTR_LIST(gc_pin_file_thresh),
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 	ATTR_LIST(inject_rate),
 	ATTR_LIST(inject_type),
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 43e98d30d2df..cfb522e6affc 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -212,6 +212,7 @@ struct f2fs_extent {
 #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
 #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
 #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
+#define F2FS_PIN_FILE		0x40	/* file should not be gced */
 
 struct f2fs_inode {
 	__le16 i_mode;			/* file mode */
@@ -229,7 +230,13 @@ struct f2fs_inode {
 	__le32 i_ctime_nsec;		/* change time in nano scale */
 	__le32 i_mtime_nsec;		/* modification time in nano scale */
 	__le32 i_generation;		/* file version (for NFS) */
-	__le32 i_current_depth;		/* only for directory depth */
+	union {
+		__le32 i_current_depth;	/* only for directory depth */
+		__le16 i_gc_failures;	/*
+					 * # of gc failures on pinned file.
+					 * only for regular files.
+					 */
+	};
 	__le32 i_xattr_nid;		/* nid to save xattr */
 	__le32 i_flags;			/* file attributes */
 	__le32 i_pino;			/* parent inode number */
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v4] f2fs: add an ioctl to disable GC for specific file
@ 2018-01-16 23:57         ` Jaegeuk Kim
  0 siblings, 0 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2018-01-16 23:57 UTC (permalink / raw)
  To: linux-kernel, linux-f2fs-devel

This patch gives a flag to disable GC on given file, which would be useful, when
user wants to keep its block map. It also conducts in-place-update for dontmove
file.

Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---

Change log from v3:
 - add F2FS_IOC_GET_PIN_FILE
 - fix to reset i_gc_failures as 1, not 0

 fs/f2fs/data.c          |  2 ++
 fs/f2fs/f2fs.h          | 29 ++++++++++++++++-
 fs/f2fs/file.c          | 82 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/f2fs/gc.c            | 11 +++++++
 fs/f2fs/gc.h            |  2 ++
 fs/f2fs/sysfs.c         |  2 ++
 include/linux/f2fs_fs.h |  9 +++++-
 7 files changed, 135 insertions(+), 2 deletions(-)

diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index cde2a3264b4c..945f15607484 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1479,6 +1479,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
 {
 	struct inode *inode = fio->page->mapping->host;
 
+	if (f2fs_is_pinned_file(inode))
+		return true;
 	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
 		return false;
 	if (is_cold_data(fio->page))
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 2da378c9d7d7..fa36a445ceda 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -356,6 +356,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
 #define F2FS_IOC_GARBAGE_COLLECT_RANGE	_IOW(F2FS_IOCTL_MAGIC, 11,	\
 						struct f2fs_gc_range)
 #define F2FS_IOC_GET_FEATURES		_IOR(F2FS_IOCTL_MAGIC, 12, __u32)
+#define F2FS_IOC_SET_PIN_FILE		_IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#define F2FS_IOC_GET_PIN_FILE		_IOR(F2FS_IOCTL_MAGIC, 14, __u32)
 
 #define F2FS_IOC_SET_ENCRYPTION_POLICY	FS_IOC_SET_ENCRYPTION_POLICY
 #define F2FS_IOC_GET_ENCRYPTION_POLICY	FS_IOC_GET_ENCRYPTION_POLICY
@@ -593,7 +595,10 @@ struct f2fs_inode_info {
 	unsigned long i_flags;		/* keep an inode flags for ioctl */
 	unsigned char i_advise;		/* use to give file attribute hints */
 	unsigned char i_dir_level;	/* use for dentry level for large dir */
-	unsigned int i_current_depth;	/* use only in directory structure */
+	union {
+		unsigned int i_current_depth;	/* only for directory depth */
+		unsigned short i_gc_failures;	/* only for regular file */
+	};
 	unsigned int i_pino;		/* parent inode number */
 	umode_t i_acl_mode;		/* keep file acl mode temporarily */
 
@@ -1142,6 +1147,9 @@ struct f2fs_sb_info {
 	/* threshold for converting bg victims for fg */
 	u64 fggc_threshold;
 
+	/* threshold for gc trials on pinned files */
+	u64 gc_pin_file_threshold;
+
 	/* maximum # of trials to find a victim segment for SSR and GC */
 	unsigned int max_victim_search;
 
@@ -2144,6 +2152,7 @@ enum {
 	FI_HOT_DATA,		/* indicate file is hot */
 	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
 	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
+	FI_PIN_FILE,		/* indicate file should not be gced */
 };
 
 static inline void __mark_inode_dirty_flag(struct inode *inode,
@@ -2158,6 +2167,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
 			return;
 	case FI_DATA_EXIST:
 	case FI_INLINE_DOTS:
+	case FI_PIN_FILE:
 		f2fs_mark_inode_dirty_sync(inode, true);
 	}
 }
@@ -2238,6 +2248,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
 	f2fs_mark_inode_dirty_sync(inode, true);
 }
 
+static inline void f2fs_i_gc_failures_write(struct inode *inode,
+					unsigned int count)
+{
+	F2FS_I(inode)->i_gc_failures = count;
+	f2fs_mark_inode_dirty_sync(inode, true);
+}
+
 static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
 {
 	F2FS_I(inode)->i_xattr_nid = xnid;
@@ -2266,6 +2283,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
 		set_bit(FI_INLINE_DOTS, &fi->flags);
 	if (ri->i_inline & F2FS_EXTRA_ATTR)
 		set_bit(FI_EXTRA_ATTR, &fi->flags);
+	if (ri->i_inline & F2FS_PIN_FILE)
+		set_bit(FI_PIN_FILE, &fi->flags);
 }
 
 static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
@@ -2284,6 +2303,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
 		ri->i_inline |= F2FS_INLINE_DOTS;
 	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
 		ri->i_inline |= F2FS_EXTRA_ATTR;
+	if (is_inode_flag_set(inode, FI_PIN_FILE))
+		ri->i_inline |= F2FS_PIN_FILE;
 }
 
 static inline int f2fs_has_extra_attr(struct inode *inode)
@@ -2329,6 +2350,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
 	return is_inode_flag_set(inode, FI_INLINE_DOTS);
 }
 
+static inline bool f2fs_is_pinned_file(struct inode *inode)
+{
+	return is_inode_flag_set(inode, FI_PIN_FILE);
+}
+
 static inline bool f2fs_is_atomic_file(struct inode *inode)
 {
 	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
@@ -2543,6 +2569,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
 void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
 long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+int f2fs_pin_file_control(struct inode *inode, bool inc);
 
 /*
  * inode.c
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 05779077cbf2..7a50cffe34b9 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -2672,6 +2672,82 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
 	return 0;
 }
 
+int f2fs_pin_file_control(struct inode *inode, bool inc)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+	/* Use i_gc_failures for normal file as a risk signal. */
+	if (inc)
+		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
+
+	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
+		f2fs_msg(sbi->sb, KERN_WARNING,
+			"%s: Enable GC = ino %lx after %x GC trials\n",
+			__func__, inode->i_ino, fi->i_gc_failures);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
+{
+	struct inode *inode = file_inode(filp);
+	__u32 pin;
+	int ret = 0;
+
+	if (!inode_owner_or_capable(inode))
+		return -EACCES;
+
+	if (get_user(pin, (__u32 __user *)arg))
+		return -EFAULT;
+
+	if (!S_ISREG(inode->i_mode))
+		return -EINVAL;
+
+	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
+		return -EROFS;
+
+	ret = mnt_want_write_file(filp);
+	if (ret)
+		return ret;
+
+	inode_lock(inode);
+
+	if (!pin) {
+		clear_inode_flag(inode, FI_PIN_FILE);
+		F2FS_I(inode)->i_gc_failures = 1;
+		goto done;
+	}
+
+	if (f2fs_pin_file_control(inode, false)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+	ret = f2fs_convert_inline_inode(inode);
+	if (ret)
+		goto out;
+
+	set_inode_flag(inode, FI_PIN_FILE);
+	ret = F2FS_I(inode)->i_gc_failures;
+done:
+	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
+out:
+	inode_unlock(inode);
+	mnt_drop_write_file(filp);
+	return ret;
+}
+
+static int f2fs_ioc_get_pin_file(struct file *filp, unsigned long arg)
+{
+	struct inode *inode = file_inode(filp);
+	__u32 pin = 0;
+
+	if (is_inode_flag_set(inode, FI_PIN_FILE))
+		pin = F2FS_I(inode)->i_gc_failures;
+	return put_user(pin, (u32 __user *)arg);
+}
+
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
@@ -2722,6 +2798,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		return f2fs_ioc_fsgetxattr(filp, arg);
 	case F2FS_IOC_FSSETXATTR:
 		return f2fs_ioc_fssetxattr(filp, arg);
+	case F2FS_IOC_GET_PIN_FILE:
+		return f2fs_ioc_get_pin_file(filp, arg);
+	case F2FS_IOC_SET_PIN_FILE:
+		return f2fs_ioc_set_pin_file(filp, arg);
 	default:
 		return -ENOTTY;
 	}
@@ -2797,6 +2877,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case F2FS_IOC_GET_FEATURES:
 	case F2FS_IOC_FSGETXATTR:
 	case F2FS_IOC_FSSETXATTR:
+	case F2FS_IOC_GET_PIN_FILE:
+	case F2FS_IOC_SET_PIN_FILE:
 		break;
 	default:
 		return -ENOIOCTLCMD;
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index d844dcb80570..33e79697e41c 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
 	if (f2fs_is_atomic_file(inode))
 		goto out;
 
+	if (f2fs_is_pinned_file(inode)) {
+		f2fs_pin_file_control(inode, true);
+		goto out;
+	}
+
 	set_new_dnode(&dn, inode, NULL, NULL, 0);
 	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
 	if (err)
@@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
 
 	if (f2fs_is_atomic_file(inode))
 		goto out;
+	if (f2fs_is_pinned_file(inode)) {
+		if (gc_type == FG_GC)
+			f2fs_pin_file_control(inode, true);
+		goto out;
+	}
 
 	if (gc_type == BG_GC) {
 		if (PageWriteback(page))
@@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
 
 	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
 				BLKS_PER_SEC(sbi), (main_count - resv_count));
+	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
 
 	/* give warm/cold data area from slower device */
 	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
index 9325191fab2d..33310165cb78 100644
--- a/fs/f2fs/gc.h
+++ b/fs/f2fs/gc.h
@@ -20,6 +20,8 @@
 #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
 #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
 
+#define DEF_GC_FAILED_PINNED_FILES	1024
+
 /* Search max. number of dirty segments to select a victim segment */
 #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
 
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index ab6028c332aa..41887e6ec1b3 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -301,6 +301,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
+F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
 F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
@@ -349,6 +350,7 @@ static struct attribute *f2fs_attrs[] = {
 	ATTR_LIST(idle_interval),
 	ATTR_LIST(iostat_enable),
 	ATTR_LIST(readdir_ra),
+	ATTR_LIST(gc_pin_file_thresh),
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 	ATTR_LIST(inject_rate),
 	ATTR_LIST(inject_type),
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 43e98d30d2df..cfb522e6affc 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -212,6 +212,7 @@ struct f2fs_extent {
 #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
 #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
 #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
+#define F2FS_PIN_FILE		0x40	/* file should not be gced */
 
 struct f2fs_inode {
 	__le16 i_mode;			/* file mode */
@@ -229,7 +230,13 @@ struct f2fs_inode {
 	__le32 i_ctime_nsec;		/* change time in nano scale */
 	__le32 i_mtime_nsec;		/* modification time in nano scale */
 	__le32 i_generation;		/* file version (for NFS) */
-	__le32 i_current_depth;		/* only for directory depth */
+	union {
+		__le32 i_current_depth;	/* only for directory depth */
+		__le16 i_gc_failures;	/*
+					 * # of gc failures on pinned file.
+					 * only for regular files.
+					 */
+	};
 	__le32 i_xattr_nid;		/* nid to save xattr */
 	__le32 i_flags;			/* file attributes */
 	__le32 i_pino;			/* parent inode number */
-- 
2.15.0.531.g2ccb3012c9-goog


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v5] f2fs: add an ioctl to disable GC for specific file
  2018-01-16 23:57         ` Jaegeuk Kim
  (?)
@ 2018-01-22 22:59         ` Jaegeuk Kim
  -1 siblings, 0 replies; 27+ messages in thread
From: Jaegeuk Kim @ 2018-01-22 22:59 UTC (permalink / raw)
  To: linux-kernel, linux-f2fs-devel

This patch gives a flag to disable GC on given file, which would be useful, when
user wants to keep its block map. It also conducts in-place-update for dontmove
file.

Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---

Change log from v1:
 - unset pin_file, if # of trials exceeds a threshold
 - increase the threshold to 2048

 fs/f2fs/data.c          |  2 ++
 fs/f2fs/f2fs.h          | 29 ++++++++++++++++-
 fs/f2fs/file.c          | 83 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/f2fs/gc.c            | 11 +++++++
 fs/f2fs/gc.h            |  2 ++
 fs/f2fs/sysfs.c         |  2 ++
 include/linux/f2fs_fs.h |  9 +++++-
 7 files changed, 136 insertions(+), 2 deletions(-)

diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index cde2a3264b4c..945f15607484 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1479,6 +1479,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
 {
 	struct inode *inode = fio->page->mapping->host;
 
+	if (f2fs_is_pinned_file(inode))
+		return true;
 	if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode))
 		return false;
 	if (is_cold_data(fio->page))
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 1b2b0acbc83d..d5090b4d2633 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -356,6 +356,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
 #define F2FS_IOC_GARBAGE_COLLECT_RANGE	_IOW(F2FS_IOCTL_MAGIC, 11,	\
 						struct f2fs_gc_range)
 #define F2FS_IOC_GET_FEATURES		_IOR(F2FS_IOCTL_MAGIC, 12, __u32)
+#define F2FS_IOC_SET_PIN_FILE		_IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#define F2FS_IOC_GET_PIN_FILE		_IOR(F2FS_IOCTL_MAGIC, 14, __u32)
 
 #define F2FS_IOC_SET_ENCRYPTION_POLICY	FS_IOC_SET_ENCRYPTION_POLICY
 #define F2FS_IOC_GET_ENCRYPTION_POLICY	FS_IOC_GET_ENCRYPTION_POLICY
@@ -593,7 +595,10 @@ struct f2fs_inode_info {
 	unsigned long i_flags;		/* keep an inode flags for ioctl */
 	unsigned char i_advise;		/* use to give file attribute hints */
 	unsigned char i_dir_level;	/* use for dentry level for large dir */
-	unsigned int i_current_depth;	/* use only in directory structure */
+	union {
+		unsigned int i_current_depth;	/* only for directory depth */
+		unsigned short i_gc_failures;	/* only for regular file */
+	};
 	unsigned int i_pino;		/* parent inode number */
 	umode_t i_acl_mode;		/* keep file acl mode temporarily */
 
@@ -1142,6 +1147,9 @@ struct f2fs_sb_info {
 	/* threshold for converting bg victims for fg */
 	u64 fggc_threshold;
 
+	/* threshold for gc trials on pinned files */
+	u64 gc_pin_file_threshold;
+
 	/* maximum # of trials to find a victim segment for SSR and GC */
 	unsigned int max_victim_search;
 
@@ -2141,6 +2149,7 @@ enum {
 	FI_HOT_DATA,		/* indicate file is hot */
 	FI_EXTRA_ATTR,		/* indicate file has extra attribute */
 	FI_PROJ_INHERIT,	/* indicate file inherits projectid */
+	FI_PIN_FILE,		/* indicate file should not be gced */
 };
 
 static inline void __mark_inode_dirty_flag(struct inode *inode,
@@ -2155,6 +2164,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
 			return;
 	case FI_DATA_EXIST:
 	case FI_INLINE_DOTS:
+	case FI_PIN_FILE:
 		f2fs_mark_inode_dirty_sync(inode, true);
 	}
 }
@@ -2235,6 +2245,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
 	f2fs_mark_inode_dirty_sync(inode, true);
 }
 
+static inline void f2fs_i_gc_failures_write(struct inode *inode,
+					unsigned int count)
+{
+	F2FS_I(inode)->i_gc_failures = count;
+	f2fs_mark_inode_dirty_sync(inode, true);
+}
+
 static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid)
 {
 	F2FS_I(inode)->i_xattr_nid = xnid;
@@ -2263,6 +2280,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
 		set_bit(FI_INLINE_DOTS, &fi->flags);
 	if (ri->i_inline & F2FS_EXTRA_ATTR)
 		set_bit(FI_EXTRA_ATTR, &fi->flags);
+	if (ri->i_inline & F2FS_PIN_FILE)
+		set_bit(FI_PIN_FILE, &fi->flags);
 }
 
 static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
@@ -2281,6 +2300,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
 		ri->i_inline |= F2FS_INLINE_DOTS;
 	if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
 		ri->i_inline |= F2FS_EXTRA_ATTR;
+	if (is_inode_flag_set(inode, FI_PIN_FILE))
+		ri->i_inline |= F2FS_PIN_FILE;
 }
 
 static inline int f2fs_has_extra_attr(struct inode *inode)
@@ -2326,6 +2347,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode)
 	return is_inode_flag_set(inode, FI_INLINE_DOTS);
 }
 
+static inline bool f2fs_is_pinned_file(struct inode *inode)
+{
+	return is_inode_flag_set(inode, FI_PIN_FILE);
+}
+
 static inline bool f2fs_is_atomic_file(struct inode *inode)
 {
 	return is_inode_flag_set(inode, FI_ATOMIC_FILE);
@@ -2540,6 +2566,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
 void truncate_data_blocks_range(struct dnode_of_data *dn, int count);
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
 long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+int f2fs_pin_file_control(struct inode *inode, bool inc);
 
 /*
  * inode.c
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 05779077cbf2..493457a200b1 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -2672,6 +2672,83 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
 	return 0;
 }
 
+int f2fs_pin_file_control(struct inode *inode, bool inc)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+	/* Use i_gc_failures for normal file as a risk signal. */
+	if (inc)
+		f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1);
+
+	if (fi->i_gc_failures > sbi->gc_pin_file_threshold) {
+		f2fs_msg(sbi->sb, KERN_WARNING,
+			"%s: Enable GC = ino %lx after %x GC trials\n",
+			__func__, inode->i_ino, fi->i_gc_failures);
+		clear_inode_flag(inode, FI_PIN_FILE);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
+{
+	struct inode *inode = file_inode(filp);
+	__u32 pin;
+	int ret = 0;
+
+	if (!inode_owner_or_capable(inode))
+		return -EACCES;
+
+	if (get_user(pin, (__u32 __user *)arg))
+		return -EFAULT;
+
+	if (!S_ISREG(inode->i_mode))
+		return -EINVAL;
+
+	if (f2fs_readonly(F2FS_I_SB(inode)->sb))
+		return -EROFS;
+
+	ret = mnt_want_write_file(filp);
+	if (ret)
+		return ret;
+
+	inode_lock(inode);
+
+	if (!pin) {
+		clear_inode_flag(inode, FI_PIN_FILE);
+		F2FS_I(inode)->i_gc_failures = 1;
+		goto done;
+	}
+
+	if (f2fs_pin_file_control(inode, false)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+	ret = f2fs_convert_inline_inode(inode);
+	if (ret)
+		goto out;
+
+	set_inode_flag(inode, FI_PIN_FILE);
+	ret = F2FS_I(inode)->i_gc_failures;
+done:
+	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
+out:
+	inode_unlock(inode);
+	mnt_drop_write_file(filp);
+	return ret;
+}
+
+static int f2fs_ioc_get_pin_file(struct file *filp, unsigned long arg)
+{
+	struct inode *inode = file_inode(filp);
+	__u32 pin = 0;
+
+	if (is_inode_flag_set(inode, FI_PIN_FILE))
+		pin = F2FS_I(inode)->i_gc_failures;
+	return put_user(pin, (u32 __user *)arg);
+}
+
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
@@ -2722,6 +2799,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		return f2fs_ioc_fsgetxattr(filp, arg);
 	case F2FS_IOC_FSSETXATTR:
 		return f2fs_ioc_fssetxattr(filp, arg);
+	case F2FS_IOC_GET_PIN_FILE:
+		return f2fs_ioc_get_pin_file(filp, arg);
+	case F2FS_IOC_SET_PIN_FILE:
+		return f2fs_ioc_set_pin_file(filp, arg);
 	default:
 		return -ENOTTY;
 	}
@@ -2797,6 +2878,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case F2FS_IOC_GET_FEATURES:
 	case F2FS_IOC_FSGETXATTR:
 	case F2FS_IOC_FSSETXATTR:
+	case F2FS_IOC_GET_PIN_FILE:
+	case F2FS_IOC_SET_PIN_FILE:
 		break;
 	default:
 		return -ENOIOCTLCMD;
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index d844dcb80570..33e79697e41c 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx,
 	if (f2fs_is_atomic_file(inode))
 		goto out;
 
+	if (f2fs_is_pinned_file(inode)) {
+		f2fs_pin_file_control(inode, true);
+		goto out;
+	}
+
 	set_new_dnode(&dn, inode, NULL, NULL, 0);
 	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
 	if (err)
@@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
 
 	if (f2fs_is_atomic_file(inode))
 		goto out;
+	if (f2fs_is_pinned_file(inode)) {
+		if (gc_type == FG_GC)
+			f2fs_pin_file_control(inode, true);
+		goto out;
+	}
 
 	if (gc_type == BG_GC) {
 		if (PageWriteback(page))
@@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
 
 	sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
 				BLKS_PER_SEC(sbi), (main_count - resv_count));
+	sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
 
 	/* give warm/cold data area from slower device */
 	if (sbi->s_ndevs && sbi->segs_per_sec == 1)
diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
index 9325191fab2d..b0045d4c8d1e 100644
--- a/fs/f2fs/gc.h
+++ b/fs/f2fs/gc.h
@@ -20,6 +20,8 @@
 #define LIMIT_INVALID_BLOCK	40 /* percentage over total user space */
 #define LIMIT_FREE_BLOCK	40 /* percentage over invalid + free space */
 
+#define DEF_GC_FAILED_PINNED_FILES	2048
+
 /* Search max. number of dirty segments to select a victim segment */
 #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */
 
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index ab6028c332aa..41887e6ec1b3 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -301,6 +301,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
+F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
 F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
@@ -349,6 +350,7 @@ static struct attribute *f2fs_attrs[] = {
 	ATTR_LIST(idle_interval),
 	ATTR_LIST(iostat_enable),
 	ATTR_LIST(readdir_ra),
+	ATTR_LIST(gc_pin_file_thresh),
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 	ATTR_LIST(inject_rate),
 	ATTR_LIST(inject_type),
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 43e98d30d2df..cfb522e6affc 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -212,6 +212,7 @@ struct f2fs_extent {
 #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
 #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
 #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
+#define F2FS_PIN_FILE		0x40	/* file should not be gced */
 
 struct f2fs_inode {
 	__le16 i_mode;			/* file mode */
@@ -229,7 +230,13 @@ struct f2fs_inode {
 	__le32 i_ctime_nsec;		/* change time in nano scale */
 	__le32 i_mtime_nsec;		/* modification time in nano scale */
 	__le32 i_generation;		/* file version (for NFS) */
-	__le32 i_current_depth;		/* only for directory depth */
+	union {
+		__le32 i_current_depth;	/* only for directory depth */
+		__le16 i_gc_failures;	/*
+					 * # of gc failures on pinned file.
+					 * only for regular files.
+					 */
+	};
 	__le32 i_xattr_nid;		/* nid to save xattr */
 	__le32 i_flags;			/* file attributes */
 	__le32 i_pino;			/* parent inode number */
-- 
2.15.0.531.g2ccb3012c9-goog

^ permalink raw reply related	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2018-01-22 22:59 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-12-08  5:15 [PATCH] f2fs: add an ioctl to disable GC for specific file Jaegeuk Kim
2017-12-08 19:37 ` [PATCH v2] " Jaegeuk Kim
2017-12-12  9:52   ` [f2fs-dev] " Chao Yu
2017-12-12  9:52     ` Chao Yu
2017-12-14 19:50     ` Jaegeuk Kim
2017-12-18 12:48       ` Chao Yu
2017-12-18 12:48         ` Chao Yu
2017-12-19 22:52         ` Jaegeuk Kim
2017-12-19 22:52           ` Jaegeuk Kim
2017-12-25  2:52           ` [f2fs-dev] " Chao Yu
2017-12-25  2:52             ` Chao Yu
2017-12-28  3:40   ` Jaegeuk Kim
2017-12-28  3:40     ` Jaegeuk Kim
2017-12-29  8:29     ` Chao Yu
2017-12-29  8:29       ` Chao Yu
2018-01-01  1:07       ` Jaegeuk Kim
2018-01-01  1:07         ` Jaegeuk Kim
2018-01-02  3:29         ` Chao Yu
2018-01-02  3:29           ` Chao Yu
2018-01-03  3:22           ` Jaegeuk Kim
2018-01-03  3:21     ` [PATCH v3] " Jaegeuk Kim
2018-01-03  7:34       ` Chao Yu
2018-01-03  7:34         ` Chao Yu
2018-01-03 18:59         ` Jaegeuk Kim
2018-01-16 23:57       ` [PATCH v4] " Jaegeuk Kim
2018-01-16 23:57         ` Jaegeuk Kim
2018-01-22 22:59         ` [PATCH v5] " Jaegeuk Kim

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.