Commit 86286e48 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull btrfs fixes from David Sterba:
 "A few fixes and error handling improvements:

   - fix deadlock between quota disable and qgroup rescan worker

   - fix use-after-free after failure to create a snapshot

   - skip warning on unmount after log cleanup failure

   - don't start transaction for scrub if the fs is mounted read-only

   - tree checker verifies item sizes"

* tag 'for-5.17-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: skip reserved bytes warning on unmount after log cleanup failure
  btrfs: fix use of uninitialized variable at rm device ioctl
  btrfs: fix use-after-free after failure to create a snapshot
  btrfs: tree-checker: check item_size for dev_item
  btrfs: tree-checker: check item_size for inode_item
  btrfs: fix deadlock between quota disable and qgroup rescan worker
  btrfs: don't start transaction for scrub if the fs is mounted read-only
parents b0bc0cb8 40cdc509
Loading
Loading
Loading
Loading
+37 −2
Original line number Diff line number Diff line
@@ -124,6 +124,15 @@ void btrfs_put_block_group(struct btrfs_block_group *cache)
{
	if (refcount_dec_and_test(&cache->refs)) {
		WARN_ON(cache->pinned > 0);
		/*
		 * If there was a failure to cleanup a log tree, very likely due
		 * to an IO failure on a writeback attempt of one or more of its
		 * extent buffers, we could not do proper (and cheap) unaccounting
		 * of their reserved space, so don't warn on reserved > 0 in that
		 * case.
		 */
		if (!(cache->flags & BTRFS_BLOCK_GROUP_METADATA) ||
		    !BTRFS_FS_LOG_CLEANUP_ERROR(cache->fs_info))
			WARN_ON(cache->reserved > 0);

		/*
@@ -2544,6 +2553,19 @@ int btrfs_inc_block_group_ro(struct btrfs_block_group *cache,
	int ret;
	bool dirty_bg_running;

	/*
	 * This can only happen when we are doing read-only scrub on read-only
	 * mount.
	 * In that case we should not start a new transaction on read-only fs.
	 * Thus here we skip all chunk allocations.
	 */
	if (sb_rdonly(fs_info->sb)) {
		mutex_lock(&fs_info->ro_block_group_mutex);
		ret = inc_block_group_ro(cache, 0);
		mutex_unlock(&fs_info->ro_block_group_mutex);
		return ret;
	}

	do {
		trans = btrfs_join_transaction(root);
		if (IS_ERR(trans))
@@ -3974,9 +3996,22 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
		 * important and indicates a real bug if this happens.
		 */
		if (WARN_ON(space_info->bytes_pinned > 0 ||
			    space_info->bytes_reserved > 0 ||
			    space_info->bytes_may_use > 0))
			btrfs_dump_space_info(info, space_info, 0, 0);

		/*
		 * If there was a failure to cleanup a log tree, very likely due
		 * to an IO failure on a writeback attempt of one or more of its
		 * extent buffers, we could not do proper (and cheap) unaccounting
		 * of their reserved space, so don't warn on bytes_reserved > 0 in
		 * that case.
		 */
		if (!(space_info->flags & BTRFS_BLOCK_GROUP_METADATA) ||
		    !BTRFS_FS_LOG_CLEANUP_ERROR(info)) {
			if (WARN_ON(space_info->bytes_reserved > 0))
				btrfs_dump_space_info(info, space_info, 0, 0);
		}

		WARN_ON(space_info->reclaim_size > 0);
		list_del(&space_info->list);
		btrfs_sysfs_remove_space_info(space_info);
+6 −0
Original line number Diff line number Diff line
@@ -145,6 +145,9 @@ enum {
	BTRFS_FS_STATE_DUMMY_FS_INFO,

	BTRFS_FS_STATE_NO_CSUMS,

	/* Indicates there was an error cleaning up a log tree. */
	BTRFS_FS_STATE_LOG_CLEANUP_ERROR,
};

#define BTRFS_BACKREF_REV_MAX		256
@@ -3593,6 +3596,9 @@ do { \

#define BTRFS_FS_ERROR(fs_info)	(unlikely(test_bit(BTRFS_FS_STATE_ERROR, \
						   &(fs_info)->fs_state)))
#define BTRFS_FS_LOG_CLEANUP_ERROR(fs_info)				\
	(unlikely(test_bit(BTRFS_FS_STATE_LOG_CLEANUP_ERROR,		\
			   &(fs_info)->fs_state)))

__printf(5, 6)
__cold
+2 −5
Original line number Diff line number Diff line
@@ -805,10 +805,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
		goto fail;
	}

	spin_lock(&fs_info->trans_lock);
	list_add(&pending_snapshot->list,
		 &trans->transaction->pending_snapshots);
	spin_unlock(&fs_info->trans_lock);
	trans->pending_snapshot = pending_snapshot;

	ret = btrfs_commit_transaction(trans);
	if (ret)
@@ -3354,7 +3351,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
	struct block_device *bdev = NULL;
	fmode_t mode;
	int ret;
	bool cancel;
	bool cancel = false;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;
+19 −2
Original line number Diff line number Diff line
@@ -1185,9 +1185,24 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
	struct btrfs_trans_handle *trans = NULL;
	int ret = 0;

	/*
	 * We need to have subvol_sem write locked, to prevent races between
	 * concurrent tasks trying to disable quotas, because we will unlock
	 * and relock qgroup_ioctl_lock across BTRFS_FS_QUOTA_ENABLED changes.
	 */
	lockdep_assert_held_write(&fs_info->subvol_sem);

	mutex_lock(&fs_info->qgroup_ioctl_lock);
	if (!fs_info->quota_root)
		goto out;

	/*
	 * Request qgroup rescan worker to complete and wait for it. This wait
	 * must be done before transaction start for quota disable since it may
	 * deadlock with transaction by the qgroup rescan worker.
	 */
	clear_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
	btrfs_qgroup_wait_for_completion(fs_info, false);
	mutex_unlock(&fs_info->qgroup_ioctl_lock);

	/*
@@ -1205,14 +1220,13 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
	if (IS_ERR(trans)) {
		ret = PTR_ERR(trans);
		trans = NULL;
		set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
		goto out;
	}

	if (!fs_info->quota_root)
		goto out;

	clear_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
	btrfs_qgroup_wait_for_completion(fs_info, false);
	spin_lock(&fs_info->qgroup_lock);
	quota_root = fs_info->quota_root;
	fs_info->quota_root = NULL;
@@ -3383,6 +3397,9 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
			btrfs_warn(fs_info,
			"qgroup rescan init failed, qgroup is not enabled");
			ret = -EINVAL;
		} else if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) {
			/* Quota disable is in progress */
			ret = -EBUSY;
		}

		if (ret) {
+24 −0
Original line number Diff line number Diff line
@@ -2000,6 +2000,27 @@ static inline void btrfs_wait_delalloc_flush(struct btrfs_fs_info *fs_info)
		btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1);
}

/*
 * Add a pending snapshot associated with the given transaction handle to the
 * respective handle. This must be called after the transaction commit started
 * and while holding fs_info->trans_lock.
 * This serves to guarantee a caller of btrfs_commit_transaction() that it can
 * safely free the pending snapshot pointer in case btrfs_commit_transaction()
 * returns an error.
 */
static void add_pending_snapshot(struct btrfs_trans_handle *trans)
{
	struct btrfs_transaction *cur_trans = trans->transaction;

	if (!trans->pending_snapshot)
		return;

	lockdep_assert_held(&trans->fs_info->trans_lock);
	ASSERT(cur_trans->state >= TRANS_STATE_COMMIT_START);

	list_add(&trans->pending_snapshot->list, &cur_trans->pending_snapshots);
}

int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
{
	struct btrfs_fs_info *fs_info = trans->fs_info;
@@ -2073,6 +2094,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
	if (cur_trans->state >= TRANS_STATE_COMMIT_START) {
		enum btrfs_trans_state want_state = TRANS_STATE_COMPLETED;

		add_pending_snapshot(trans);

		spin_unlock(&fs_info->trans_lock);
		refcount_inc(&cur_trans->use_count);

@@ -2163,6 +2186,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
	 * COMMIT_DOING so make sure to wait for num_writers to == 1 again.
	 */
	spin_lock(&fs_info->trans_lock);
	add_pending_snapshot(trans);
	cur_trans->state = TRANS_STATE_COMMIT_DOING;
	spin_unlock(&fs_info->trans_lock);
	wait_event(cur_trans->writer_wait,
Loading