Commit a436a0b8 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'ext4_for_linux-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Pull ext4 updates from Ted Ts'o:

 - Refactor code paths involved with partial block zero-out in
   prearation for converting ext4 to use iomap for buffered writes

 - Remove use of d_alloc() from ext4 in preparation for the deprecation
   of this interface

 - Replace some J_ASSERTS with a journal abort so we can avoid a kernel
   panic for a localized file system error

 - Simplify various code paths in mballoc, move_extent, and fast commit

 - Fix rare deadlock in jbd2_journal_cancel_revoke() that can be
   triggered by generic/013 when blocksize < pagesize

 - Fix memory leak when releasing an extended attribute when its value
   is stored in an ea_inode

 - Fix various potential kunit test bugs in fs/ext4/extents.c

 - Fix potential out-of-bounds access in check_xattr() with a corrupted
   file system

 - Make the jbd2_inode dirty range tracking safe for lockless reads

 - Avoid a WARN_ON when writeback files due to a corrupted file system;
   we already print an ext4 warning indicatign that data will be lost,
   so the WARN_ON is not necessary and doesn't add any new information

* tag 'ext4_for_linux-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (37 commits)
  jbd2: fix deadlock in jbd2_journal_cancel_revoke()
  ext4: fix missing brelse() in ext4_xattr_inode_dec_ref_all()
  ext4: fix possible null-ptr-deref in mbt_kunit_exit()
  ext4: fix possible null-ptr-deref in extents_kunit_exit()
  ext4: fix the error handling process in extents_kunit_init).
  ext4: call deactivate_super() in extents_kunit_exit()
  ext4: fix miss unlock 'sb->s_umount' in extents_kunit_init()
  ext4: fix bounds check in check_xattrs() to prevent out-of-bounds access
  ext4: zero post-EOF partial block before appending write
  ext4: move pagecache_isize_extended() out of active handle
  ext4: remove ctime/mtime update from ext4_alloc_file_blocks()
  ext4: unify SYNC mode checks in fallocate paths
  ext4: ensure zeroed partial blocks are persisted in SYNC mode
  ext4: move zero partial block range functions out of active handle
  ext4: pass allocate range as loff_t to ext4_alloc_file_blocks()
  ext4: remove handle parameters from zero partial block functions
  ext4: move ordered data handling out of ext4_block_do_zero_range()
  ext4: rename ext4_block_zero_page_range() to ext4_block_zero_range()
  ext4: factor out journalled block zeroing range
  ext4: rename and extend ext4_block_truncate_page()
  ...
parents 30999ad0 981fcc56
Loading
Loading
Loading
Loading
+6 −8
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@
#include <linux/seqlock.h>
#include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/sched/signal.h>
#include <linux/blockgroup_lock.h>
#include <linux/percpu_counter.h>
@@ -1082,9 +1081,6 @@ struct ext4_inode_info {

	spinlock_t i_raw_lock;	/* protects updates to the raw inode */

	/* Fast commit wait queue for this inode */
	wait_queue_head_t i_fc_wait;

	/*
	 * Protect concurrent accesses on i_fc_lblk_start, i_fc_lblk_len
	 * and inode's EXT4_FC_STATE_COMMITTING state bit.
@@ -2976,7 +2972,8 @@ void __ext4_fc_track_unlink(handle_t *handle, struct inode *inode,
void __ext4_fc_track_link(handle_t *handle, struct inode *inode,
	struct dentry *dentry);
void ext4_fc_track_unlink(handle_t *handle, struct dentry *dentry);
void ext4_fc_track_link(handle_t *handle, struct dentry *dentry);
void ext4_fc_track_link(handle_t *handle, struct inode *inode,
			struct dentry *dentry);
void __ext4_fc_track_create(handle_t *handle, struct inode *inode,
			    struct dentry *dentry);
void ext4_fc_track_create(handle_t *handle, struct dentry *dentry);
@@ -3101,8 +3098,9 @@ extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks);
extern int ext4_chunk_trans_extent(struct inode *inode, int nrblocks);
extern int ext4_meta_trans_blocks(struct inode *inode, int lblocks,
				  int pextents);
extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
			     loff_t lstart, loff_t lend);
extern int ext4_block_zero_eof(struct inode *inode, loff_t from, loff_t end);
extern int ext4_zero_partial_blocks(struct inode *inode, loff_t lstart,
				    loff_t length, bool *did_zero);
extern vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf);
extern qsize_t *ext4_get_reserved_space(struct inode *inode);
extern int ext4_get_projid(struct inode *inode, kprojid_t *projid);
@@ -3721,7 +3719,7 @@ extern int ext4_handle_dirty_dirblock(handle_t *handle, struct inode *inode,
extern int __ext4_unlink(struct inode *dir, const struct qstr *d_name,
			 struct inode *inode, struct dentry *dentry);
extern int __ext4_link(struct inode *dir, struct inode *inode,
		       struct dentry *dentry);
		       const struct qstr *d_name, struct dentry *dentry);

#define S_SHIFT 12
static const unsigned char ext4_type_by_mode[(S_IFMT >> S_SHIFT) + 1] = {
+43 −17
Original line number Diff line number Diff line
@@ -142,10 +142,14 @@ static struct file_system_type ext_fs_type = {

static void extents_kunit_exit(struct kunit *test)
{
	struct super_block *sb = k_ctx.k_ei->vfs_inode.i_sb;
	struct ext4_sb_info *sbi = sb->s_fs_info;
	struct ext4_sb_info *sbi;

	if (!k_ctx.k_ei)
		return;

	sbi = k_ctx.k_ei->vfs_inode.i_sb->s_fs_info;
	ext4_es_unregister_shrinker(sbi);
	deactivate_super(sbi->s_sb);
	kfree(sbi);
	kfree(k_ctx.k_ei);
	kfree(k_ctx.k_data);
@@ -224,34 +228,38 @@ static int extents_kunit_init(struct kunit *test)
		(struct kunit_ext_test_param *)(test->param_value);
	int err;

	sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL);
	if (IS_ERR(sb))
		return PTR_ERR(sb);

	sb->s_blocksize = 4096;
	sb->s_blocksize_bits = 12;

	sbi = kzalloc_obj(struct ext4_sb_info);
	if (sbi == NULL)
		return -ENOMEM;

	sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL);
	if (IS_ERR(sb)) {
		kfree(sbi);
		return PTR_ERR(sb);
	}

	sbi->s_sb = sb;
	sb->s_fs_info = sbi;

	sb->s_blocksize = 4096;
	sb->s_blocksize_bits = 12;

	if (!param || !param->disable_zeroout)
		sbi->s_extent_max_zeroout_kb = 32;

	err = ext4_es_register_shrinker(sbi);
	if (err)
		goto out_deactivate;

	/* setup the mock inode */
	k_ctx.k_ei = kzalloc_obj(struct ext4_inode_info);
	if (k_ctx.k_ei == NULL)
		return -ENOMEM;
	if (k_ctx.k_ei == NULL) {
		err = -ENOMEM;
		goto out;
	}
	ei = k_ctx.k_ei;
	inode = &ei->vfs_inode;

	err = ext4_es_register_shrinker(sbi);
	if (err)
		return err;

	ext4_es_init_tree(&ei->i_es_tree);
	rwlock_init(&ei->i_es_lock);
	INIT_LIST_HEAD(&ei->i_es_list);
@@ -266,8 +274,10 @@ static int extents_kunit_init(struct kunit *test)
	inode->i_sb = sb;

	k_ctx.k_data = kzalloc(EXT_DATA_LEN * 4096, GFP_KERNEL);
	if (k_ctx.k_data == NULL)
		return -ENOMEM;
	if (k_ctx.k_data == NULL) {
		err = -ENOMEM;
		goto out;
	}

	/*
	 * set the data area to a junk value
@@ -309,7 +319,23 @@ static int extents_kunit_init(struct kunit *test)
	kunit_activate_static_stub(test, ext4_ext_zeroout, ext4_ext_zeroout_stub);
	kunit_activate_static_stub(test, ext4_issue_zeroout,
				   ext4_issue_zeroout_stub);
	up_write(&sb->s_umount);

	return 0;

out:
	kfree(k_ctx.k_ei);
	k_ctx.k_ei = NULL;

	kfree(k_ctx.k_data);
	k_ctx.k_data = NULL;

	ext4_es_unregister_shrinker(sbi);
out_deactivate:
	deactivate_locked_super(sb);
	kfree(sbi);

	return err;
}

/*
+86 −77
Original line number Diff line number Diff line
@@ -4571,30 +4571,30 @@ int ext4_ext_truncate(handle_t *handle, struct inode *inode)
	return err;
}

static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
				  ext4_lblk_t len, loff_t new_size,
				  int flags)
static int ext4_alloc_file_blocks(struct file *file, loff_t offset, loff_t len,
				  loff_t new_size, int flags)
{
	struct inode *inode = file_inode(file);
	handle_t *handle;
	int ret = 0, ret2 = 0, ret3 = 0;
	int retries = 0;
	int depth = 0;
	ext4_lblk_t len_lblk;
	struct ext4_map_blocks map;
	unsigned int credits;
	loff_t epos, old_size = i_size_read(inode);
	loff_t epos = 0, old_size = i_size_read(inode);
	unsigned int blkbits = inode->i_blkbits;
	bool alloc_zero = false;

	BUG_ON(!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS));
	map.m_lblk = offset;
	map.m_len = len;
	map.m_lblk = offset >> blkbits;
	map.m_len = len_lblk = EXT4_MAX_BLOCKS(len, offset, blkbits);
	/*
	 * Don't normalize the request if it can fit in one extent so
	 * that it doesn't get unnecessarily split into multiple
	 * extents.
	 */
	if (len <= EXT_UNWRITTEN_MAX_LEN)
	if (len_lblk <= EXT_UNWRITTEN_MAX_LEN)
		flags |= EXT4_GET_BLOCKS_NO_NORMALIZE;

	/*
@@ -4611,16 +4611,23 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
	/*
	 * credits to insert 1 extent into extent tree
	 */
	credits = ext4_chunk_trans_blocks(inode, len);
	credits = ext4_chunk_trans_blocks(inode, len_lblk);
	depth = ext_depth(inode);

	/* Zero to the end of the block containing i_size */
	if (new_size > old_size) {
		ret = ext4_block_zero_eof(inode, old_size, LLONG_MAX);
		if (ret)
			return ret;
	}

retry:
	while (len) {
	while (len_lblk) {
		/*
		 * Recalculate credits when extent tree depth changes.
		 */
		if (depth != ext_depth(inode)) {
			credits = ext4_chunk_trans_blocks(inode, len);
			credits = ext4_chunk_trans_blocks(inode, len_lblk);
			depth = ext_depth(inode);
		}

@@ -4640,50 +4647,60 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
			ext4_journal_stop(handle);
			break;
		}
		ext4_update_inode_fsync_trans(handle, inode, 1);
		ret = ext4_journal_stop(handle);
		if (unlikely(ret))
			break;

		/*
		 * allow a full retry cycle for any remaining allocations
		 */
		retries = 0;
		epos = EXT4_LBLK_TO_B(inode, map.m_lblk + ret);
		inode_set_ctime_current(inode);
		if (new_size) {
			if (epos > new_size)
				epos = new_size;
			if (ext4_update_inode_size(inode, epos) & 0x1)
				inode_set_mtime_to_ts(inode,
						      inode_get_ctime(inode));
			if (epos > old_size) {
				pagecache_isize_extended(inode, old_size, epos);
				ext4_zero_partial_blocks(handle, inode,
						     old_size, epos - old_size);
			}
		}
		ret2 = ext4_mark_inode_dirty(handle, inode);
		ext4_update_inode_fsync_trans(handle, inode, 1);
		ret3 = ext4_journal_stop(handle);
		ret2 = ret3 ? ret3 : ret2;
		if (unlikely(ret2))
			break;

		if (alloc_zero &&
		    (map.m_flags & (EXT4_MAP_MAPPED | EXT4_MAP_UNWRITTEN))) {
			ret2 = ext4_issue_zeroout(inode, map.m_lblk, map.m_pblk,
			ret = ext4_issue_zeroout(inode, map.m_lblk, map.m_pblk,
						 map.m_len);
			if (likely(!ret2))
				ret2 = ext4_convert_unwritten_extents(NULL,
			if (likely(!ret))
				ret = ext4_convert_unwritten_extents(NULL,
					inode, (loff_t)map.m_lblk << blkbits,
					(loff_t)map.m_len << blkbits);
			if (ret2)
			if (ret)
				break;
		}

		map.m_lblk += ret;
		map.m_len = len = len - ret;
		map.m_lblk += map.m_len;
		map.m_len = len_lblk = len_lblk - map.m_len;
		epos = EXT4_LBLK_TO_B(inode, map.m_lblk);
	}

	if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
		goto retry;

	return ret > 0 ? ret2 : ret;
	if (!epos || !new_size)
		return ret;

	/*
	 * Allocate blocks, update the file size to match the size of the
	 * already successfully allocated blocks.
	 */
	if (epos > new_size)
		epos = new_size;

	handle = ext4_journal_start(inode, EXT4_HT_MISC, 1);
	if (IS_ERR(handle))
		return ret ? ret : PTR_ERR(handle);

	ext4_update_inode_size(inode, epos);
	ret2 = ext4_mark_inode_dirty(handle, inode);
	ext4_update_inode_fsync_trans(handle, inode, 1);
	ret3 = ext4_journal_stop(handle);
	ret2 = ret3 ? ret3 : ret2;

	if (epos > old_size)
		pagecache_isize_extended(inode, old_size, epos);

	return ret ? ret : ret2;
}

static int ext4_collapse_range(struct file *file, loff_t offset, loff_t len);
@@ -4695,12 +4712,11 @@ static long ext4_zero_range(struct file *file, loff_t offset,
{
	struct inode *inode = file_inode(file);
	handle_t *handle = NULL;
	loff_t new_size = 0;
	loff_t align_start, align_end, new_size = 0;
	loff_t end = offset + len;
	ext4_lblk_t start_lblk, end_lblk;
	unsigned int blocksize = i_blocksize(inode);
	unsigned int blkbits = inode->i_blkbits;
	int ret, flags, credits;
	bool partial_zeroed = false;
	int ret, flags;

	trace_ext4_zero_range(inode, offset, len, mode);
	WARN_ON_ONCE(!inode_is_locked(inode));
@@ -4720,11 +4736,8 @@ static long ext4_zero_range(struct file *file, loff_t offset,
	flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT;
	/* Preallocate the range including the unaligned edges */
	if (!IS_ALIGNED(offset | end, blocksize)) {
		ext4_lblk_t alloc_lblk = offset >> blkbits;
		ext4_lblk_t len_lblk = EXT4_MAX_BLOCKS(len, offset, blkbits);

		ret = ext4_alloc_file_blocks(file, alloc_lblk, len_lblk,
					     new_size, flags);
		ret = ext4_alloc_file_blocks(file, offset, len, new_size,
					     flags);
		if (ret)
			return ret;
	}
@@ -4739,18 +4752,17 @@ static long ext4_zero_range(struct file *file, loff_t offset,
		return ret;

	/* Zero range excluding the unaligned edges */
	start_lblk = EXT4_B_TO_LBLK(inode, offset);
	end_lblk = end >> blkbits;
	if (end_lblk > start_lblk) {
		ext4_lblk_t zero_blks = end_lblk - start_lblk;

	align_start = round_up(offset, blocksize);
	align_end = round_down(end, blocksize);
	if (align_end > align_start) {
		if (mode & FALLOC_FL_WRITE_ZEROES)
			flags = EXT4_GET_BLOCKS_CREATE_ZERO | EXT4_EX_NOCACHE;
		else
			flags |= (EXT4_GET_BLOCKS_CONVERT_UNWRITTEN |
				  EXT4_EX_NOCACHE);
		ret = ext4_alloc_file_blocks(file, start_lblk, zero_blks,
					     new_size, flags);
		ret = ext4_alloc_file_blocks(file, align_start,
					     align_end - align_start, new_size,
					     flags);
		if (ret)
			return ret;
	}
@@ -4758,25 +4770,24 @@ static long ext4_zero_range(struct file *file, loff_t offset,
	if (IS_ALIGNED(offset | end, blocksize))
		return ret;

	/*
	 * In worst case we have to writeout two nonadjacent unwritten
	 * blocks and update the inode
	 */
	credits = (2 * ext4_ext_index_trans_blocks(inode, 2)) + 1;
	if (ext4_should_journal_data(inode))
		credits += 2;
	handle = ext4_journal_start(inode, EXT4_HT_MISC, credits);
	/* Zero out partial block at the edges of the range */
	ret = ext4_zero_partial_blocks(inode, offset, len, &partial_zeroed);
	if (ret)
		return ret;
	if (((file->f_flags & O_SYNC) || IS_SYNC(inode)) && partial_zeroed) {
		ret = filemap_write_and_wait_range(inode->i_mapping, offset,
						   end - 1);
		if (ret)
			return ret;
	}

	handle = ext4_journal_start(inode, EXT4_HT_MISC, 1);
	if (IS_ERR(handle)) {
		ret = PTR_ERR(handle);
		ext4_std_error(inode->i_sb, ret);
		return ret;
	}

	/* Zero out partial block at the edges of the range */
	ret = ext4_zero_partial_blocks(handle, inode, offset, len);
	if (ret)
		goto out_handle;

	if (new_size)
		ext4_update_inode_size(inode, new_size);
	ret = ext4_mark_inode_dirty(handle, inode);
@@ -4784,7 +4795,7 @@ static long ext4_zero_range(struct file *file, loff_t offset,
		goto out_handle;

	ext4_update_inode_fsync_trans(handle, inode, 1);
	if (file->f_flags & O_SYNC)
	if ((file->f_flags & O_SYNC) || IS_SYNC(inode))
		ext4_handle_sync(handle);

out_handle:
@@ -4798,15 +4809,11 @@ static long ext4_do_fallocate(struct file *file, loff_t offset,
	struct inode *inode = file_inode(file);
	loff_t end = offset + len;
	loff_t new_size = 0;
	ext4_lblk_t start_lblk, len_lblk;
	int ret;

	trace_ext4_fallocate_enter(inode, offset, len, mode);
	WARN_ON_ONCE(!inode_is_locked(inode));

	start_lblk = offset >> inode->i_blkbits;
	len_lblk = EXT4_MAX_BLOCKS(len, offset, inode->i_blkbits);

	/* We only support preallocation for extent-based files only. */
	if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
		ret = -EOPNOTSUPP;
@@ -4821,17 +4828,19 @@ static long ext4_do_fallocate(struct file *file, loff_t offset,
			goto out;
	}

	ret = ext4_alloc_file_blocks(file, start_lblk, len_lblk, new_size,
	ret = ext4_alloc_file_blocks(file, offset, len, new_size,
				     EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT);
	if (ret)
		goto out;

	if (file->f_flags & O_SYNC && EXT4_SB(inode->i_sb)->s_journal) {
	if (((file->f_flags & O_SYNC) || IS_SYNC(inode)) &&
	    EXT4_SB(inode->i_sb)->s_journal) {
		ret = ext4_fc_commit(EXT4_SB(inode->i_sb)->s_journal,
					EXT4_I(inode)->i_sync_tid);
	}
out:
	trace_ext4_fallocate_exit(inode, offset, len_lblk, ret);
	trace_ext4_fallocate_exit(inode, offset,
			EXT4_MAX_BLOCKS(len, offset, inode->i_blkbits), ret);
	return ret;
}

@@ -5598,7 +5607,7 @@ static int ext4_collapse_range(struct file *file, loff_t offset, loff_t len)
		goto out_handle;

	ext4_update_inode_fsync_trans(handle, inode, 1);
	if (IS_SYNC(inode))
	if ((file->f_flags & O_SYNC) || IS_SYNC(inode))
		ext4_handle_sync(handle);

out_handle:
@@ -5722,7 +5731,7 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len)
		goto out_handle;

	ext4_update_inode_fsync_trans(handle, inode, 1);
	if (IS_SYNC(inode))
	if ((file->f_flags & O_SYNC) || IS_SYNC(inode))
		ext4_handle_sync(handle);

out_handle:
+24 −65
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include "mballoc.h"

#include <linux/lockdep.h>
#include <linux/wait_bit.h>
/*
 * Ext4 Fast Commits
 * -----------------
@@ -215,7 +216,6 @@ void ext4_fc_init_inode(struct inode *inode)
	ext4_clear_inode_state(inode, EXT4_STATE_FC_COMMITTING);
	INIT_LIST_HEAD(&ei->i_fc_list);
	INIT_LIST_HEAD(&ei->i_fc_dilist);
	init_waitqueue_head(&ei->i_fc_wait);
}

static bool ext4_fc_disabled(struct super_block *sb)
@@ -224,6 +224,12 @@ static bool ext4_fc_disabled(struct super_block *sb)
		(EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY));
}

static bool ext4_fc_eligible(struct super_block *sb)
{
	return !ext4_fc_disabled(sb) &&
		!(ext4_test_mount_flag(sb, EXT4_MF_FC_INELIGIBLE));
}

/*
 * Remove inode from fast commit list. If the inode is being committed
 * we wait until inode commit is done.
@@ -320,7 +326,7 @@ void ext4_fc_mark_ineligible(struct super_block *sb, int reason, handle_t *handl
	if (ext4_fc_disabled(sb))
		return;

	if (handle && !IS_ERR(handle))
	if (!IS_ERR_OR_NULL(handle))
		tid = handle->h_transaction->t_tid;
	else {
		read_lock(&sbi->s_journal->j_state_lock);
@@ -473,12 +479,7 @@ void ext4_fc_track_unlink(handle_t *handle, struct dentry *dentry)
{
	struct inode *inode = d_inode(dentry);

	if (ext4_fc_disabled(inode->i_sb))
		return;

	if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE))
		return;

	if (ext4_fc_eligible(inode->i_sb))
		__ext4_fc_track_unlink(handle, inode, dentry);
}

@@ -496,16 +497,10 @@ void __ext4_fc_track_link(handle_t *handle,
	trace_ext4_fc_track_link(handle, inode, dentry, ret);
}

void ext4_fc_track_link(handle_t *handle, struct dentry *dentry)
void ext4_fc_track_link(handle_t *handle, struct inode *inode,
			struct dentry *dentry)
{
	struct inode *inode = d_inode(dentry);

	if (ext4_fc_disabled(inode->i_sb))
		return;

	if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE))
		return;

	if (ext4_fc_eligible(inode->i_sb))
		__ext4_fc_track_link(handle, inode, dentry);
}

@@ -527,12 +522,7 @@ void ext4_fc_track_create(handle_t *handle, struct dentry *dentry)
{
	struct inode *inode = d_inode(dentry);

	if (ext4_fc_disabled(inode->i_sb))
		return;

	if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE))
		return;

	if (ext4_fc_eligible(inode->i_sb))
		__ext4_fc_track_create(handle, inode, dentry);
}

@@ -557,16 +547,13 @@ void ext4_fc_track_inode(handle_t *handle, struct inode *inode)
	if (S_ISDIR(inode->i_mode))
		return;

	if (ext4_fc_disabled(inode->i_sb))
		return;

	if (ext4_should_journal_data(inode)) {
		ext4_fc_mark_ineligible(inode->i_sb,
					EXT4_FC_REASON_INODE_JOURNAL_DATA, handle);
		return;
	}

	if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE))
	if (!ext4_fc_eligible(inode->i_sb))
		return;

	/*
@@ -644,10 +631,7 @@ void ext4_fc_track_range(handle_t *handle, struct inode *inode, ext4_lblk_t star
	if (S_ISDIR(inode->i_mode))
		return;

	if (ext4_fc_disabled(inode->i_sb))
		return;

	if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE))
	if (!ext4_fc_eligible(inode->i_sb))
		return;

	if (ext4_has_inline_data(inode)) {
@@ -1446,7 +1430,6 @@ static int ext4_fc_replay_link_internal(struct super_block *sb,
				struct inode *inode)
{
	struct inode *dir = NULL;
	struct dentry *dentry_dir = NULL, *dentry_inode = NULL;
	struct qstr qstr_dname = QSTR_INIT(darg->dname, darg->dname_len);
	int ret = 0;

@@ -1457,21 +1440,7 @@ static int ext4_fc_replay_link_internal(struct super_block *sb,
		goto out;
	}

	dentry_dir = d_obtain_alias(dir);
	if (IS_ERR(dentry_dir)) {
		ext4_debug("Failed to obtain dentry");
		dentry_dir = NULL;
		goto out;
	}

	dentry_inode = d_alloc(dentry_dir, &qstr_dname);
	if (!dentry_inode) {
		ext4_debug("Inode dentry not created.");
		ret = -ENOMEM;
		goto out;
	}

	ret = __ext4_link(dir, inode, dentry_inode);
	ret = __ext4_link(dir, inode, &qstr_dname, NULL);
	/*
	 * It's possible that link already existed since data blocks
	 * for the dir in question got persisted before we crashed OR
@@ -1485,16 +1454,8 @@ static int ext4_fc_replay_link_internal(struct super_block *sb,

	ret = 0;
out:
	if (dentry_dir) {
		d_drop(dentry_dir);
		dput(dentry_dir);
	} else if (dir) {
	if (dir)
		iput(dir);
	}
	if (dentry_inode) {
		d_drop(dentry_inode);
		dput(dentry_inode);
	}

	return ret;
}
@@ -1759,8 +1720,7 @@ int ext4_fc_record_regions(struct super_block *sb, int ino,
}

/* Replay add range tag */
static int ext4_fc_replay_add_range(struct super_block *sb,
				    struct ext4_fc_tl_mem *tl, u8 *val)
static int ext4_fc_replay_add_range(struct super_block *sb, u8 *val)
{
	struct ext4_fc_add_range fc_add_ex;
	struct ext4_extent newex, *ex;
@@ -1880,8 +1840,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb,

/* Replay DEL_RANGE tag */
static int
ext4_fc_replay_del_range(struct super_block *sb,
			 struct ext4_fc_tl_mem *tl, u8 *val)
ext4_fc_replay_del_range(struct super_block *sb, u8 *val)
{
	struct inode *inode;
	struct ext4_fc_del_range lrange;
@@ -2251,13 +2210,13 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,
			ret = ext4_fc_replay_unlink(sb, &tl, val);
			break;
		case EXT4_FC_TAG_ADD_RANGE:
			ret = ext4_fc_replay_add_range(sb, &tl, val);
			ret = ext4_fc_replay_add_range(sb, val);
			break;
		case EXT4_FC_TAG_CREAT:
			ret = ext4_fc_replay_create(sb, &tl, val);
			break;
		case EXT4_FC_TAG_DEL_RANGE:
			ret = ext4_fc_replay_del_range(sb, &tl, val);
			ret = ext4_fc_replay_del_range(sb, val);
			break;
		case EXT4_FC_TAG_INODE:
			ret = ext4_fc_replay_inode(sb, &tl, val);
+17 −0
Original line number Diff line number Diff line
@@ -270,6 +270,8 @@ static ssize_t ext4_generic_write_checks(struct kiocb *iocb,

static ssize_t ext4_write_checks(struct kiocb *iocb, struct iov_iter *from)
{
	struct inode *inode = file_inode(iocb->ki_filp);
	loff_t old_size = i_size_read(inode);
	ssize_t ret, count;

	count = ext4_generic_write_checks(iocb, from);
@@ -279,6 +281,21 @@ static ssize_t ext4_write_checks(struct kiocb *iocb, struct iov_iter *from)
	ret = file_modified(iocb->ki_filp);
	if (ret)
		return ret;

	/*
	 * If the position is beyond the EOF, it is necessary to zero out the
	 * partial block that beyond the existing EOF, as it may contains
	 * stale data written through mmap.
	 */
	if (iocb->ki_pos > old_size && !ext4_verity_in_progress(inode)) {
		if (iocb->ki_flags & IOCB_NOWAIT)
			return -EAGAIN;

		ret = ext4_block_zero_eof(inode, old_size, iocb->ki_pos);
		if (ret)
			return ret;
	}

	return count;
}

Loading