Commit 711e9a4d authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull btrfs fixes from David Sterba:

 - one more fix for a tree-log 'write time corruption' report, update
   the last dir index directly and don't keep in the log context

 - do VFS-level inode lock around FIEMAP to prevent a deadlock with
   concurrent fsync, the extent-level lock is not sufficient

 - don't cache a single-device filesystem device to avoid cases when a
   loop device is reformatted and the entry gets stale

* tag 'for-6.2-rc7-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: free device in btrfs_close_devices for a single device filesystem
  btrfs: lock the inode in shared mode before starting fiemap
  btrfs: simplify update of last_dir_index_offset when logging a directory
parents e2bca0eb 5f58d783
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -3826,6 +3826,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
	lockend = round_up(start + len, inode->root->fs_info->sectorsize);
	prev_extent_end = lockstart;

	btrfs_inode_lock(inode, BTRFS_ILOCK_SHARED);
	lock_extent(&inode->io_tree, lockstart, lockend, &cached_state);

	ret = fiemap_find_last_extent_offset(inode, path, &last_extent_end);
@@ -4019,6 +4020,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,

out_unlock:
	unlock_extent(&inode->io_tree, lockstart, lockend, &cached_state);
	btrfs_inode_unlock(inode, BTRFS_ILOCK_SHARED);
out:
	free_extent_state(delalloc_cached_state);
	btrfs_free_backref_share_ctx(backref_ctx);
+17 −6
Original line number Diff line number Diff line
@@ -3576,17 +3576,19 @@ static noinline int insert_dir_log_key(struct btrfs_trans_handle *trans,
}

static int flush_dir_items_batch(struct btrfs_trans_handle *trans,
				 struct btrfs_root *log,
				 struct btrfs_inode *inode,
				 struct extent_buffer *src,
				 struct btrfs_path *dst_path,
				 int start_slot,
				 int count)
{
	struct btrfs_root *log = inode->root->log_root;
	char *ins_data = NULL;
	struct btrfs_item_batch batch;
	struct extent_buffer *dst;
	unsigned long src_offset;
	unsigned long dst_offset;
	u64 last_index;
	struct btrfs_key key;
	u32 item_size;
	int ret;
@@ -3644,6 +3646,19 @@ static int flush_dir_items_batch(struct btrfs_trans_handle *trans,
	src_offset = btrfs_item_ptr_offset(src, start_slot + count - 1);
	copy_extent_buffer(dst, src, dst_offset, src_offset, batch.total_data_size);
	btrfs_release_path(dst_path);

	last_index = batch.keys[count - 1].offset;
	ASSERT(last_index > inode->last_dir_index_offset);

	/*
	 * If for some unexpected reason the last item's index is not greater
	 * than the last index we logged, warn and return an error to fallback
	 * to a transaction commit.
	 */
	if (WARN_ON(last_index <= inode->last_dir_index_offset))
		ret = -EUCLEAN;
	else
		inode->last_dir_index_offset = last_index;
out:
	kfree(ins_data);

@@ -3693,7 +3708,6 @@ static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
		}

		di = btrfs_item_ptr(src, i, struct btrfs_dir_item);
		ctx->last_dir_item_offset = key.offset;

		/*
		 * Skip ranges of items that consist only of dir item keys created
@@ -3756,7 +3770,7 @@ static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
	if (batch_size > 0) {
		int ret;

		ret = flush_dir_items_batch(trans, log, src, dst_path,
		ret = flush_dir_items_batch(trans, inode, src, dst_path,
					    batch_start, batch_size);
		if (ret < 0)
			return ret;
@@ -4044,7 +4058,6 @@ static noinline int log_directory_changes(struct btrfs_trans_handle *trans,

	min_key = BTRFS_DIR_START_INDEX;
	max_key = 0;
	ctx->last_dir_item_offset = inode->last_dir_index_offset;

	while (1) {
		ret = log_dir_items(trans, inode, path, dst_path,
@@ -4056,8 +4069,6 @@ static noinline int log_directory_changes(struct btrfs_trans_handle *trans,
		min_key = max_key + 1;
	}

	inode->last_dir_index_offset = ctx->last_dir_item_offset;

	return 0;
}

+0 −2
Original line number Diff line number Diff line
@@ -24,8 +24,6 @@ struct btrfs_log_ctx {
	bool logging_new_delayed_dentries;
	/* Indicate if the inode being logged was logged before. */
	bool logged_before;
	/* Tracks the last logged dir item/index key offset. */
	u64 last_dir_item_offset;
	struct inode *inode;
	struct list_head list;
	/* Only used for fast fsyncs. */
+15 −1
Original line number Diff line number Diff line
@@ -403,6 +403,7 @@ void btrfs_free_device(struct btrfs_device *device)
static void free_fs_devices(struct btrfs_fs_devices *fs_devices)
{
	struct btrfs_device *device;

	WARN_ON(fs_devices->opened);
	while (!list_empty(&fs_devices->devices)) {
		device = list_entry(fs_devices->devices.next,
@@ -1181,9 +1182,22 @@ void btrfs_close_devices(struct btrfs_fs_devices *fs_devices)

	mutex_lock(&uuid_mutex);
	close_fs_devices(fs_devices);
	if (!fs_devices->opened)
	if (!fs_devices->opened) {
		list_splice_init(&fs_devices->seed_list, &list);

		/*
		 * If the struct btrfs_fs_devices is not assembled with any
		 * other device, it can be re-initialized during the next mount
		 * without the needing device-scan step. Therefore, it can be
		 * fully freed.
		 */
		if (fs_devices->num_devices == 1) {
			list_del(&fs_devices->fs_list);
			free_fs_devices(fs_devices);
		}
	}


	list_for_each_entry_safe(fs_devices, tmp, &list, seed_list) {
		close_fs_devices(fs_devices);
		list_del(&fs_devices->seed_list);