Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d2e72fa0 authored by Daeho Jeong's avatar Daeho Jeong Committed by Greg Kroah-Hartman
Browse files

BACKPORT: f2fs: change i_compr_blocks of inode to atomic value



commit c2759ebaf7e8 upstream.

writepages() can be concurrently invoked for the same file by different
threads such as a thread fsyncing the file and a kworker kernel thread.
So, changing i_compr_blocks without protection is racy and we need to
protect it by changing it with atomic type value. Plus, we don't need
a 64bit value for i_compr_blocks, so just we will use a atomic value,
not atomic64.

Bug: 237904436
Signed-off-by: default avatarDaeho Jeong <daehojeong@google.com>
Reviewed-by: default avatarChao Yu <yuchao0@huawei.com>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
Change-Id: I11f837249078d1b7ef50ac9c156c375542c714b3
parent 86290591
Loading
Loading
Loading
Loading
+10 −7
Original line number Diff line number Diff line
@@ -814,7 +814,7 @@ struct f2fs_inode_info {
	struct timespec64 i_disk_time[4];/* inode disk times */

	/* for file compress */
	u64 i_compr_blocks;			/* # of compressed blocks */
	atomic_t i_compr_blocks;		/* # of compressed blocks */
	unsigned char i_compress_algorithm;	/* algorithm type */
	unsigned char i_log_cluster_size;	/* log of cluster size */
	unsigned int i_cluster_size;		/* cluster size */
@@ -3942,17 +3942,19 @@ static inline void set_compress_context(struct inode *inode)
	f2fs_mark_inode_dirty_sync(inode, true);
}

static inline u64 f2fs_disable_compressed_file(struct inode *inode)
static inline u32 f2fs_disable_compressed_file(struct inode *inode)
{
	struct f2fs_inode_info *fi = F2FS_I(inode);
	u32 i_compr_blocks;

	if (!f2fs_compressed_file(inode))
		return 0;
	if (S_ISREG(inode->i_mode)) {
		if (get_dirty_pages(inode))
			return 1;
		if (fi->i_compr_blocks)
			return fi->i_compr_blocks;
		i_compr_blocks = atomic_read(&fi->i_compr_blocks);
		if (i_compr_blocks)
			return i_compr_blocks;
	}

	fi->i_flags &= ~F2FS_COMPR_FL;
@@ -4070,16 +4072,17 @@ static inline void f2fs_i_compr_blocks_update(struct inode *inode,
						u64 blocks, bool add)
{
	int diff = F2FS_I(inode)->i_cluster_size - blocks;
	struct f2fs_inode_info *fi = F2FS_I(inode);

	/* don't update i_compr_blocks if saved blocks were released */
	if (!add && !F2FS_I(inode)->i_compr_blocks)
	if (!add && !atomic_read(&fi->i_compr_blocks))
		return;

	if (add) {
		F2FS_I(inode)->i_compr_blocks += diff;
		atomic_add(diff, &fi->i_compr_blocks);
		stat_add_compr_blocks(inode, diff);
	} else {
		F2FS_I(inode)->i_compr_blocks -= diff;
		atomic_sub(diff, &fi->i_compr_blocks);
		stat_sub_compr_blocks(inode, diff);
	}
	f2fs_mark_inode_dirty_sync(inode, true);
+12 −10
Original line number Diff line number Diff line
@@ -557,7 +557,7 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
	bool compressed_cluster = false;
	int cluster_index = 0, valid_blocks = 0;
	int cluster_size = F2FS_I(dn->inode)->i_cluster_size;
	bool released = !F2FS_I(dn->inode)->i_compr_blocks;
	bool released = !atomic_read(&F2FS_I(dn->inode)->i_compr_blocks);

	if (IS_INODE(dn->node_page) && f2fs_has_extra_attr(dn->inode))
		base = get_extra_isize(dn->inode);
@@ -3429,7 +3429,7 @@ static int f2fs_get_compress_blocks(struct file *filp, unsigned long arg)
	if (!f2fs_compressed_file(inode))
		return -EINVAL;

	blocks = F2FS_I(inode)->i_compr_blocks;
	blocks = atomic_read(&F2FS_I(inode)->i_compr_blocks);
	return put_user(blocks, (u64 __user *)arg);
}

@@ -3533,7 +3533,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg)
	inode->i_ctime = current_time(inode);
	f2fs_mark_inode_dirty_sync(inode, true);

	if (!F2FS_I(inode)->i_compr_blocks)
	if (!atomic_read(&F2FS_I(inode)->i_compr_blocks))
		goto out;

	down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
@@ -3581,14 +3581,15 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg)

	if (ret >= 0) {
		ret = put_user(released_blocks, (u64 __user *)arg);
	} else if (released_blocks && F2FS_I(inode)->i_compr_blocks) {
	} else if (released_blocks &&
			atomic_read(&F2FS_I(inode)->i_compr_blocks)) {
		set_sbi_flag(sbi, SBI_NEED_FSCK);
		f2fs_warn(sbi, "%s: partial blocks were released i_ino=%lx "
			"iblocks=%llu, released=%u, compr_blocks=%llu, "
			"iblocks=%llu, released=%u, compr_blocks=%u, "
			"run fsck to fix.",
			__func__, inode->i_ino, inode->i_blocks,
			released_blocks,
			F2FS_I(inode)->i_compr_blocks);
			atomic_read(&F2FS_I(inode)->i_compr_blocks));
	}

	return ret;
@@ -3676,7 +3677,7 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg)
	if (ret)
		return ret;

	if (F2FS_I(inode)->i_compr_blocks)
	if (atomic_read(&F2FS_I(inode)->i_compr_blocks))
		goto out;

	f2fs_balance_fs(F2FS_I_SB(inode), true);
@@ -3740,14 +3741,15 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg)

	if (ret >= 0) {
		ret = put_user(reserved_blocks, (u64 __user *)arg);
	} else if (reserved_blocks && F2FS_I(inode)->i_compr_blocks) {
	} else if (reserved_blocks &&
			atomic_read(&F2FS_I(inode)->i_compr_blocks)) {
		set_sbi_flag(sbi, SBI_NEED_FSCK);
		f2fs_warn(sbi, "%s: partial blocks were released i_ino=%lx "
			"iblocks=%llu, reserved=%u, compr_blocks=%llu, "
			"iblocks=%llu, reserved=%u, compr_blocks=%u, "
			"run fsck to fix.",
			__func__, inode->i_ino, inode->i_blocks,
			reserved_blocks,
			F2FS_I(inode)->i_compr_blocks);
			atomic_read(&F2FS_I(inode)->i_compr_blocks));
	}

	return ret;
+7 −4
Original line number Diff line number Diff line
@@ -442,7 +442,8 @@ static int do_read_inode(struct inode *inode)
					(fi->i_flags & F2FS_COMPR_FL)) {
		if (F2FS_FITS_IN_INODE(ri, fi->i_extra_isize,
					i_log_cluster_size)) {
			fi->i_compr_blocks = le64_to_cpu(ri->i_compr_blocks);
			atomic_set(&fi->i_compr_blocks,
					le64_to_cpu(ri->i_compr_blocks));
			fi->i_compress_algorithm = ri->i_compress_algorithm;
			fi->i_log_cluster_size = ri->i_log_cluster_size;
			fi->i_cluster_size = 1 << fi->i_log_cluster_size;
@@ -460,7 +461,7 @@ static int do_read_inode(struct inode *inode)
	stat_inc_inline_inode(inode);
	stat_inc_inline_dir(inode);
	stat_inc_compr_inode(inode);
	stat_add_compr_blocks(inode, F2FS_I(inode)->i_compr_blocks);
	stat_add_compr_blocks(inode, atomic_read(&fi->i_compr_blocks));

	return 0;
}
@@ -619,7 +620,8 @@ void f2fs_update_inode(struct inode *inode, struct page *node_page)
			F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize,
							i_log_cluster_size)) {
			ri->i_compr_blocks =
				cpu_to_le64(F2FS_I(inode)->i_compr_blocks);
				cpu_to_le64(atomic_read(
					&F2FS_I(inode)->i_compr_blocks));
			ri->i_compress_algorithm =
				F2FS_I(inode)->i_compress_algorithm;
			ri->i_log_cluster_size =
@@ -768,7 +770,8 @@ void f2fs_evict_inode(struct inode *inode)
	stat_dec_inline_dir(inode);
	stat_dec_inline_inode(inode);
	stat_dec_compr_inode(inode);
	stat_sub_compr_blocks(inode, F2FS_I(inode)->i_compr_blocks);
	stat_sub_compr_blocks(inode,
			atomic_read(&F2FS_I(inode)->i_compr_blocks));

	if (likely(!f2fs_cp_error(sbi) &&
				!is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
+1 −0
Original line number Diff line number Diff line
@@ -1054,6 +1054,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)

	/* Initialize f2fs-specific inode info */
	atomic_set(&fi->dirty_pages, 0);
	atomic_set(&fi->i_compr_blocks, 0);
	init_rwsem(&fi->i_sem);
	spin_lock_init(&fi->i_size_lock);
	INIT_LIST_HEAD(&fi->dirty_list);