diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
| -rw-r--r-- | fs/btrfs/tree-log.c | 107 | 
1 files changed, 97 insertions, 10 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index d31a0c4f56be..ef9c55bc7907 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -27,6 +27,7 @@  #include "backref.h"  #include "hash.h"  #include "compression.h" +#include "qgroup.h"  /* magic values for the inode_only field in btrfs_log_inode:   * @@ -680,6 +681,21 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,  		ins.type = BTRFS_EXTENT_ITEM_KEY;  		offset = key->offset - btrfs_file_extent_offset(eb, item); +		/* +		 * Manually record dirty extent, as here we did a shallow +		 * file extent item copy and skip normal backref update, +		 * but modifying extent tree all by ourselves. +		 * So need to manually record dirty extent for qgroup, +		 * as the owner of the file extent changed from log tree +		 * (doesn't affect qgroup) to fs/file tree(affects qgroup) +		 */ +		ret = btrfs_qgroup_insert_dirty_extent(trans, root->fs_info, +				btrfs_file_extent_disk_bytenr(eb, item), +				btrfs_file_extent_disk_num_bytes(eb, item), +				GFP_NOFS); +		if (ret < 0) +			goto out; +  		if (ins.objectid > 0) {  			u64 csum_start;  			u64 csum_end; @@ -2807,7 +2823,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  	 */  	mutex_unlock(&root->log_mutex); -	btrfs_init_log_ctx(&root_log_ctx); +	btrfs_init_log_ctx(&root_log_ctx, NULL);  	mutex_lock(&log_root_tree->log_mutex);  	atomic_inc(&log_root_tree->log_batch); @@ -2851,6 +2867,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  	if (log_root_tree->log_transid_committed >= root_log_ctx.log_transid) {  		blk_finish_plug(&plug); +		list_del_init(&root_log_ctx.list);  		mutex_unlock(&log_root_tree->log_mutex);  		ret = root_log_ctx.log_ret;  		goto out; @@ -4469,7 +4486,8 @@ static int btrfs_log_trailing_hole(struct btrfs_trans_handle *trans,  static int btrfs_check_ref_name_override(struct extent_buffer *eb,  					 const int slot,  					 const struct btrfs_key *key, -					 struct inode *inode) +					 struct inode *inode, +					 u64 *other_ino)  {  	int ret;  	struct btrfs_path *search_path; @@ -4528,7 +4546,16 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb,  					   search_path, parent,  					   name, this_name_len, 0);  		if (di && !IS_ERR(di)) { -			ret = 1; +			struct btrfs_key di_key; + +			btrfs_dir_item_key_to_cpu(search_path->nodes[0], +						  di, &di_key); +			if (di_key.type == BTRFS_INODE_ITEM_KEY) { +				ret = 1; +				*other_ino = di_key.objectid; +			} else { +				ret = -EAGAIN; +			}  			goto out;  		} else if (IS_ERR(di)) {  			ret = PTR_ERR(di); @@ -4722,16 +4749,72 @@ again:  		if ((min_key.type == BTRFS_INODE_REF_KEY ||  		     min_key.type == BTRFS_INODE_EXTREF_KEY) &&  		    BTRFS_I(inode)->generation == trans->transid) { +			u64 other_ino = 0; +  			ret = btrfs_check_ref_name_override(path->nodes[0],  							    path->slots[0], -							    &min_key, inode); +							    &min_key, inode, +							    &other_ino);  			if (ret < 0) {  				err = ret;  				goto out_unlock; -			} else if (ret > 0) { -				err = 1; -				btrfs_set_log_full_commit(root->fs_info, trans); -				goto out_unlock; +			} else if (ret > 0 && ctx && +				   other_ino != btrfs_ino(ctx->inode)) { +				struct btrfs_key inode_key; +				struct inode *other_inode; + +				if (ins_nr > 0) { +					ins_nr++; +				} else { +					ins_nr = 1; +					ins_start_slot = path->slots[0]; +				} +				ret = copy_items(trans, inode, dst_path, path, +						 &last_extent, ins_start_slot, +						 ins_nr, inode_only, +						 logged_isize); +				if (ret < 0) { +					err = ret; +					goto out_unlock; +				} +				ins_nr = 0; +				btrfs_release_path(path); +				inode_key.objectid = other_ino; +				inode_key.type = BTRFS_INODE_ITEM_KEY; +				inode_key.offset = 0; +				other_inode = btrfs_iget(root->fs_info->sb, +							 &inode_key, root, +							 NULL); +				/* +				 * If the other inode that had a conflicting dir +				 * entry was deleted in the current transaction, +				 * we don't need to do more work nor fallback to +				 * a transaction commit. +				 */ +				if (IS_ERR(other_inode) && +				    PTR_ERR(other_inode) == -ENOENT) { +					goto next_key; +				} else if (IS_ERR(other_inode)) { +					err = PTR_ERR(other_inode); +					goto out_unlock; +				} +				/* +				 * We are safe logging the other inode without +				 * acquiring its i_mutex as long as we log with +				 * the LOG_INODE_EXISTS mode. We're safe against +				 * concurrent renames of the other inode as well +				 * because during a rename we pin the log and +				 * update the log with the new name before we +				 * unpin it. +				 */ +				err = btrfs_log_inode(trans, root, other_inode, +						      LOG_INODE_EXISTS, +						      0, LLONG_MAX, ctx); +				iput(other_inode); +				if (err) +					goto out_unlock; +				else +					goto next_key;  			}  		} @@ -4799,7 +4882,7 @@ next_slot:  			ins_nr = 0;  		}  		btrfs_release_path(path); - +next_key:  		if (min_key.offset < (u64)-1) {  			min_key.offset++;  		} else if (min_key.type < max_key.type) { @@ -4993,8 +5076,12 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,  		if (!parent || d_really_is_negative(parent) || sb != parent->d_sb)  			break; -		if (IS_ROOT(parent)) +		if (IS_ROOT(parent)) { +			inode = d_inode(parent); +			if (btrfs_must_commit_transaction(trans, inode)) +				ret = 1;  			break; +		}  		parent = dget_parent(parent);  		dput(old_parent);  | 
