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

Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Pull ext4 fixes from Ted Ts'o:
 "Miscellaneous ext4 bug fixes for v5.12"

* tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: initialize ret to suppress smatch warning
  ext4: stop inode update before return
  ext4: fix rename whiteout with fast commit
  ext4: fix timer use-after-free on failed mount
  ext4: fix potential error in ext4_do_update_inode
  ext4: do not try to set xattr into ea_inode if value is empty
  ext4: do not iput inode under running transaction in ext4_rename()
  ext4: find old entry again if failed to rename whiteout
  ext4: fix error handling in ext4_end_enable_verity()
  ext4: fix bh ref count on error paths
  fs/ext4: fix integer overflow in s_log_groups_per_flex
  ext4: add reclaim checks to xattr code
  ext4: shrink race window in ext4_should_retry_alloc()
parents 2c41fab1 64395d95
Loading
Loading
Loading
Loading
+26 −12
Original line number Diff line number Diff line
@@ -627,26 +627,40 @@ int ext4_claim_free_clusters(struct ext4_sb_info *sbi,
/**
 * ext4_should_retry_alloc() - check if a block allocation should be retried
 * @sb:			superblock
 * @retries:		number of attemps has been made
 * @retries:		number of retry attempts made so far
 *
 * ext4_should_retry_alloc() is called when ENOSPC is returned, and if
 * it is profitable to retry the operation, this function will wait
 * for the current or committing transaction to complete, and then
 * return TRUE.  We will only retry once.
 * ext4_should_retry_alloc() is called when ENOSPC is returned while
 * attempting to allocate blocks.  If there's an indication that a pending
 * journal transaction might free some space and allow another attempt to
 * succeed, this function will wait for the current or committing transaction
 * to complete and then return TRUE.
 */
int ext4_should_retry_alloc(struct super_block *sb, int *retries)
{
	if (!ext4_has_free_clusters(EXT4_SB(sb), 1, 0) ||
	    (*retries)++ > 1 ||
	    !EXT4_SB(sb)->s_journal)
	struct ext4_sb_info *sbi = EXT4_SB(sb);

	if (!sbi->s_journal)
		return 0;

	smp_mb();
	if (EXT4_SB(sb)->s_mb_free_pending == 0)
	if (++(*retries) > 3) {
		percpu_counter_inc(&sbi->s_sra_exceeded_retry_limit);
		return 0;
	}

	/*
	 * if there's no indication that blocks are about to be freed it's
	 * possible we just missed a transaction commit that did so
	 */
	smp_mb();
	if (sbi->s_mb_free_pending == 0)
		return ext4_has_free_clusters(sbi, 1, 0);

	/*
	 * it's possible we've just missed a transaction commit here,
	 * so ignore the returned status
	 */
	jbd_debug(1, "%s: retrying operation after ENOSPC\n", sb->s_id);
	jbd2_journal_force_commit_nested(EXT4_SB(sb)->s_journal);
	(void) jbd2_journal_force_commit_nested(sbi->s_journal);
	return 1;
}

+3 −0
Original line number Diff line number Diff line
@@ -1484,6 +1484,7 @@ struct ext4_sb_info {
	struct percpu_counter s_freeinodes_counter;
	struct percpu_counter s_dirs_counter;
	struct percpu_counter s_dirtyclusters_counter;
	struct percpu_counter s_sra_exceeded_retry_limit;
	struct blockgroup_lock *s_blockgroup_lock;
	struct proc_dir_entry *s_proc;
	struct kobject s_kobj;
@@ -2793,6 +2794,8 @@ 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_create(handle_t *handle, struct inode *inode,
			    struct dentry *dentry);
void ext4_fc_track_create(handle_t *handle, struct dentry *dentry);
void ext4_fc_track_inode(handle_t *handle, struct inode *inode);
void ext4_fc_mark_ineligible(struct super_block *sb, int reason);
+1 −1
Original line number Diff line number Diff line
@@ -4382,7 +4382,7 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
{
	struct inode *inode = file_inode(file);
	handle_t *handle;
	int ret, ret2 = 0, ret3 = 0;
	int ret = 0, ret2 = 0, ret3 = 0;
	int retries = 0;
	int depth = 0;
	struct ext4_map_blocks map;
+7 −2
Original line number Diff line number Diff line
@@ -513,10 +513,10 @@ void ext4_fc_track_link(handle_t *handle, struct dentry *dentry)
	__ext4_fc_track_link(handle, d_inode(dentry), dentry);
}

void ext4_fc_track_create(handle_t *handle, struct dentry *dentry)
void __ext4_fc_track_create(handle_t *handle, struct inode *inode,
			  struct dentry *dentry)
{
	struct __track_dentry_update_args args;
	struct inode *inode = d_inode(dentry);
	int ret;

	args.dentry = dentry;
@@ -527,6 +527,11 @@ void ext4_fc_track_create(handle_t *handle, struct dentry *dentry)
	trace_ext4_fc_track_create(inode, dentry, ret);
}

void ext4_fc_track_create(handle_t *handle, struct dentry *dentry)
{
	__ext4_fc_track_create(handle, d_inode(dentry), dentry);
}

/* __track_fn for inode tracking */
static int __track_inode(struct inode *inode, void *arg, bool update)
{
+10 −8
Original line number Diff line number Diff line
@@ -1938,13 +1938,13 @@ static int __ext4_journalled_writepage(struct page *page,
	if (!ret)
		ret = err;

	if (!ext4_has_inline_data(inode))
		ext4_walk_page_buffers(NULL, page_bufs, 0, len,
				       NULL, bput_one);
	ext4_set_inode_state(inode, EXT4_STATE_JDATA);
out:
	unlock_page(page);
out_no_pagelock:
	if (!inline_data && page_bufs)
		ext4_walk_page_buffers(NULL, page_bufs, 0, len,
				       NULL, bput_one);
	brelse(inode_bh);
	return ret;
}
@@ -5026,7 +5026,7 @@ static int ext4_do_update_inode(handle_t *handle,
	struct ext4_inode_info *ei = EXT4_I(inode);
	struct buffer_head *bh = iloc->bh;
	struct super_block *sb = inode->i_sb;
	int err = 0, rc, block;
	int err = 0, block;
	int need_datasync = 0, set_large_file = 0;
	uid_t i_uid;
	gid_t i_gid;
@@ -5138,9 +5138,9 @@ static int ext4_do_update_inode(handle_t *handle,
					      bh->b_data);

	BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
	rc = ext4_handle_dirty_metadata(handle, NULL, bh);
	if (!err)
		err = rc;
	err = ext4_handle_dirty_metadata(handle, NULL, bh);
	if (err)
		goto out_brelse;
	ext4_clear_inode_state(inode, EXT4_STATE_NEW);
	if (set_large_file) {
		BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get write access");
@@ -5387,9 +5387,11 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
			inode->i_gid = attr->ia_gid;
		error = ext4_mark_inode_dirty(handle, inode);
		ext4_journal_stop(handle);
		if (unlikely(error))
		if (unlikely(error)) {
			ext4_fc_stop_update(inode);
			return error;
		}
	}

	if (attr->ia_valid & ATTR_SIZE) {
		handle_t *handle;
Loading