Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bdeb03ca authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull btrfs update from Chris Mason:
 "From a feature point of view, most of the code here comes from Miao
  Xie and others at Fujitsu to implement scrubbing and replacing devices
  on raid56.  This has been in development for a while, and it's a big
  improvement.

  Filipe and Josef have a great assortment of fixes, many of which solve
  problems corruptions either after a crash or in error conditions.  I
  still have a round two from Filipe for next week that solves
  corruptions with discard and block group removal"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs: (62 commits)
  Btrfs: make get_caching_control unconditionally return the ctl
  Btrfs: fix unprotected deletion from pending_chunks list
  Btrfs: fix fs mapping extent map leak
  Btrfs: fix memory leak after block remove + trimming
  Btrfs: make btrfs_abort_transaction consider existence of new block groups
  Btrfs: fix race between writing free space cache and trimming
  Btrfs: fix race between fs trimming and block group remove/allocation
  Btrfs, replace: enable dev-replace for raid56
  Btrfs: fix freeing used extents after removing empty block group
  Btrfs: fix crash caused by block group removal
  Btrfs: fix invalid block group rbtree access after bg is removed
  Btrfs, raid56: fix use-after-free problem in the final device replace procedure on raid56
  Btrfs, replace: write raid56 parity into the replace target device
  Btrfs, replace: write dirty pages into the replace target device
  Btrfs, raid56: support parity scrub on raid56
  Btrfs, raid56: use a variant to record the operation type
  Btrfs, scrub: repair the common data on RAID5/6 if it is corrupted
  Btrfs, raid56: don't change bbio and raid_map
  Btrfs: remove unnecessary code of stripe_index assignment in __btrfs_map_block
  Btrfs: remove noused bbio_ret in __btrfs_map_block in condition
  ...
parents 0349678c 9627aeee
Loading
Loading
Loading
Loading
+65 −98
Original line number Diff line number Diff line
@@ -94,6 +94,7 @@
#include <linux/mutex.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/vmalloc.h>
#include "ctree.h"
#include "disk-io.h"
#include "hash.h"
@@ -326,9 +327,6 @@ static int btrfsic_handle_extent_data(struct btrfsic_state *state,
static int btrfsic_map_block(struct btrfsic_state *state, u64 bytenr, u32 len,
			     struct btrfsic_block_data_ctx *block_ctx_out,
			     int mirror_num);
static int btrfsic_map_superblock(struct btrfsic_state *state, u64 bytenr,
				  u32 len, struct block_device *bdev,
				  struct btrfsic_block_data_ctx *block_ctx_out);
static void btrfsic_release_block_ctx(struct btrfsic_block_data_ctx *block_ctx);
static int btrfsic_read_block(struct btrfsic_state *state,
			      struct btrfsic_block_data_ctx *block_ctx);
@@ -1326,24 +1324,25 @@ static int btrfsic_create_link_to_next_block(
		l = NULL;
		next_block->generation = BTRFSIC_GENERATION_UNKNOWN;
	} else {
		if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) {
			if (next_block->logical_bytenr != next_bytenr &&
			    !(!next_block->is_metadata &&
		      0 == next_block->logical_bytenr)) {
			      0 == next_block->logical_bytenr))
				printk(KERN_INFO
			       "Referenced block @%llu (%s/%llu/%d)"
			       " found in hash table, %c,"
			       " bytenr mismatch (!= stored %llu).\n",
				       "Referenced block @%llu (%s/%llu/%d) found in hash table, %c, bytenr mismatch (!= stored %llu).\n",
				       next_bytenr, next_block_ctx->dev->name,
				       next_block_ctx->dev_bytenr, *mirror_nump,
			       btrfsic_get_block_type(state, next_block),
				       btrfsic_get_block_type(state,
							      next_block),
				       next_block->logical_bytenr);
		} else if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE)
			else
				printk(KERN_INFO
			       "Referenced block @%llu (%s/%llu/%d)"
			       " found in hash table, %c.\n",
				       "Referenced block @%llu (%s/%llu/%d) found in hash table, %c.\n",
				       next_bytenr, next_block_ctx->dev->name,
				       next_block_ctx->dev_bytenr, *mirror_nump,
			       btrfsic_get_block_type(state, next_block));
				       btrfsic_get_block_type(state,
							      next_block));
		}
		next_block->logical_bytenr = next_bytenr;

		next_block->mirror_num = *mirror_nump;
@@ -1529,7 +1528,9 @@ static int btrfsic_handle_extent_data(
				return -1;
			}
			if (!block_was_created) {
				if (next_block->logical_bytenr != next_bytenr &&
				if ((state->print_mask &
				     BTRFSIC_PRINT_MASK_VERBOSE) &&
				    next_block->logical_bytenr != next_bytenr &&
				    !(!next_block->is_metadata &&
				      0 == next_block->logical_bytenr)) {
					printk(KERN_INFO
@@ -1607,25 +1608,6 @@ static int btrfsic_map_block(struct btrfsic_state *state, u64 bytenr, u32 len,
	return ret;
}

static int btrfsic_map_superblock(struct btrfsic_state *state, u64 bytenr,
				  u32 len, struct block_device *bdev,
				  struct btrfsic_block_data_ctx *block_ctx_out)
{
	block_ctx_out->dev = btrfsic_dev_state_lookup(bdev);
	block_ctx_out->dev_bytenr = bytenr;
	block_ctx_out->start = bytenr;
	block_ctx_out->len = len;
	block_ctx_out->datav = NULL;
	block_ctx_out->pagev = NULL;
	block_ctx_out->mem_to_free = NULL;
	if (NULL != block_ctx_out->dev) {
		return 0;
	} else {
		printk(KERN_INFO "btrfsic: error, cannot lookup dev (#2)!\n");
		return -ENXIO;
	}
}

static void btrfsic_release_block_ctx(struct btrfsic_block_data_ctx *block_ctx)
{
	if (block_ctx->mem_to_free) {
@@ -1901,25 +1883,26 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
							       dev_state,
							       dev_bytenr);
			}
			if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) {
				if (block->logical_bytenr != bytenr &&
				    !(!block->is_metadata &&
				      block->logical_bytenr == 0))
					printk(KERN_INFO
				       "Written block @%llu (%s/%llu/%d)"
				       " found in hash table, %c,"
				       " bytenr mismatch"
				       " (!= stored %llu).\n",
				       bytenr, dev_state->name, dev_bytenr,
					       "Written block @%llu (%s/%llu/%d) found in hash table, %c, bytenr mismatch (!= stored %llu).\n",
					       bytenr, dev_state->name,
					       dev_bytenr,
					       block->mirror_num,
				       btrfsic_get_block_type(state, block),
					       btrfsic_get_block_type(state,
								      block),
					       block->logical_bytenr);
			else if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE)
				else
					printk(KERN_INFO
				       "Written block @%llu (%s/%llu/%d)"
				       " found in hash table, %c.\n",
				       bytenr, dev_state->name, dev_bytenr,
				       block->mirror_num,
				       btrfsic_get_block_type(state, block));
					       "Written block @%llu (%s/%llu/%d) found in hash table, %c.\n",
					       bytenr, dev_state->name,
					       dev_bytenr, block->mirror_num,
					       btrfsic_get_block_type(state,
								      block));
			}
			block->logical_bytenr = bytenr;
		} else {
			if (num_pages * PAGE_CACHE_SIZE <
@@ -2002,24 +1985,13 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
			}
		}

		if (block->is_superblock)
			ret = btrfsic_map_superblock(state, bytenr,
						     processed_len,
						     bdev, &block_ctx);
		else
			ret = btrfsic_map_block(state, bytenr, processed_len,
						&block_ctx, 0);
		if (ret) {
			printk(KERN_INFO
			       "btrfsic: btrfsic_map_block(root @%llu)"
			       " failed!\n", bytenr);
			goto continue_loop;
		}
		block_ctx.datav = mapped_datav;
		/* the following is required in case of writes to mirrors,
		 * use the same that was used for the lookup */
		block_ctx.dev = dev_state;
		block_ctx.dev_bytenr = dev_bytenr;
		block_ctx.start = bytenr;
		block_ctx.len = processed_len;
		block_ctx.pagev = NULL;
		block_ctx.mem_to_free = NULL;
		block_ctx.datav = mapped_datav;

		if (is_metadata || state->include_extent_data) {
			block->never_written = 0;
@@ -2133,10 +2105,6 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
			/* this is getting ugly for the
			 * include_extent_data case... */
			bytenr = 0;	/* unknown */
			block_ctx.start = bytenr;
			block_ctx.len = processed_len;
			block_ctx.mem_to_free = NULL;
			block_ctx.pagev = NULL;
		} else {
			processed_len = state->metablock_size;
			bytenr = btrfs_stack_header_bytenr(
@@ -2149,22 +2117,15 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
				       "Written block @%llu (%s/%llu/?)"
				       " !found in hash table, M.\n",
				       bytenr, dev_state->name, dev_bytenr);

			ret = btrfsic_map_block(state, bytenr, processed_len,
						&block_ctx, 0);
			if (ret) {
				printk(KERN_INFO
				       "btrfsic: btrfsic_map_block(root @%llu)"
				       " failed!\n",
				       dev_bytenr);
				goto continue_loop;
		}
		}
		block_ctx.datav = mapped_datav;
		/* the following is required in case of writes to mirrors,
		 * use the same that was used for the lookup */

		block_ctx.dev = dev_state;
		block_ctx.dev_bytenr = dev_bytenr;
		block_ctx.start = bytenr;
		block_ctx.len = processed_len;
		block_ctx.pagev = NULL;
		block_ctx.mem_to_free = NULL;
		block_ctx.datav = mapped_datav;

		block = btrfsic_block_alloc();
		if (NULL == block) {
@@ -3130,11 +3091,14 @@ int btrfsic_mount(struct btrfs_root *root,
		       root->sectorsize, PAGE_CACHE_SIZE);
		return -1;
	}
	state = kzalloc(sizeof(*state), GFP_NOFS);
	if (NULL == state) {
		printk(KERN_INFO "btrfs check-integrity: kmalloc() failed!\n");
	state = kzalloc(sizeof(*state), GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
	if (!state) {
		state = vzalloc(sizeof(*state));
		if (!state) {
			printk(KERN_INFO "btrfs check-integrity: vzalloc() failed!\n");
			return -1;
		}
	}

	if (!btrfsic_is_initialized) {
		mutex_init(&btrfsic_mutex);
@@ -3277,5 +3241,8 @@ void btrfsic_unmount(struct btrfs_root *root,

	mutex_unlock(&btrfsic_mutex);

	if (is_vmalloc_addr(state))
		vfree(state);
	else
		kfree(state);
}
+12 −6
Original line number Diff line number Diff line
@@ -224,16 +224,19 @@ static void end_compressed_bio_read(struct bio *bio, int err)
 * Clear the writeback bits on all of the file
 * pages for a compressed write
 */
static noinline void end_compressed_writeback(struct inode *inode, u64 start,
					      unsigned long ram_size)
static noinline void end_compressed_writeback(struct inode *inode,
					      const struct compressed_bio *cb)
{
	unsigned long index = start >> PAGE_CACHE_SHIFT;
	unsigned long end_index = (start + ram_size - 1) >> PAGE_CACHE_SHIFT;
	unsigned long index = cb->start >> PAGE_CACHE_SHIFT;
	unsigned long end_index = (cb->start + cb->len - 1) >> PAGE_CACHE_SHIFT;
	struct page *pages[16];
	unsigned long nr_pages = end_index - index + 1;
	int i;
	int ret;

	if (cb->errors)
		mapping_set_error(inode->i_mapping, -EIO);

	while (nr_pages > 0) {
		ret = find_get_pages_contig(inode->i_mapping, index,
				     min_t(unsigned long,
@@ -244,6 +247,8 @@ static noinline void end_compressed_writeback(struct inode *inode, u64 start,
			continue;
		}
		for (i = 0; i < ret; i++) {
			if (cb->errors)
				SetPageError(pages[i]);
			end_page_writeback(pages[i]);
			page_cache_release(pages[i]);
		}
@@ -287,10 +292,11 @@ static void end_compressed_bio_write(struct bio *bio, int err)
	tree->ops->writepage_end_io_hook(cb->compressed_pages[0],
					 cb->start,
					 cb->start + cb->len - 1,
					 NULL, 1);
					 NULL,
					 err ? 0 : 1);
	cb->compressed_pages[0]->mapping = NULL;

	end_compressed_writeback(inode, cb->start, cb->len);
	end_compressed_writeback(inode, cb);
	/* note, our inode could be gone now */

	/*
+1 −1
Original line number Diff line number Diff line
@@ -2929,7 +2929,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
	 */
	if (!p->leave_spinning)
		btrfs_set_path_blocking(p);
	if (ret < 0)
	if (ret < 0 && !p->skip_release_on_error)
		btrfs_release_path(p);
	return ret;
}
+80 −5
Original line number Diff line number Diff line
@@ -607,6 +607,7 @@ struct btrfs_path {
	unsigned int leave_spinning:1;
	unsigned int search_commit_root:1;
	unsigned int need_commit_sem:1;
	unsigned int skip_release_on_error:1;
};

/*
@@ -1170,6 +1171,7 @@ struct btrfs_space_info {
	struct percpu_counter total_bytes_pinned;

	struct list_head list;
	struct list_head ro_bgs;

	struct rw_semaphore groups_sem;
	/* for block groups in our same type */
@@ -1276,6 +1278,8 @@ struct btrfs_block_group_cache {
	unsigned int ro:1;
	unsigned int dirty:1;
	unsigned int iref:1;
	unsigned int has_caching_ctl:1;
	unsigned int removed:1;

	int disk_cache_state;

@@ -1305,6 +1309,11 @@ struct btrfs_block_group_cache {

	/* For delayed block group creation or deletion of empty block groups */
	struct list_head bg_list;

	/* For read-only block groups */
	struct list_head ro_list;

	atomic_t trimming;
};

/* delayed seq elem */
@@ -1402,6 +1411,11 @@ struct btrfs_fs_info {
	 */
	u64 last_trans_log_full_commit;
	unsigned long mount_opt;
	/*
	 * Track requests for actions that need to be done during transaction
	 * commit (like for some mount options).
	 */
	unsigned long pending_changes;
	unsigned long compress_type:4;
	int commit_interval;
	/*
@@ -1729,6 +1743,12 @@ struct btrfs_fs_info {

	/* For btrfs to record security options */
	struct security_mnt_opts security_opts;

	/*
	 * Chunks that can't be freed yet (under a trim/discard operation)
	 * and will be latter freed. Protected by fs_info->chunk_mutex.
	 */
	struct list_head pinned_chunks;
};

struct btrfs_subvolume_writers {
@@ -2093,7 +2113,6 @@ struct btrfs_ioctl_defrag_range_args {
#define BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA (1 << 21)
#define BTRFS_MOUNT_PANIC_ON_FATAL_ERROR	(1 << 22)
#define BTRFS_MOUNT_RESCAN_UUID_TREE	(1 << 23)
#define	BTRFS_MOUNT_CHANGE_INODE_CACHE	(1 << 24)

#define BTRFS_DEFAULT_COMMIT_INTERVAL	(30)
#define BTRFS_DEFAULT_MAX_INLINE	(8192)
@@ -2103,6 +2122,7 @@ struct btrfs_ioctl_defrag_range_args {
#define btrfs_raw_test_opt(o, opt)	((o) & BTRFS_MOUNT_##opt)
#define btrfs_test_opt(root, opt)	((root)->fs_info->mount_opt & \
					 BTRFS_MOUNT_##opt)

#define btrfs_set_and_info(root, opt, fmt, args...)			\
{									\
	if (!btrfs_test_opt(root, opt))					\
@@ -2117,6 +2137,49 @@ struct btrfs_ioctl_defrag_range_args {
	btrfs_clear_opt(root->fs_info->mount_opt, opt);			\
}

/*
 * Requests for changes that need to be done during transaction commit.
 *
 * Internal mount options that are used for special handling of the real
 * mount options (eg. cannot be set during remount and have to be set during
 * transaction commit)
 */

#define BTRFS_PENDING_SET_INODE_MAP_CACHE	(0)
#define BTRFS_PENDING_CLEAR_INODE_MAP_CACHE	(1)
#define BTRFS_PENDING_COMMIT			(2)

#define btrfs_test_pending(info, opt)	\
	test_bit(BTRFS_PENDING_##opt, &(info)->pending_changes)
#define btrfs_set_pending(info, opt)	\
	set_bit(BTRFS_PENDING_##opt, &(info)->pending_changes)
#define btrfs_clear_pending(info, opt)	\
	clear_bit(BTRFS_PENDING_##opt, &(info)->pending_changes)

/*
 * Helpers for setting pending mount option changes.
 *
 * Expects corresponding macros
 * BTRFS_PENDING_SET_ and CLEAR_ + short mount option name
 */
#define btrfs_set_pending_and_info(info, opt, fmt, args...)            \
do {                                                                   \
       if (!btrfs_raw_test_opt((info)->mount_opt, opt)) {              \
               btrfs_info((info), fmt, ##args);                        \
               btrfs_set_pending((info), SET_##opt);                   \
               btrfs_clear_pending((info), CLEAR_##opt);               \
       }                                                               \
} while(0)

#define btrfs_clear_pending_and_info(info, opt, fmt, args...)          \
do {                                                                   \
       if (btrfs_raw_test_opt((info)->mount_opt, opt)) {               \
               btrfs_info((info), fmt, ##args);                        \
               btrfs_set_pending((info), CLEAR_##opt);                 \
               btrfs_clear_pending((info), SET_##opt);                 \
       }                                                               \
} while(0)

/*
 * Inode flags
 */
@@ -3351,7 +3414,8 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
			   u64 type, u64 chunk_objectid, u64 chunk_offset,
			   u64 size);
int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
			     struct btrfs_root *root, u64 group_start);
			     struct btrfs_root *root, u64 group_start,
			     struct extent_map *em);
void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info);
void btrfs_create_pending_block_groups(struct btrfs_trans_handle *trans,
				       struct btrfs_root *root);
@@ -3427,8 +3491,8 @@ int btrfs_init_space_info(struct btrfs_fs_info *fs_info);
int btrfs_delayed_refs_qgroup_accounting(struct btrfs_trans_handle *trans,
					 struct btrfs_fs_info *fs_info);
int __get_raid_index(u64 flags);
int btrfs_start_nocow_write(struct btrfs_root *root);
void btrfs_end_nocow_write(struct btrfs_root *root);
int btrfs_start_write_no_snapshoting(struct btrfs_root *root);
void btrfs_end_write_no_snapshoting(struct btrfs_root *root);
/* ctree.c */
int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key,
		     int level, int *slot);
@@ -3686,6 +3750,10 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans,
int verify_dir_item(struct btrfs_root *root,
		    struct extent_buffer *leaf,
		    struct btrfs_dir_item *dir_item);
struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
						 struct btrfs_path *path,
						 const char *name,
						 int name_len);

/* orphan.c */
int btrfs_insert_orphan_item(struct btrfs_trans_handle *trans,
@@ -3857,6 +3925,7 @@ int btrfs_prealloc_file_range_trans(struct inode *inode,
				    struct btrfs_trans_handle *trans, int mode,
				    u64 start, u64 num_bytes, u64 min_size,
				    loff_t actual_len, u64 *alloc_hint);
int btrfs_inode_check_errors(struct inode *inode);
extern const struct dentry_operations btrfs_dentry_operations;

/* ioctl.c */
@@ -3901,6 +3970,7 @@ int btrfs_dirty_pages(struct btrfs_root *root, struct inode *inode,
		      struct page **pages, size_t num_pages,
		      loff_t pos, size_t write_bytes,
		      struct extent_state **cached);
int btrfs_fdatawrite_range(struct inode *inode, loff_t start, loff_t end);

/* tree-defrag.c */
int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
@@ -4097,7 +4167,12 @@ int btrfs_scrub_progress(struct btrfs_root *root, u64 devid,
/* dev-replace.c */
void btrfs_bio_counter_inc_blocked(struct btrfs_fs_info *fs_info);
void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info);
void btrfs_bio_counter_dec(struct btrfs_fs_info *fs_info);
void btrfs_bio_counter_sub(struct btrfs_fs_info *fs_info, s64 amount);

static inline void btrfs_bio_counter_dec(struct btrfs_fs_info *fs_info)
{
	btrfs_bio_counter_sub(fs_info, 1);
}

/* reada.c */
struct reada_control {
+17 −15
Original line number Diff line number Diff line
@@ -316,11 +316,6 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
	struct btrfs_device *tgt_device = NULL;
	struct btrfs_device *src_device = NULL;

	if (btrfs_fs_incompat(fs_info, RAID56)) {
		btrfs_warn(fs_info, "dev_replace cannot yet handle RAID5/RAID6");
		return -EOPNOTSUPP;
	}

	switch (args->start.cont_reading_from_srcdev_mode) {
	case BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS:
	case BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID:
@@ -422,9 +417,15 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
			      &dev_replace->scrub_progress, 0, 1);

	ret = btrfs_dev_replace_finishing(root->fs_info, ret);
	/* don't warn if EINPROGRESS, someone else might be running scrub */
	if (ret == -EINPROGRESS) {
		args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS;
		ret = 0;
	} else {
		WARN_ON(ret);
	}

	return 0;
	return ret;

leave:
	dev_replace->srcdev = NULL;
@@ -542,7 +543,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
			btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
		mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);

		return 0;
		return scrub_ret;
	}

	printk_in_rcu(KERN_INFO
@@ -571,15 +572,11 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
	list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list);
	fs_info->fs_devices->rw_devices++;

	/* replace the sysfs entry */
	btrfs_kobj_rm_device(fs_info, src_device);
	btrfs_kobj_add_device(fs_info, tgt_device);

	btrfs_dev_replace_unlock(dev_replace);

	btrfs_rm_dev_replace_blocked(fs_info);

	btrfs_rm_dev_replace_srcdev(fs_info, src_device);
	btrfs_rm_dev_replace_remove_srcdev(fs_info, src_device);

	btrfs_rm_dev_replace_unblocked(fs_info);

@@ -594,6 +591,11 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
	mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
	mutex_unlock(&uuid_mutex);

	/* replace the sysfs entry */
	btrfs_kobj_rm_device(fs_info, src_device);
	btrfs_kobj_add_device(fs_info, tgt_device);
	btrfs_rm_dev_replace_free_srcdev(fs_info, src_device);

	/* write back the superblocks */
	trans = btrfs_start_transaction(root, 0);
	if (!IS_ERR(trans))
@@ -920,9 +922,9 @@ void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info)
	percpu_counter_inc(&fs_info->bio_counter);
}

void btrfs_bio_counter_dec(struct btrfs_fs_info *fs_info)
void btrfs_bio_counter_sub(struct btrfs_fs_info *fs_info, s64 amount)
{
	percpu_counter_dec(&fs_info->bio_counter);
	percpu_counter_sub(&fs_info->bio_counter, amount);

	if (waitqueue_active(&fs_info->replace_wait))
		wake_up(&fs_info->replace_wait);
Loading