Commit 9daee913 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ext4 updates from Ted Ts'o:
 "Add new ioctls to set and get the file system UUID in the ext4
  superblock and improved the performance of the online resizing of file
  systems with bigalloc enabled.

  Fixed a lot of bugs, in particular for the inline data feature,
  potential races when creating and deleting inodes with shared extended
  attribute blocks, and the handling of directory blocks which are
  corrupted"

* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (37 commits)
  ext4: add ioctls to get/set the ext4 superblock uuid
  ext4: avoid resizing to a partial cluster size
  ext4: reduce computation of overhead during resize
  jbd2: fix assertion 'jh->b_frozen_data == NULL' failure when journal aborted
  ext4: block range must be validated before use in ext4_mb_clear_bb()
  mbcache: automatically delete entries from cache on freeing
  mbcache: Remove mb_cache_entry_delete()
  ext2: avoid deleting xattr block that is being reused
  ext2: unindent codeblock in ext2_xattr_set()
  ext2: factor our freeing of xattr block reference
  ext4: fix race when reusing xattr blocks
  ext4: unindent codeblock in ext4_xattr_block_set()
  ext4: remove EA inode entry from mbcache on inode eviction
  mbcache: add functions to delete entry if unused
  mbcache: don't reclaim used entries
  ext4: make sure ext4_append() always allocates new block
  ext4: check if directory block is within i_size
  ext4: reflect mb_optimize_scan value in options file
  ext4: avoid remove directory when directory is corrupted
  ext4: aligned '*' in comments
  ...
parents fa9db655 d95efb14
Loading
Loading
Loading
Loading
+78 −92
Original line number Diff line number Diff line
@@ -517,23 +517,24 @@ ext2_xattr_set(struct inode *inode, int name_index, const char *name,
	/* Here we know that we can set the new attribute. */

	if (header) {
		/* assert(header == HDR(bh)); */
		int offset;

		lock_buffer(bh);
		if (header->h_refcount == cpu_to_le32(1)) {
			__u32 hash = le32_to_cpu(header->h_hash);
			struct mb_cache_entry *oe;

			oe = mb_cache_entry_delete_or_get(EA_BLOCK_CACHE(inode),
					hash, bh->b_blocknr);
			if (!oe) {
				ea_bdebug(bh, "modifying in-place");
				goto update_block;
			}
			/*
			 * This must happen under buffer lock for
			 * ext2_xattr_set2() to reliably detect modified block
			 * Someone is trying to reuse the block, leave it alone
			 */
			mb_cache_entry_delete(EA_BLOCK_CACHE(inode), hash,
					      bh->b_blocknr);

			/* keep the buffer locked while modifying it. */
		} else {
			int offset;

			mb_cache_entry_put(EA_BLOCK_CACHE(inode), oe);
		}
		unlock_buffer(bh);
		ea_bdebug(bh, "cloning");
		header = kmemdup(HDR(bh), bh->b_size, GFP_KERNEL);
@@ -546,7 +547,6 @@ ext2_xattr_set(struct inode *inode, int name_index, const char *name,
		here = ENTRY((char *)header + offset);
		offset = (char *)last - bh->b_data;
		last = ENTRY((char *)header + offset);
		}
	} else {
		/* Allocate a buffer where we construct the new block. */
		header = kzalloc(sb->s_blocksize, GFP_KERNEL);
@@ -559,6 +559,7 @@ ext2_xattr_set(struct inode *inode, int name_index, const char *name,
		last = here = ENTRY(header+1);
	}

update_block:
	/* Iff we are modifying the block in-place, bh is locked here. */

	if (not_found) {
@@ -651,6 +652,55 @@ ext2_xattr_set(struct inode *inode, int name_index, const char *name,
	return error;
}

static void ext2_xattr_release_block(struct inode *inode,
				     struct buffer_head *bh)
{
	struct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode);

retry_ref:
	lock_buffer(bh);
	if (HDR(bh)->h_refcount == cpu_to_le32(1)) {
		__u32 hash = le32_to_cpu(HDR(bh)->h_hash);
		struct mb_cache_entry *oe;

		/*
		 * This must happen under buffer lock to properly
		 * serialize with ext2_xattr_set() reusing the block.
		 */
		oe = mb_cache_entry_delete_or_get(ea_block_cache, hash,
						  bh->b_blocknr);
		if (oe) {
			/*
			 * Someone is trying to reuse the block. Wait
			 * and retry.
			 */
			unlock_buffer(bh);
			mb_cache_entry_wait_unused(oe);
			mb_cache_entry_put(ea_block_cache, oe);
			goto retry_ref;
		}

		/* Free the old block. */
		ea_bdebug(bh, "freeing");
		ext2_free_blocks(inode, bh->b_blocknr, 1);
		/* We let our caller release bh, so we
		 * need to duplicate the buffer before. */
		get_bh(bh);
		bforget(bh);
		unlock_buffer(bh);
	} else {
		/* Decrement the refcount only. */
		le32_add_cpu(&HDR(bh)->h_refcount, -1);
		dquot_free_block(inode, 1);
		mark_buffer_dirty(bh);
		unlock_buffer(bh);
		ea_bdebug(bh, "refcount now=%d",
			le32_to_cpu(HDR(bh)->h_refcount));
		if (IS_SYNC(inode))
			sync_dirty_buffer(bh);
	}
}

/*
 * Second half of ext2_xattr_set(): Update the file system.
 */
@@ -747,34 +797,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
		 * If there was an old block and we are no longer using it,
		 * release the old block.
		 */
		lock_buffer(old_bh);
		if (HDR(old_bh)->h_refcount == cpu_to_le32(1)) {
			__u32 hash = le32_to_cpu(HDR(old_bh)->h_hash);

			/*
			 * This must happen under buffer lock for
			 * ext2_xattr_set2() to reliably detect freed block
			 */
			mb_cache_entry_delete(ea_block_cache, hash,
					      old_bh->b_blocknr);
			/* Free the old block. */
			ea_bdebug(old_bh, "freeing");
			ext2_free_blocks(inode, old_bh->b_blocknr, 1);
			mark_inode_dirty(inode);
			/* We let our caller release old_bh, so we
			 * need to duplicate the buffer before. */
			get_bh(old_bh);
			bforget(old_bh);
		} else {
			/* Decrement the refcount only. */
			le32_add_cpu(&HDR(old_bh)->h_refcount, -1);
			dquot_free_block_nodirty(inode, 1);
			mark_inode_dirty(inode);
			mark_buffer_dirty(old_bh);
			ea_bdebug(old_bh, "refcount now=%d",
				le32_to_cpu(HDR(old_bh)->h_refcount));
		}
		unlock_buffer(old_bh);
		ext2_xattr_release_block(inode, old_bh);
	}

cleanup:
@@ -828,30 +851,7 @@ ext2_xattr_delete_inode(struct inode *inode)
			EXT2_I(inode)->i_file_acl);
		goto cleanup;
	}
	lock_buffer(bh);
	if (HDR(bh)->h_refcount == cpu_to_le32(1)) {
		__u32 hash = le32_to_cpu(HDR(bh)->h_hash);

		/*
		 * This must happen under buffer lock for ext2_xattr_set2() to
		 * reliably detect freed block
		 */
		mb_cache_entry_delete(EA_BLOCK_CACHE(inode), hash,
				      bh->b_blocknr);
		ext2_free_blocks(inode, EXT2_I(inode)->i_file_acl, 1);
		get_bh(bh);
		bforget(bh);
		unlock_buffer(bh);
	} else {
		le32_add_cpu(&HDR(bh)->h_refcount, -1);
		ea_bdebug(bh, "refcount now=%d",
			le32_to_cpu(HDR(bh)->h_refcount));
		unlock_buffer(bh);
		mark_buffer_dirty(bh);
		if (IS_SYNC(inode))
			sync_dirty_buffer(bh);
		dquot_free_block_nodirty(inode, 1);
	}
	ext2_xattr_release_block(inode, bh);
	EXT2_I(inode)->i_file_acl = 0;

cleanup:
@@ -943,7 +943,7 @@ ext2_xattr_cache_find(struct inode *inode, struct ext2_xattr_header *header)
	if (!header->h_hash)
		return NULL;  /* never share */
	ea_idebug(inode, "looking for cached blocks [%x]", (int)hash);
again:

	ce = mb_cache_entry_find_first(ea_block_cache, hash);
	while (ce) {
		struct buffer_head *bh;
@@ -955,21 +955,7 @@ ext2_xattr_cache_find(struct inode *inode, struct ext2_xattr_header *header)
				inode->i_ino, (unsigned long) ce->e_value);
		} else {
			lock_buffer(bh);
			/*
			 * We have to be careful about races with freeing or
			 * rehashing of xattr block. Once we hold buffer lock
			 * xattr block's state is stable so we can check
			 * whether the block got freed / rehashed or not.
			 * Since we unhash mbcache entry under buffer lock when
			 * freeing / rehashing xattr block, checking whether
			 * entry is still hashed is reliable.
			 */
			if (hlist_bl_unhashed(&ce->e_hash_list)) {
				mb_cache_entry_put(ea_block_cache, ce);
				unlock_buffer(bh);
				brelse(bh);
				goto again;
			} else if (le32_to_cpu(HDR(bh)->h_refcount) >
			if (le32_to_cpu(HDR(bh)->h_refcount) >
			    EXT2_XATTR_REFCOUNT_MAX) {
				ea_idebug(inode, "block %ld refcount %d>%d",
					  (unsigned long) ce->e_value,
+1 −1
Original line number Diff line number Diff line
@@ -666,7 +666,7 @@ int ext4_should_retry_alloc(struct super_block *sb, int *retries)
	 * 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);
	ext4_debug("%s: retrying operation after ENOSPC\n", sb->s_id);
	(void) jbd2_journal_force_commit_nested(sbi->s_journal);
	return 1;
}
+14 −2
Original line number Diff line number Diff line
@@ -724,6 +724,8 @@ enum {
#define EXT4_IOC_GETSTATE		_IOW('f', 41, __u32)
#define EXT4_IOC_GET_ES_CACHE		_IOWR('f', 42, struct fiemap)
#define EXT4_IOC_CHECKPOINT		_IOW('f', 43, __u32)
#define EXT4_IOC_GETFSUUID		_IOR('f', 44, struct fsuuid)
#define EXT4_IOC_SETFSUUID		_IOW('f', 44, struct fsuuid)

#define EXT4_IOC_SHUTDOWN _IOR ('X', 125, __u32)

@@ -753,6 +755,15 @@ enum {
						EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT | \
						EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN)

/*
 * Structure for EXT4_IOC_GETFSUUID/EXT4_IOC_SETFSUUID
 */
struct fsuuid {
	__u32       fsu_len;
	__u32       fsu_flags;
	__u8        fsu_uuid[];
};

#if defined(__KERNEL__) && defined(CONFIG_COMPAT)
/*
 * ioctl commands in 32 bit emulation
@@ -3016,7 +3027,7 @@ int ext4_fileattr_set(struct user_namespace *mnt_userns,
		      struct dentry *dentry, struct fileattr *fa);
int ext4_fileattr_get(struct dentry *dentry, struct fileattr *fa);
extern void ext4_reset_inode_seed(struct inode *inode);
int ext4_update_overhead(struct super_block *sb);
int ext4_update_overhead(struct super_block *sb, bool force);

/* migrate.c */
extern int ext4_ext_migrate(struct inode *);
@@ -3583,6 +3594,7 @@ extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode,
extern int ext4_inline_data_fiemap(struct inode *inode,
				   struct fiemap_extent_info *fieinfo,
				   int *has_inline, __u64 start, __u64 len);
extern void *ext4_read_inline_link(struct inode *inode);

struct iomap;
extern int ext4_inline_data_iomap(struct inode *inode, struct iomap *iomap);
@@ -3799,7 +3811,7 @@ static inline void set_bitmap_uptodate(struct buffer_head *bh)
extern wait_queue_head_t ext4__ioend_wq[EXT4_WQ_HASH_SZ];

extern int ext4_resize_begin(struct super_block *sb);
extern void ext4_resize_end(struct super_block *sb);
extern int ext4_resize_end(struct super_block *sb, bool update_backups);

static inline void ext4_set_io_unwritten_flag(struct inode *inode,
					      struct ext4_io_end *io_end)
+1 −2
Original line number Diff line number Diff line
@@ -267,8 +267,7 @@ int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
	trace_ext4_forget(inode, is_metadata, blocknr);
	BUFFER_TRACE(bh, "enter");

	jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, "
		  "data mode %x\n",
	ext4_debug("forgetting bh %p: is_metadata=%d, mode %o, data mode %x\n",
		  bh, is_metadata, inode->i_mode,
		  test_opt(inode->i_sb, DATA_FLAGS));

+22 −22
Original line number Diff line number Diff line
@@ -917,8 +917,8 @@ static int ext4_fc_write_inode_data(struct inode *inode, u32 *crc)
	mutex_unlock(&ei->i_fc_lock);

	cur_lblk_off = old_blk_size;
	jbd_debug(1, "%s: will try writing %d to %d for inode %ld\n",
		  __func__, cur_lblk_off, new_blk_size, inode->i_ino);
	ext4_debug("will try writing %d to %d for inode %ld\n",
		   cur_lblk_off, new_blk_size, inode->i_ino);

	while (cur_lblk_off <= new_blk_size) {
		map.m_lblk = cur_lblk_off;
@@ -1168,7 +1168,7 @@ static void ext4_fc_update_stats(struct super_block *sb, int status,
{
	struct ext4_fc_stats *stats = &EXT4_SB(sb)->s_fc_stats;

	jbd_debug(1, "Fast commit ended with status = %d for tid %u",
	ext4_debug("Fast commit ended with status = %d for tid %u",
			status, commit_tid);
	if (status == EXT4_FC_STATUS_OK) {
		stats->fc_num_commits++;
@@ -1375,14 +1375,14 @@ static int ext4_fc_replay_unlink(struct super_block *sb, struct ext4_fc_tl *tl,
	inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL);

	if (IS_ERR(inode)) {
		jbd_debug(1, "Inode %d not found", darg.ino);
		ext4_debug("Inode %d not found", darg.ino);
		return 0;
	}

	old_parent = ext4_iget(sb, darg.parent_ino,
				EXT4_IGET_NORMAL);
	if (IS_ERR(old_parent)) {
		jbd_debug(1, "Dir with inode  %d not found", darg.parent_ino);
		ext4_debug("Dir with inode %d not found", darg.parent_ino);
		iput(inode);
		return 0;
	}
@@ -1407,21 +1407,21 @@ static int ext4_fc_replay_link_internal(struct super_block *sb,

	dir = ext4_iget(sb, darg->parent_ino, EXT4_IGET_NORMAL);
	if (IS_ERR(dir)) {
		jbd_debug(1, "Dir with inode %d not found.", darg->parent_ino);
		ext4_debug("Dir with inode %d not found.", darg->parent_ino);
		dir = NULL;
		goto out;
	}

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

	dentry_inode = d_alloc(dentry_dir, &qstr_dname);
	if (!dentry_inode) {
		jbd_debug(1, "Inode dentry not created.");
		ext4_debug("Inode dentry not created.");
		ret = -ENOMEM;
		goto out;
	}
@@ -1434,7 +1434,7 @@ static int ext4_fc_replay_link_internal(struct super_block *sb,
	 * could complete.
	 */
	if (ret && ret != -EEXIST) {
		jbd_debug(1, "Failed to link\n");
		ext4_debug("Failed to link\n");
		goto out;
	}

@@ -1468,7 +1468,7 @@ static int ext4_fc_replay_link(struct super_block *sb, struct ext4_fc_tl *tl,

	inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL);
	if (IS_ERR(inode)) {
		jbd_debug(1, "Inode not found.");
		ext4_debug("Inode not found.");
		return 0;
	}

@@ -1576,7 +1576,7 @@ static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl,
	/* Given that we just wrote the inode on disk, this SHOULD succeed. */
	inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL);
	if (IS_ERR(inode)) {
		jbd_debug(1, "Inode not found.");
		ext4_debug("Inode not found.");
		return -EFSCORRUPTED;
	}

@@ -1630,7 +1630,7 @@ static int ext4_fc_replay_create(struct super_block *sb, struct ext4_fc_tl *tl,

	inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL);
	if (IS_ERR(inode)) {
		jbd_debug(1, "inode %d not found.", darg.ino);
		ext4_debug("inode %d not found.", darg.ino);
		inode = NULL;
		ret = -EINVAL;
		goto out;
@@ -1643,7 +1643,7 @@ static int ext4_fc_replay_create(struct super_block *sb, struct ext4_fc_tl *tl,
		 */
		dir = ext4_iget(sb, darg.parent_ino, EXT4_IGET_NORMAL);
		if (IS_ERR(dir)) {
			jbd_debug(1, "Dir %d not found.", darg.ino);
			ext4_debug("Dir %d not found.", darg.ino);
			goto out;
		}
		ret = ext4_init_new_dir(NULL, dir, inode);
@@ -1727,7 +1727,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb,

	inode = ext4_iget(sb, le32_to_cpu(fc_add_ex.fc_ino), EXT4_IGET_NORMAL);
	if (IS_ERR(inode)) {
		jbd_debug(1, "Inode not found.");
		ext4_debug("Inode not found.");
		return 0;
	}

@@ -1741,7 +1741,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb,

	cur = start;
	remaining = len;
	jbd_debug(1, "ADD_RANGE, lblk %d, pblk %lld, len %d, unwritten %d, inode %ld\n",
	ext4_debug("ADD_RANGE, lblk %d, pblk %lld, len %d, unwritten %d, inode %ld\n",
		  start, start_pblk, len, ext4_ext_is_unwritten(ex),
		  inode->i_ino);

@@ -1802,7 +1802,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
		}

		/* Range is mapped and needs a state change */
		jbd_debug(1, "Converting from %ld to %d %lld",
		ext4_debug("Converting from %ld to %d %lld",
				map.m_flags & EXT4_MAP_UNWRITTEN,
			ext4_ext_is_unwritten(ex), map.m_pblk);
		ret = ext4_ext_replay_update_ex(inode, cur, map.m_len,
@@ -1845,7 +1845,7 @@ ext4_fc_replay_del_range(struct super_block *sb, struct ext4_fc_tl *tl,

	inode = ext4_iget(sb, le32_to_cpu(lrange.fc_ino), EXT4_IGET_NORMAL);
	if (IS_ERR(inode)) {
		jbd_debug(1, "Inode %d not found", le32_to_cpu(lrange.fc_ino));
		ext4_debug("Inode %d not found", le32_to_cpu(lrange.fc_ino));
		return 0;
	}

@@ -1853,7 +1853,7 @@ ext4_fc_replay_del_range(struct super_block *sb, struct ext4_fc_tl *tl,
	if (ret)
		goto out;

	jbd_debug(1, "DEL_RANGE, inode %ld, lblk %d, len %d\n",
	ext4_debug("DEL_RANGE, inode %ld, lblk %d, len %d\n",
			inode->i_ino, le32_to_cpu(lrange.fc_lblk),
			le32_to_cpu(lrange.fc_len));
	while (remaining > 0) {
@@ -1902,7 +1902,7 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
		inode = ext4_iget(sb, state->fc_modified_inodes[i],
			EXT4_IGET_NORMAL);
		if (IS_ERR(inode)) {
			jbd_debug(1, "Inode %d not found.",
			ext4_debug("Inode %d not found.",
				state->fc_modified_inodes[i]);
			continue;
		}
@@ -2031,7 +2031,7 @@ static int ext4_fc_replay_scan(journal_t *journal,
	for (cur = start; cur < end; cur = cur + sizeof(tl) + le16_to_cpu(tl.fc_len)) {
		memcpy(&tl, cur, sizeof(tl));
		val = cur + sizeof(tl);
		jbd_debug(3, "Scan phase, tag:%s, blk %lld\n",
		ext4_debug("Scan phase, tag:%s, blk %lld\n",
			  tag2str(le16_to_cpu(tl.fc_tag)), bh->b_blocknr);
		switch (le16_to_cpu(tl.fc_tag)) {
		case EXT4_FC_TAG_ADD_RANGE:
@@ -2126,7 +2126,7 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,
		sbi->s_mount_state |= EXT4_FC_REPLAY;
	}
	if (!sbi->s_fc_replay_state.fc_replay_num_tags) {
		jbd_debug(1, "Replay stops\n");
		ext4_debug("Replay stops\n");
		ext4_fc_set_bitmaps_and_counters(sb);
		return 0;
	}
@@ -2150,7 +2150,7 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,
			ext4_fc_set_bitmaps_and_counters(sb);
			break;
		}
		jbd_debug(3, "Replay phase, tag:%s\n",
		ext4_debug("Replay phase, tag:%s\n",
				tag2str(le16_to_cpu(tl.fc_tag)));
		state->fc_replay_num_tags--;
		switch (le16_to_cpu(tl.fc_tag)) {
Loading