diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
| -rw-r--r-- | fs/btrfs/tree-log.c | 323 | 
1 files changed, 156 insertions, 167 deletions
| diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 66500ebdd35c..747daaaaf332 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -162,16 +162,17 @@ struct walk_control {  	struct btrfs_key log_key;  	/* The slot being processed of the current log leaf. */  	int log_slot; + +	/* A path used for searches and modifications to subvolume trees. */ +	struct btrfs_path *subvol_path;  };  static int btrfs_log_inode(struct btrfs_trans_handle *trans,  			   struct btrfs_inode *inode,  			   int inode_only,  			   struct btrfs_log_ctx *ctx); -static int link_to_fixup_dir(struct walk_control *wc, -			     struct btrfs_path *path, u64 objectid); +static int link_to_fixup_dir(struct walk_control *wc, u64 objectid);  static noinline int replay_dir_deletes(struct walk_control *wc, -				       struct btrfs_path *path,  				       u64 dirid, bool del_all);  static void wait_log_commit(struct btrfs_root *root, int transid); @@ -422,7 +423,7 @@ static int process_one_buffer(struct extent_buffer *eb,   *   * If the key isn't in the destination yet, a new item is inserted.   */ -static int overwrite_item(struct walk_control *wc, struct btrfs_path *path) +static int overwrite_item(struct walk_control *wc)  {  	struct btrfs_trans_handle *trans = wc->trans;  	struct btrfs_root *root = wc->root; @@ -449,14 +450,14 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)  	src_ptr = btrfs_item_ptr_offset(wc->log_leaf, wc->log_slot);  	/* Look for the key in the destination tree. */ -	ret = btrfs_search_slot(NULL, root, &wc->log_key, path, 0, 0); +	ret = btrfs_search_slot(NULL, root, &wc->log_key, wc->subvol_path, 0, 0);  	if (ret < 0) {  		btrfs_abort_transaction(trans, ret);  		return ret;  	} -	dst_eb = path->nodes[0]; -	dst_slot = path->slots[0]; +	dst_eb = wc->subvol_path->nodes[0]; +	dst_slot = wc->subvol_path->slots[0];  	if (ret == 0) {  		char *src_copy; @@ -466,7 +467,7 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)  			goto insert;  		if (item_size == 0) { -			btrfs_release_path(path); +			btrfs_release_path(wc->subvol_path);  			return 0;  		}  		src_copy = kmalloc(item_size, GFP_NOFS); @@ -487,7 +488,7 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)  		 * sync  		 */  		if (ret == 0) { -			btrfs_release_path(path); +			btrfs_release_path(wc->subvol_path);  			return 0;  		} @@ -537,23 +538,23 @@ static int overwrite_item(struct walk_control *wc, struct btrfs_path *path)  			btrfs_set_inode_size(wc->log_leaf, item, 0);  	}  insert: -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	/* try to insert the key into the destination tree */ -	path->skip_release_on_error = 1; -	ret = btrfs_insert_empty_item(trans, root, path, &wc->log_key, item_size); -	path->skip_release_on_error = 0; +	wc->subvol_path->skip_release_on_error = 1; +	ret = btrfs_insert_empty_item(trans, root, wc->subvol_path, &wc->log_key, item_size); +	wc->subvol_path->skip_release_on_error = 0; -	dst_eb = path->nodes[0]; -	dst_slot = path->slots[0]; +	dst_eb = wc->subvol_path->nodes[0]; +	dst_slot = wc->subvol_path->slots[0];  	/* make sure any existing item is the correct size */  	if (ret == -EEXIST || ret == -EOVERFLOW) {  		const u32 found_size = btrfs_item_size(dst_eb, dst_slot);  		if (found_size > item_size) -			btrfs_truncate_item(trans, path, item_size, 1); +			btrfs_truncate_item(trans, wc->subvol_path, item_size, 1);  		else if (found_size < item_size) -			btrfs_extend_item(trans, path, item_size - found_size); +			btrfs_extend_item(trans, wc->subvol_path, item_size - found_size);  	} else if (ret) {  		btrfs_abort_transaction(trans, ret);  		return ret; @@ -618,7 +619,7 @@ insert:  			btrfs_set_inode_generation(dst_eb, dst_item, trans->transid);  	}  no_copy: -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	return 0;  } @@ -649,7 +650,7 @@ static int read_alloc_one_name(struct extent_buffer *eb, void *start, int len,   * The extent is inserted into the file, dropping any existing extents   * from the file that overlap the new one.   */ -static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path *path) +static noinline int replay_one_extent(struct walk_control *wc)  {  	struct btrfs_trans_handle *trans = wc->trans;  	struct btrfs_root *root = wc->root; @@ -710,16 +711,18 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path  	 * file.  This must be done before the btrfs_drop_extents run  	 * so we don't try to drop this extent.  	 */ -	ret = btrfs_lookup_file_extent(trans, root, path, btrfs_ino(inode), start, 0); +	ret = btrfs_lookup_file_extent(trans, root, wc->subvol_path, +				       btrfs_ino(inode), start, 0);  	if (ret == 0 &&  	    (found_type == BTRFS_FILE_EXTENT_REG ||  	     found_type == BTRFS_FILE_EXTENT_PREALLOC)) { +		struct extent_buffer *leaf = wc->subvol_path->nodes[0];  		struct btrfs_file_extent_item existing;  		unsigned long ptr; -		ptr = btrfs_item_ptr_offset(path->nodes[0], path->slots[0]); -		read_extent_buffer(path->nodes[0], &existing, ptr, sizeof(existing)); +		ptr = btrfs_item_ptr_offset(leaf, wc->subvol_path->slots[0]); +		read_extent_buffer(leaf, &existing, ptr, sizeof(existing));  		/*  		 * we already have a pointer to this exact extent, @@ -727,17 +730,17 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path  		 */  		if (memcmp_extent_buffer(wc->log_leaf, &existing, (unsigned long)item,  					 sizeof(existing)) == 0) { -			btrfs_release_path(path); +			btrfs_release_path(wc->subvol_path);  			goto out;  		}  	} -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	/* drop any overlapping extents */  	drop_args.start = start;  	drop_args.end = extent_end;  	drop_args.drop_cache = true; -	drop_args.path = path; +	drop_args.path = wc->subvol_path;  	ret = btrfs_drop_extents(trans, root, inode, &drop_args);  	if (ret) {  		btrfs_abort_transaction(trans, ret); @@ -746,7 +749,7 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path  	if (found_type == BTRFS_FILE_EXTENT_INLINE) {  		/* inline extents are easy, we just overwrite them */ -		ret = overwrite_item(wc, path); +		ret = overwrite_item(wc);  		if (ret)  			goto out;  		goto update_inode; @@ -762,13 +765,15 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path  	    btrfs_fs_incompat(fs_info, NO_HOLES))  		goto update_inode; -	ret = btrfs_insert_empty_item(trans, root, path, &wc->log_key, sizeof(*item)); +	ret = btrfs_insert_empty_item(trans, root, wc->subvol_path, +				      &wc->log_key, sizeof(*item));  	if (ret) {  		btrfs_abort_transaction(trans, ret);  		goto out;  	} -	dest_offset = btrfs_item_ptr_offset(path->nodes[0], path->slots[0]); -	copy_extent_buffer(path->nodes[0], wc->log_leaf, dest_offset, +	dest_offset = btrfs_item_ptr_offset(wc->subvol_path->nodes[0], +					    wc->subvol_path->slots[0]); +	copy_extent_buffer(wc->subvol_path->nodes[0], wc->log_leaf, dest_offset,  			   (unsigned long)item, sizeof(*item));  	/* @@ -778,7 +783,7 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path  	 * update the inode item.  	 */  	if (btrfs_file_extent_disk_bytenr(wc->log_leaf, item) == 0) { -		btrfs_release_path(path); +		btrfs_release_path(wc->subvol_path);  		goto update_inode;  	} @@ -833,7 +838,7 @@ static noinline int replay_one_extent(struct walk_control *wc, struct btrfs_path  		}  	} -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	if (btrfs_file_extent_compression(wc->log_leaf, item)) {  		csum_start = ins.objectid; @@ -967,7 +972,6 @@ static int unlink_inode_for_log_replay(struct walk_control *wc,   * item   */  static noinline int drop_one_dir_item(struct walk_control *wc, -				      struct btrfs_path *path,  				      struct btrfs_inode *dir,  				      struct btrfs_dir_item *di)  { @@ -975,12 +979,10 @@ static noinline int drop_one_dir_item(struct walk_control *wc,  	struct btrfs_root *root = dir->root;  	struct btrfs_inode *inode;  	struct fscrypt_str name; -	struct extent_buffer *leaf; +	struct extent_buffer *leaf = wc->subvol_path->nodes[0];  	struct btrfs_key location;  	int ret; -	leaf = path->nodes[0]; -  	btrfs_dir_item_key_to_cpu(leaf, di, &location);  	ret = read_alloc_one_name(leaf, di + 1, btrfs_dir_name_len(leaf, di), &name);  	if (ret) { @@ -988,7 +990,7 @@ static noinline int drop_one_dir_item(struct walk_control *wc,  		return ret;  	} -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	inode = btrfs_iget_logging(location.objectid, root);  	if (IS_ERR(inode)) { @@ -998,7 +1000,7 @@ static noinline int drop_one_dir_item(struct walk_control *wc,  		goto out;  	} -	ret = link_to_fixup_dir(wc, path, location.objectid); +	ret = link_to_fixup_dir(wc, location.objectid);  	if (ret)  		goto out; @@ -1097,13 +1099,12 @@ out:  }  static int unlink_refs_not_in_log(struct walk_control *wc, -				  struct btrfs_path *path,  				  struct btrfs_key *search_key,  				  struct btrfs_inode *dir,  				  struct btrfs_inode *inode,  				  u64 parent_objectid)  { -	struct extent_buffer *leaf = path->nodes[0]; +	struct extent_buffer *leaf = wc->subvol_path->nodes[0];  	unsigned long ptr;  	unsigned long ptr_end; @@ -1112,8 +1113,8 @@ static int unlink_refs_not_in_log(struct walk_control *wc,  	 * log. If so, we allow them to stay otherwise they must be unlinked as  	 * a conflict.  	 */ -	ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); -	ptr_end = ptr + btrfs_item_size(leaf, path->slots[0]); +	ptr = btrfs_item_ptr_offset(leaf, wc->subvol_path->slots[0]); +	ptr_end = ptr + btrfs_item_size(leaf, wc->subvol_path->slots[0]);  	while (ptr < ptr_end) {  		struct btrfs_trans_handle *trans = wc->trans;  		struct fscrypt_str victim_name; @@ -1141,7 +1142,7 @@ static int unlink_refs_not_in_log(struct walk_control *wc,  		}  		inc_nlink(&inode->vfs_inode); -		btrfs_release_path(path); +		btrfs_release_path(wc->subvol_path);  		ret = unlink_inode_for_log_replay(wc, dir, inode, &victim_name);  		kfree(victim_name.name); @@ -1154,15 +1155,14 @@ static int unlink_refs_not_in_log(struct walk_control *wc,  }  static int unlink_extrefs_not_in_log(struct walk_control *wc, -				     struct btrfs_path *path,  				     struct btrfs_key *search_key,  				     struct btrfs_inode *inode,  				     u64 inode_objectid,  				     u64 parent_objectid)  { -	struct extent_buffer *leaf = path->nodes[0]; -	const unsigned long base = btrfs_item_ptr_offset(leaf, path->slots[0]); -	const u32 item_size = btrfs_item_size(leaf, path->slots[0]); +	struct extent_buffer *leaf = wc->subvol_path->nodes[0]; +	const unsigned long base = btrfs_item_ptr_offset(leaf, wc->subvol_path->slots[0]); +	const u32 item_size = btrfs_item_size(leaf, wc->subvol_path->slots[0]);  	u32 cur_offset = 0;  	while (cur_offset < item_size) { @@ -1213,7 +1213,7 @@ next:  		}  		inc_nlink(&inode->vfs_inode); -		btrfs_release_path(path); +		btrfs_release_path(wc->subvol_path);  		ret = unlink_inode_for_log_replay(wc, victim_parent, inode,  						  &victim_name); @@ -1228,7 +1228,6 @@ next:  }  static inline int __add_inode_ref(struct walk_control *wc, -				  struct btrfs_path *path,  				  struct btrfs_inode *dir,  				  struct btrfs_inode *inode,  				  u64 inode_objectid, u64 parent_objectid, @@ -1246,7 +1245,7 @@ again:  	search_key.objectid = inode_objectid;  	search_key.type = BTRFS_INODE_REF_KEY;  	search_key.offset = parent_objectid; -	ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0); +	ret = btrfs_search_slot(NULL, root, &search_key, wc->subvol_path, 0, 0);  	if (ret < 0) {  		btrfs_abort_transaction(trans, ret);  		return ret; @@ -1258,53 +1257,54 @@ again:  		if (search_key.objectid == search_key.offset)  			return 1; -		ret = unlink_refs_not_in_log(wc, path, &search_key, dir, inode, +		ret = unlink_refs_not_in_log(wc, &search_key, dir, inode,  					     parent_objectid);  		if (ret == -EAGAIN)  			goto again;  		else if (ret)  			return ret;  	} -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	/* Same search but for extended refs */ -	extref = btrfs_lookup_inode_extref(root, path, name, inode_objectid, parent_objectid); +	extref = btrfs_lookup_inode_extref(root, wc->subvol_path, name, +					   inode_objectid, parent_objectid);  	if (IS_ERR(extref)) {  		return PTR_ERR(extref);  	} else if (extref) { -		ret = unlink_extrefs_not_in_log(wc, path, &search_key, inode, +		ret = unlink_extrefs_not_in_log(wc, &search_key, inode,  						inode_objectid, parent_objectid);  		if (ret == -EAGAIN)  			goto again;  		else if (ret)  			return ret;  	} -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	/* look for a conflicting sequence number */ -	di = btrfs_lookup_dir_index_item(trans, root, path, btrfs_ino(dir), +	di = btrfs_lookup_dir_index_item(trans, root, wc->subvol_path, btrfs_ino(dir),  					 ref_index, name, 0);  	if (IS_ERR(di)) {  		ret = PTR_ERR(di);  		btrfs_abort_transaction(trans, ret);  		return ret;  	} else if (di) { -		ret = drop_one_dir_item(wc, path, dir, di); +		ret = drop_one_dir_item(wc, dir, di);  		if (ret)  			return ret;  	} -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	/* look for a conflicting name */ -	di = btrfs_lookup_dir_item(trans, root, path, btrfs_ino(dir), name, 0); +	di = btrfs_lookup_dir_item(trans, root, wc->subvol_path, btrfs_ino(dir), name, 0);  	if (IS_ERR(di)) {  		return PTR_ERR(di);  	} else if (di) { -		ret = drop_one_dir_item(wc, path, dir, di); +		ret = drop_one_dir_item(wc, dir, di);  		if (ret)  			return ret;  	} -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	return 0;  } @@ -1357,9 +1357,7 @@ static int ref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,   * proper unlink of that name (that is, remove its entry from the inode   * reference item and both dir index keys).   */ -static int unlink_old_inode_refs(struct walk_control *wc, -				 struct btrfs_path *path, -				 struct btrfs_inode *inode) +static int unlink_old_inode_refs(struct walk_control *wc, struct btrfs_inode *inode)  {  	struct btrfs_trans_handle *trans = wc->trans;  	struct btrfs_root *root = wc->root; @@ -1369,8 +1367,8 @@ static int unlink_old_inode_refs(struct walk_control *wc,  	struct extent_buffer *eb;  again: -	btrfs_release_path(path); -	ret = btrfs_search_slot(NULL, root, &wc->log_key, path, 0, 0); +	btrfs_release_path(wc->subvol_path); +	ret = btrfs_search_slot(NULL, root, &wc->log_key, wc->subvol_path, 0, 0);  	if (ret > 0) {  		ret = 0;  		goto out; @@ -1380,9 +1378,9 @@ again:  		goto out;  	} -	eb = path->nodes[0]; -	ref_ptr = btrfs_item_ptr_offset(eb, path->slots[0]); -	ref_end = ref_ptr + btrfs_item_size(eb, path->slots[0]); +	eb = wc->subvol_path->nodes[0]; +	ref_ptr = btrfs_item_ptr_offset(eb, wc->subvol_path->slots[0]); +	ref_end = ref_ptr + btrfs_item_size(eb, wc->subvol_path->slots[0]);  	while (ref_ptr < ref_end) {  		struct fscrypt_str name;  		u64 parent_id; @@ -1413,7 +1411,7 @@ again:  		if (!ret) {  			struct btrfs_inode *dir; -			btrfs_release_path(path); +			btrfs_release_path(wc->subvol_path);  			dir = btrfs_iget_logging(parent_id, root);  			if (IS_ERR(dir)) {  				ret = PTR_ERR(dir); @@ -1438,7 +1436,7 @@ again:  	}  	ret = 0;   out: -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	return ret;  } @@ -1446,7 +1444,7 @@ again:   * Replay one inode back reference item found in the log tree.   * Path is for temporary use by this function (it should be released on return).   */ -static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *path) +static noinline int add_inode_ref(struct walk_control *wc)  {  	struct btrfs_trans_handle *trans = wc->trans;  	struct btrfs_root *root = wc->root; @@ -1548,8 +1546,8 @@ static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *pa  			}  		} -		ret = inode_in_dir(root, path, btrfs_ino(dir), btrfs_ino(inode), -				   ref_index, &name); +		ret = inode_in_dir(root, wc->subvol_path, btrfs_ino(dir), +				   btrfs_ino(inode), ref_index, &name);  		if (ret < 0) {  			btrfs_abort_transaction(trans, ret);  			goto out; @@ -1561,7 +1559,7 @@ static noinline int add_inode_ref(struct walk_control *wc, struct btrfs_path *pa  			 * overwrite any existing back reference, and we don't  			 * want to create dangling pointers in the directory.  			 */ -			ret = __add_inode_ref(wc, path, dir, inode, inode_objectid, +			ret = __add_inode_ref(wc, dir, inode, inode_objectid,  					      parent_objectid, ref_index, &name);  			if (ret) {  				if (ret == 1) @@ -1602,14 +1600,14 @@ next:  	 * dir index entries exist for a name but there is no inode reference  	 * item with the same name.  	 */ -	ret = unlink_old_inode_refs(wc, path, inode); +	ret = unlink_old_inode_refs(wc, inode);  	if (ret)  		goto out;  	/* finally write the back reference in the inode */ -	ret = overwrite_item(wc, path); +	ret = overwrite_item(wc);  out: -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	kfree(name.name);  	if (dir)  		iput(&dir->vfs_inode); @@ -1728,7 +1726,6 @@ process_slot:   * will free the inode.   */  static noinline int fixup_inode_link_count(struct walk_control *wc, -					   struct btrfs_path *path,  					   struct btrfs_inode *inode)  {  	struct btrfs_trans_handle *trans = wc->trans; @@ -1737,13 +1734,13 @@ static noinline int fixup_inode_link_count(struct walk_control *wc,  	u64 nlink = 0;  	const u64 ino = btrfs_ino(inode); -	ret = count_inode_refs(inode, path); +	ret = count_inode_refs(inode, wc->subvol_path);  	if (ret < 0)  		goto out;  	nlink = ret; -	ret = count_inode_extrefs(inode, path); +	ret = count_inode_extrefs(inode, wc->subvol_path);  	if (ret < 0)  		goto out; @@ -1762,7 +1759,7 @@ static noinline int fixup_inode_link_count(struct walk_control *wc,  	if (inode->vfs_inode.i_nlink == 0) {  		if (S_ISDIR(inode->vfs_inode.i_mode)) { -			ret = replay_dir_deletes(wc, path, ino, true); +			ret = replay_dir_deletes(wc, ino, true);  			if (ret)  				goto out;  		} @@ -1772,12 +1769,11 @@ static noinline int fixup_inode_link_count(struct walk_control *wc,  	}  out: -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	return ret;  } -static noinline int fixup_inode_link_counts(struct walk_control *wc, -					    struct btrfs_path *path) +static noinline int fixup_inode_link_counts(struct walk_control *wc)  {  	int ret;  	struct btrfs_key key; @@ -1790,34 +1786,34 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,  		struct btrfs_root *root = wc->root;  		struct btrfs_inode *inode; -		ret = btrfs_search_slot(trans, root, &key, path, -1, 1); +		ret = btrfs_search_slot(trans, root, &key, wc->subvol_path, -1, 1);  		if (ret < 0)  			break;  		if (ret == 1) {  			ret = 0; -			if (path->slots[0] == 0) +			if (wc->subvol_path->slots[0] == 0)  				break; -			path->slots[0]--; +			wc->subvol_path->slots[0]--;  		} -		btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); +		btrfs_item_key_to_cpu(wc->subvol_path->nodes[0], &key, wc->subvol_path->slots[0]);  		if (key.objectid != BTRFS_TREE_LOG_FIXUP_OBJECTID ||  		    key.type != BTRFS_ORPHAN_ITEM_KEY)  			break; -		ret = btrfs_del_item(trans, root, path); +		ret = btrfs_del_item(trans, root, wc->subvol_path);  		if (ret)  			break; -		btrfs_release_path(path); +		btrfs_release_path(wc->subvol_path);  		inode = btrfs_iget_logging(key.offset, root);  		if (IS_ERR(inode)) {  			ret = PTR_ERR(inode);  			break;  		} -		ret = fixup_inode_link_count(wc, path, inode); +		ret = fixup_inode_link_count(wc, inode);  		iput(&inode->vfs_inode);  		if (ret)  			break; @@ -1829,7 +1825,7 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,  		 */  		key.offset = (u64)-1;  	} -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	return ret;  } @@ -1839,9 +1835,7 @@ static noinline int fixup_inode_link_counts(struct walk_control *wc,   * count when replay is done.  The link count is incremented here   * so the inode won't go away until we check it   */ -static noinline int link_to_fixup_dir(struct walk_control *wc, -				      struct btrfs_path *path, -				      u64 objectid) +static noinline int link_to_fixup_dir(struct walk_control *wc, u64 objectid)  {  	struct btrfs_trans_handle *trans = wc->trans;  	struct btrfs_root *root = wc->root; @@ -1862,9 +1856,9 @@ static noinline int link_to_fixup_dir(struct walk_control *wc,  	key.type = BTRFS_ORPHAN_ITEM_KEY;  	key.offset = objectid; -	ret = btrfs_insert_empty_item(trans, root, path, &key, 0); +	ret = btrfs_insert_empty_item(trans, root, wc->subvol_path, &key, 0); -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	if (ret == 0) {  		if (!vfs_inode->i_nlink)  			set_nlink(vfs_inode, 1); @@ -1917,7 +1911,6 @@ static noinline int insert_one_name(struct btrfs_trans_handle *trans,  static int delete_conflicting_dir_entry(struct walk_control *wc,  					struct btrfs_inode *dir, -					struct btrfs_path *path,  					struct btrfs_dir_item *dst_di,  					const struct btrfs_key *log_key,  					u8 log_flags, @@ -1925,12 +1918,12 @@ static int delete_conflicting_dir_entry(struct walk_control *wc,  {  	struct btrfs_key found_key; -	btrfs_dir_item_key_to_cpu(path->nodes[0], dst_di, &found_key); +	btrfs_dir_item_key_to_cpu(wc->subvol_path->nodes[0], dst_di, &found_key);  	/* The existing dentry points to the same inode, don't delete it. */  	if (found_key.objectid == log_key->objectid &&  	    found_key.type == log_key->type &&  	    found_key.offset == log_key->offset && -	    btrfs_dir_flags(path->nodes[0], dst_di) == log_flags) +	    btrfs_dir_flags(wc->subvol_path->nodes[0], dst_di) == log_flags)  		return 1;  	/* @@ -1940,7 +1933,7 @@ static int delete_conflicting_dir_entry(struct walk_control *wc,  	if (!exists)  		return 0; -	return drop_one_dir_item(wc, path, dir, dst_di); +	return drop_one_dir_item(wc, dir, dst_di);  }  /* @@ -1959,9 +1952,7 @@ static int delete_conflicting_dir_entry(struct walk_control *wc,   * Returns < 0 on error, 0 if the name wasn't replayed (dentry points to a   * non-existing inode) and 1 if the name was replayed.   */ -static noinline int replay_one_name(struct walk_control *wc, -				    struct btrfs_path *path, -				    struct btrfs_dir_item *di) +static noinline int replay_one_name(struct walk_control *wc, struct btrfs_dir_item *di)  {  	struct btrfs_trans_handle *trans = wc->trans;  	struct btrfs_root *root = wc->root; @@ -1995,8 +1986,8 @@ static noinline int replay_one_name(struct walk_control *wc,  	log_flags = btrfs_dir_flags(wc->log_leaf, di);  	btrfs_dir_item_key_to_cpu(wc->log_leaf, di, &log_key); -	ret = btrfs_lookup_inode(trans, root, path, &log_key, 0); -	btrfs_release_path(path); +	ret = btrfs_lookup_inode(trans, root, wc->subvol_path, &log_key, 0); +	btrfs_release_path(wc->subvol_path);  	if (ret < 0) {  		btrfs_abort_transaction(trans, ret);  		goto out; @@ -2004,14 +1995,14 @@ static noinline int replay_one_name(struct walk_control *wc,  	exists = (ret == 0);  	ret = 0; -	dir_dst_di = btrfs_lookup_dir_item(trans, root, path, wc->log_key.objectid, -					   &name, 1); +	dir_dst_di = btrfs_lookup_dir_item(trans, root, wc->subvol_path, +					   wc->log_key.objectid, &name, 1);  	if (IS_ERR(dir_dst_di)) {  		ret = PTR_ERR(dir_dst_di);  		btrfs_abort_transaction(trans, ret);  		goto out;  	} else if (dir_dst_di) { -		ret = delete_conflicting_dir_entry(wc, dir, path, dir_dst_di, +		ret = delete_conflicting_dir_entry(wc, dir, dir_dst_di,  						   &log_key, log_flags, exists);  		if (ret < 0) {  			btrfs_abort_transaction(trans, ret); @@ -2020,9 +2011,9 @@ static noinline int replay_one_name(struct walk_control *wc,  		dir_dst_matches = (ret == 1);  	} -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path); -	index_dst_di = btrfs_lookup_dir_index_item(trans, root, path, +	index_dst_di = btrfs_lookup_dir_index_item(trans, root, wc->subvol_path,  						   wc->log_key.objectid,  						   wc->log_key.offset, &name, 1);  	if (IS_ERR(index_dst_di)) { @@ -2030,7 +2021,7 @@ static noinline int replay_one_name(struct walk_control *wc,  		btrfs_abort_transaction(trans, ret);  		goto out;  	} else if (index_dst_di) { -		ret = delete_conflicting_dir_entry(wc, dir, path, index_dst_di, +		ret = delete_conflicting_dir_entry(wc, dir, index_dst_di,  						   &log_key, log_flags, exists);  		if (ret < 0) {  			btrfs_abort_transaction(trans, ret); @@ -2039,7 +2030,7 @@ static noinline int replay_one_name(struct walk_control *wc,  		index_dst_matches = (ret == 1);  	} -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	if (dir_dst_matches && index_dst_matches) {  		ret = 0; @@ -2104,8 +2095,7 @@ out:  }  /* Replay one dir item from a BTRFS_DIR_INDEX_KEY key. */ -static noinline int replay_one_dir_item(struct walk_control *wc, -					struct btrfs_path *path) +static noinline int replay_one_dir_item(struct walk_control *wc)  {  	int ret;  	struct btrfs_dir_item *di; @@ -2114,7 +2104,7 @@ static noinline int replay_one_dir_item(struct walk_control *wc,  	ASSERT(wc->log_key.type == BTRFS_DIR_INDEX_KEY);  	di = btrfs_item_ptr(wc->log_leaf, wc->log_slot, struct btrfs_dir_item); -	ret = replay_one_name(wc, path, di); +	ret = replay_one_name(wc, di);  	if (ret < 0)  		return ret; @@ -2148,7 +2138,7 @@ static noinline int replay_one_dir_item(struct walk_control *wc,  		struct btrfs_key di_key;  		btrfs_dir_item_key_to_cpu(wc->log_leaf, di, &di_key); -		ret = link_to_fixup_dir(wc, path, di_key.objectid); +		ret = link_to_fixup_dir(wc, di_key.objectid);  	}  	return ret; @@ -2242,7 +2232,6 @@ out:   * to is unlinked   */  static noinline int check_item_in_log(struct walk_control *wc, -				      struct btrfs_path *path,  				      struct btrfs_path *log_path,  				      struct btrfs_inode *dir,  				      struct btrfs_key *dir_key, @@ -2266,8 +2255,8 @@ static noinline int check_item_in_log(struct walk_control *wc,  	 */  	ASSERT(dir_key->type == BTRFS_DIR_INDEX_KEY); -	eb = path->nodes[0]; -	slot = path->slots[0]; +	eb = wc->subvol_path->nodes[0]; +	slot = wc->subvol_path->slots[0];  	di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);  	ret = read_alloc_one_name(eb, di + 1, btrfs_dir_name_len(eb, di), &name);  	if (ret) { @@ -2293,7 +2282,7 @@ static noinline int check_item_in_log(struct walk_control *wc,  	}  	btrfs_dir_item_key_to_cpu(eb, di, &location); -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	btrfs_release_path(log_path);  	inode = btrfs_iget_logging(location.objectid, root);  	if (IS_ERR(inode)) { @@ -2303,7 +2292,7 @@ static noinline int check_item_in_log(struct walk_control *wc,  		goto out;  	} -	ret = link_to_fixup_dir(wc, path, location.objectid); +	ret = link_to_fixup_dir(wc, location.objectid);  	if (ret)  		goto out; @@ -2315,7 +2304,7 @@ static noinline int check_item_in_log(struct walk_control *wc,  	 * (an index number), so we're done.  	 */  out: -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	btrfs_release_path(log_path);  	kfree(name.name);  	if (inode) @@ -2323,7 +2312,7 @@ out:  	return ret;  } -static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path) +static int replay_xattr_deletes(struct walk_control *wc)  {  	struct btrfs_trans_handle *trans = wc->trans;  	struct btrfs_root *root = wc->root; @@ -2331,7 +2320,6 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path  	struct btrfs_key search_key;  	struct btrfs_path *log_path;  	const u64 ino = wc->log_key.objectid; -	int i;  	int nritems;  	int ret; @@ -2345,32 +2333,32 @@ static int replay_xattr_deletes(struct walk_control *wc, struct btrfs_path *path  	search_key.type = BTRFS_XATTR_ITEM_KEY;  	search_key.offset = 0;  again: -	ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0); +	ret = btrfs_search_slot(NULL, root, &search_key, wc->subvol_path, 0, 0);  	if (ret < 0) {  		btrfs_abort_transaction(trans, ret);  		goto out;  	}  process_leaf: -	nritems = btrfs_header_nritems(path->nodes[0]); -	for (i = path->slots[0]; i < nritems; i++) { +	nritems = btrfs_header_nritems(wc->subvol_path->nodes[0]); +	for (int i = wc->subvol_path->slots[0]; i < nritems; i++) {  		struct btrfs_key key;  		struct btrfs_dir_item *di;  		struct btrfs_dir_item *log_di;  		u32 total_size;  		u32 cur; -		btrfs_item_key_to_cpu(path->nodes[0], &key, i); +		btrfs_item_key_to_cpu(wc->subvol_path->nodes[0], &key, i);  		if (key.objectid != ino || key.type != BTRFS_XATTR_ITEM_KEY) {  			ret = 0;  			goto out;  		} -		di = btrfs_item_ptr(path->nodes[0], i, struct btrfs_dir_item); -		total_size = btrfs_item_size(path->nodes[0], i); +		di = btrfs_item_ptr(wc->subvol_path->nodes[0], i, struct btrfs_dir_item); +		total_size = btrfs_item_size(wc->subvol_path->nodes[0], i);  		cur = 0;  		while (cur < total_size) { -			u16 name_len = btrfs_dir_name_len(path->nodes[0], di); -			u16 data_len = btrfs_dir_data_len(path->nodes[0], di); +			u16 name_len = btrfs_dir_name_len(wc->subvol_path->nodes[0], di); +			u16 data_len = btrfs_dir_data_len(wc->subvol_path->nodes[0], di);  			u32 this_len = sizeof(*di) + name_len + data_len;  			char *name; @@ -2380,7 +2368,7 @@ process_leaf:  				btrfs_abort_transaction(trans, ret);  				goto out;  			} -			read_extent_buffer(path->nodes[0], name, +			read_extent_buffer(wc->subvol_path->nodes[0], name,  					   (unsigned long)(di + 1), name_len);  			log_di = btrfs_lookup_xattr(NULL, log, log_path, ino, @@ -2388,8 +2376,8 @@ process_leaf:  			btrfs_release_path(log_path);  			if (!log_di) {  				/* Doesn't exist in log tree, so delete it. */ -				btrfs_release_path(path); -				di = btrfs_lookup_xattr(trans, root, path, ino, +				btrfs_release_path(wc->subvol_path); +				di = btrfs_lookup_xattr(trans, root, wc->subvol_path, ino,  							name, name_len, -1);  				kfree(name);  				if (IS_ERR(di)) { @@ -2399,12 +2387,12 @@ process_leaf:  				}  				ASSERT(di);  				ret = btrfs_delete_one_dir_name(trans, root, -								path, di); +								wc->subvol_path, di);  				if (ret) {  					btrfs_abort_transaction(trans, ret);  					goto out;  				} -				btrfs_release_path(path); +				btrfs_release_path(wc->subvol_path);  				search_key = key;  				goto again;  			} @@ -2418,7 +2406,7 @@ process_leaf:  			di = (struct btrfs_dir_item *)((char *)di + this_len);  		}  	} -	ret = btrfs_next_leaf(root, path); +	ret = btrfs_next_leaf(root, wc->subvol_path);  	if (ret > 0)  		ret = 0;  	else if (ret == 0) @@ -2427,7 +2415,7 @@ process_leaf:  		btrfs_abort_transaction(trans, ret);  out:  	btrfs_free_path(log_path); -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	return ret;  } @@ -2443,7 +2431,6 @@ out:   * directory.   */  static noinline int replay_dir_deletes(struct walk_control *wc, -				       struct btrfs_path *path,  				       u64 dirid, bool del_all)  {  	struct btrfs_trans_handle *trans = wc->trans; @@ -2486,7 +2473,7 @@ static noinline int replay_dir_deletes(struct walk_control *wc,  		if (del_all)  			range_end = (u64)-1;  		else { -			ret = find_dir_range(log, path, dirid, +			ret = find_dir_range(log, wc->subvol_path, dirid,  					     &range_start, &range_end);  			if (ret < 0) {  				btrfs_abort_transaction(trans, ret); @@ -2499,16 +2486,16 @@ static noinline int replay_dir_deletes(struct walk_control *wc,  		dir_key.offset = range_start;  		while (1) {  			int nritems; -			ret = btrfs_search_slot(NULL, root, &dir_key, path, -						0, 0); +			ret = btrfs_search_slot(NULL, root, &dir_key, +						wc->subvol_path, 0, 0);  			if (ret < 0) {  				btrfs_abort_transaction(trans, ret);  				goto out;  			} -			nritems = btrfs_header_nritems(path->nodes[0]); -			if (path->slots[0] >= nritems) { -				ret = btrfs_next_leaf(root, path); +			nritems = btrfs_header_nritems(wc->subvol_path->nodes[0]); +			if (wc->subvol_path->slots[0] >= nritems) { +				ret = btrfs_next_leaf(root, wc->subvol_path);  				if (ret == 1) {  					break;  				} else if (ret < 0) { @@ -2516,8 +2503,8 @@ static noinline int replay_dir_deletes(struct walk_control *wc,  					goto out;  				}  			} -			btrfs_item_key_to_cpu(path->nodes[0], &found_key, -					      path->slots[0]); +			btrfs_item_key_to_cpu(wc->subvol_path->nodes[0], &found_key, +					      wc->subvol_path->slots[0]);  			if (found_key.objectid != dirid ||  			    found_key.type != dir_key.type) {  				ret = 0; @@ -2527,22 +2514,21 @@ static noinline int replay_dir_deletes(struct walk_control *wc,  			if (found_key.offset > range_end)  				break; -			ret = check_item_in_log(wc, path, log_path, dir, -						&found_key, del_all); +			ret = check_item_in_log(wc, log_path, dir, &found_key, del_all);  			if (ret)  				goto out;  			if (found_key.offset == (u64)-1)  				break;  			dir_key.offset = found_key.offset + 1;  		} -		btrfs_release_path(path); +		btrfs_release_path(wc->subvol_path);  		if (range_end == (u64)-1)  			break;  		range_start = range_end + 1;  	}  	ret = 0;  out: -	btrfs_release_path(path); +	btrfs_release_path(wc->subvol_path);  	btrfs_free_path(log_path);  	iput(&dir->vfs_inode);  	return ret; @@ -2567,7 +2553,6 @@ static int replay_one_buffer(struct extent_buffer *eb,  		.transid = gen,  		.level = level  	}; -	struct btrfs_path *path;  	struct btrfs_root *root = wc->root;  	struct btrfs_trans_handle *trans = wc->trans;  	int ret; @@ -2581,8 +2566,9 @@ static int replay_one_buffer(struct extent_buffer *eb,  		return ret;  	} -	path = btrfs_alloc_path(); -	if (!path) { +	ASSERT(wc->subvol_path == NULL); +	wc->subvol_path = btrfs_alloc_path(); +	if (!wc->subvol_path) {  		btrfs_abort_transaction(trans, -ENOMEM);  		return -ENOMEM;  	} @@ -2630,16 +2616,16 @@ static int replay_one_buffer(struct extent_buffer *eb,  		    wc->stage == LOG_WALK_REPLAY_INODES) {  			u32 mode; -			ret = replay_xattr_deletes(wc, path); +			ret = replay_xattr_deletes(wc);  			if (ret)  				break;  			mode = btrfs_inode_mode(eb, inode_item);  			if (S_ISDIR(mode)) { -				ret = replay_dir_deletes(wc, path, wc->log_key.objectid, false); +				ret = replay_dir_deletes(wc, wc->log_key.objectid, false);  				if (ret)  					break;  			} -			ret = overwrite_item(wc, path); +			ret = overwrite_item(wc);  			if (ret)  				break; @@ -2667,7 +2653,7 @@ static int replay_one_buffer(struct extent_buffer *eb,  				drop_args.start = from;  				drop_args.end = (u64)-1;  				drop_args.drop_cache = true; -				drop_args.path = path; +				drop_args.path = wc->subvol_path;  				ret = btrfs_drop_extents(trans, root, inode,  &drop_args);  				if (ret) {  					btrfs_abort_transaction(trans, ret); @@ -2684,7 +2670,7 @@ static int replay_one_buffer(struct extent_buffer *eb,  					break;  			} -			ret = link_to_fixup_dir(wc, path, wc->log_key.objectid); +			ret = link_to_fixup_dir(wc, wc->log_key.objectid);  			if (ret)  				break;  		} @@ -2694,7 +2680,7 @@ static int replay_one_buffer(struct extent_buffer *eb,  		if (wc->log_key.type == BTRFS_DIR_INDEX_KEY &&  		    wc->stage == LOG_WALK_REPLAY_DIR_INDEX) { -			ret = replay_one_dir_item(wc, path); +			ret = replay_one_dir_item(wc);  			if (ret)  				break;  		} @@ -2704,16 +2690,16 @@ static int replay_one_buffer(struct extent_buffer *eb,  		/* these keys are simply copied */  		if (wc->log_key.type == BTRFS_XATTR_ITEM_KEY) { -			ret = overwrite_item(wc, path); +			ret = overwrite_item(wc);  			if (ret)  				break;  		} else if (wc->log_key.type == BTRFS_INODE_REF_KEY ||  			   wc->log_key.type == BTRFS_INODE_EXTREF_KEY) { -			ret = add_inode_ref(wc, path); +			ret = add_inode_ref(wc);  			if (ret)  				break;  		} else if (wc->log_key.type == BTRFS_EXTENT_DATA_KEY) { -			ret = replay_one_extent(wc, path); +			ret = replay_one_extent(wc);  			if (ret)  				break;  		} @@ -2724,7 +2710,8 @@ static int replay_one_buffer(struct extent_buffer *eb,  		 * older kernel with such keys, ignore them.  		 */  	} -	btrfs_free_path(path); +	btrfs_free_path(wc->subvol_path); +	wc->subvol_path = NULL;  	return ret;  } @@ -7515,7 +7502,9 @@ again:  		if (wc.stage == LOG_WALK_REPLAY_ALL) {  			struct btrfs_root *root = wc.root; -			ret = fixup_inode_link_counts(&wc, path); +			wc.subvol_path = path; +			ret = fixup_inode_link_counts(&wc); +			wc.subvol_path = NULL;  			if (ret) {  				btrfs_abort_transaction(trans, ret);  				goto next; | 
