@@ -1636,7 +1636,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag)
/* use out-place-update for direct IO under LFS mode */
if (map->m_may_create && (is_hole ||
(flag == F2FS_GET_BLOCK_DIO && f2fs_lfs_mode(sbi) &&
- !f2fs_is_pinned_file(inode)))) {
+ !f2fs_is_pinned_file(inode) && !f2fs_is_nocow_file(inode)))) {
if (unlikely(f2fs_cp_error(sbi))) {
err = -EIO;
goto sync_out;
@@ -2636,6 +2636,11 @@ bool f2fs_should_update_inplace(struct inode *inode, struct f2fs_io_info *fio)
if (file_is_cold(inode) && !is_inode_flag_set(inode, FI_OPU_WRITE))
return true;
+ /* allows NOCoW file to be migrated during defragmentation and swapon */
+ if (f2fs_is_nocow_file(inode) &&
+ !is_inode_flag_set(inode, FI_OPU_WRITE))
+ return true;
+
return check_inplace_update_policy(inode, fio);
}
@@ -2658,6 +2663,9 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)
sbi->first_zoned_segno)
return true;
}
+ /* should check NOCoW flag after lfs_mode check condition */
+ if (f2fs_is_nocow_file(inode))
+ return false;
if (S_ISDIR(inode->i_mode))
return true;
if (IS_NOQUOTA(inode))
@@ -2722,11 +2730,22 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
DATA_GENERIC_ENHANCE))
return -EFSCORRUPTED;
+ /*
+ * if data block of NOCoW inode locates in sequential-zone,
+ * let's trigger OPU.
+ */
+ if (f2fs_is_nocow_file(inode) &&
+ f2fs_blkzoned_has_regular_section(fio->sbi) &&
+ GET_SEGNO(fio->sbi, fio->old_blkaddr) >=
+ fio->sbi->first_zoned_segno)
+ goto get_dnode;
+
ipu_force = true;
fio->need_lock = LOCK_DONE;
goto got_it;
}
+get_dnode:
/* Deadlock due to between page->lock and f2fs_lock_op */
if (fio->need_lock == LOCK_REQ && !f2fs_trylock_op(fio->sbi))
return -EAGAIN;
@@ -3047,6 +3047,7 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
#define F2FS_NOCOMP_FL 0x00000400 /* Don't compress */
#define F2FS_INDEX_FL 0x00001000 /* hash-indexed directory */
#define F2FS_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */
+#define F2FS_NOCOW_FL 0x00800000 /* Do not cow file */
#define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
#define F2FS_CASEFOLD_FL 0x40000000 /* Casefolded file */
#define F2FS_DEVICE_ALIAS_FL 0x80000000 /* File for aliasing a device */
@@ -3332,6 +3333,11 @@ static inline bool f2fs_is_cow_file(struct inode *inode)
return is_inode_flag_set(inode, FI_COW_FILE);
}
+static inline bool f2fs_is_nocow_file(struct inode *inode)
+{
+ return F2FS_I(inode)->i_flags & F2FS_NOCOW_FL;
+}
+
static inline void *inline_data_addr(struct inode *inode, struct page *page)
{
__le32 *addr = get_dnode_addr(inode, page);
@@ -4658,8 +4664,8 @@ static inline bool f2fs_low_mem_mode(struct f2fs_sb_info *sbi)
static inline bool f2fs_may_compress(struct inode *inode)
{
if (IS_SWAPFILE(inode) || f2fs_is_pinned_file(inode) ||
- f2fs_is_atomic_file(inode) || f2fs_has_inline_data(inode) ||
- f2fs_is_mmap_file(inode))
+ f2fs_is_nocow_file(inode) || f2fs_is_atomic_file(inode) ||
+ f2fs_has_inline_data(inode) || f2fs_is_mmap_file(inode))
return false;
return S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode);
}
@@ -1998,6 +1998,20 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
if (IS_NOQUOTA(inode))
return -EPERM;
+ if ((iflags ^ masked_flags) & F2FS_NOCOW_FL) {
+ if (iflags & F2FS_NOCOW_FL) {
+ int ret;
+
+ if (!S_ISREG(inode->i_mode))
+ return -EINVAL;
+ if (f2fs_should_update_outplace(inode, NULL))
+ return -EINVAL;
+ ret = f2fs_convert_inline_inode(inode);
+ if (ret)
+ return ret;
+ }
+ }
+
if ((iflags ^ masked_flags) & F2FS_CASEFOLD_FL) {
if (!f2fs_sb_has_casefold(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
@@ -2077,6 +2091,7 @@ static const struct {
{ F2FS_NOCOMP_FL, FS_NOCOMP_FL },
{ F2FS_INDEX_FL, FS_INDEX_FL },
{ F2FS_DIRSYNC_FL, FS_DIRSYNC_FL },
+ { F2FS_NOCOW_FL, FS_NOCOW_FL },
{ F2FS_PROJINHERIT_FL, FS_PROJINHERIT_FL },
{ F2FS_CASEFOLD_FL, FS_CASEFOLD_FL },
};
@@ -2108,7 +2123,8 @@ static const struct {
FS_NOCOMP_FL | \
FS_DIRSYNC_FL | \
FS_PROJINHERIT_FL | \
- FS_CASEFOLD_FL)
+ FS_CASEFOLD_FL | \
+ FS_NOCOW_FL)
/* Convert f2fs on-disk i_flags to FS_IOC_{GET,SET}FLAGS flags */
static inline u32 f2fs_iflags_to_fsflags(u32 iflags)
@@ -2171,7 +2187,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
inode_lock(inode);
if (!f2fs_disable_compressed_file(inode) ||
- f2fs_is_pinned_file(inode)) {
+ f2fs_is_pinned_file(inode) || f2fs_is_nocow_file(inode)) {
ret = -EINVAL;
goto out;
}
@@ -3273,8 +3289,6 @@ int f2fs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
fsflags |= FS_VERITY_FL;
if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode))
fsflags |= FS_INLINE_DATA_FL;
- if (is_inode_flag_set(inode, FI_PIN_FILE))
- fsflags |= FS_NOCOW_FL;
fileattr_fill_flags(fa, fsflags & F2FS_GETTABLE_FS_FL);
Overhead of pinfile is heavy due to: 1) GC tries to skip migrating section which contains pinfile's data block, it will cost more time during GC, 2) and also it will take high risk when GC unpins file and migrates it's data block once inode.i_gc_failure exceeds threshold. This patch proposes to support NOCoW flag on inode, which has similar semantics to pinfile: Pinfile NOCoW [Enable] only regular yes yes only empty file yes no blkzoned=on,mode=lfs Support Support blkzoned=off,mode=lfs Not support Not support [fsync/wb/dio IO behavior] block in regular device IPU IPU block in zoned device N/A OPU [Misc IO behavior] GC skip/OPU OPU defrag ioc/swapon Not support Support If userspace doesn't need pinned physical block address for following direct access, we can tag file w/ NOCoW flag instead of pinfile flag, we will be benefit from it for below cases: 1) user's write won't fragment NOCoW file's data block due to IPU 2) GC/defrag/swapon won't fail on NOCoW file. Please note that, this patch changes to not show NOCoW flag for a pinned file, the logic is as below: Before Pinned file lsattr C f2fs_io pinfile get pinned After Pinned file NOCoW file lsattr - C f2fs_io pinfile get pinned un-pinned So, to consider backward compatibility, if NOCoW flag shown on a target inode, we need to check pinned flag to see whether NOCoW flag was really tagged or not. Signed-off-by: Chao Yu <chao@kernel.org> --- fs/f2fs/data.c | 21 ++++++++++++++++++++- fs/f2fs/f2fs.h | 10 ++++++++-- fs/f2fs/file.c | 22 ++++++++++++++++++---- 3 files changed, 46 insertions(+), 7 deletions(-)