diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
| -rw-r--r-- | fs/btrfs/tree-log.c | 52 | 
1 files changed, 47 insertions, 5 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 6f757361db53..d3f115909ff0 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -808,7 +808,8 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,  						struct btrfs_ordered_sum,  						list);  				if (!ret) -					ret = btrfs_del_csums(trans, fs_info, +					ret = btrfs_del_csums(trans, +							      fs_info->csum_root,  							      sums->bytenr,  							      sums->len);  				if (!ret) @@ -3909,6 +3910,28 @@ static int log_inode_item(struct btrfs_trans_handle *trans,  	return 0;  } +static int log_csums(struct btrfs_trans_handle *trans, +		     struct btrfs_root *log_root, +		     struct btrfs_ordered_sum *sums) +{ +	int ret; + +	/* +	 * Due to extent cloning, we might have logged a csum item that covers a +	 * subrange of a cloned extent, and later we can end up logging a csum +	 * item for a larger subrange of the same extent or the entire range. +	 * This would leave csum items in the log tree that cover the same range +	 * and break the searches for checksums in the log tree, resulting in +	 * some checksums missing in the fs/subvolume tree. So just delete (or +	 * trim and adjust) any existing csum items in the log for this range. +	 */ +	ret = btrfs_del_csums(trans, log_root, sums->bytenr, sums->len); +	if (ret) +		return ret; + +	return btrfs_csum_file_blocks(trans, log_root, sums); +} +  static noinline int copy_items(struct btrfs_trans_handle *trans,  			       struct btrfs_inode *inode,  			       struct btrfs_path *dst_path, @@ -4054,7 +4077,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,  						   struct btrfs_ordered_sum,  						   list);  		if (!ret) -			ret = btrfs_csum_file_blocks(trans, log, sums); +			ret = log_csums(trans, log, sums);  		list_del(&sums->list);  		kfree(sums);  	} @@ -4274,7 +4297,7 @@ static int log_extent_csums(struct btrfs_trans_handle *trans,  						   struct btrfs_ordered_sum,  						   list);  		if (!ret) -			ret = btrfs_csum_file_blocks(trans, log_root, sums); +			ret = log_csums(trans, log_root, sums);  		list_del(&sums->list);  		kfree(sums);  	} @@ -6294,9 +6317,28 @@ again:  		wc.replay_dest = btrfs_read_fs_root_no_name(fs_info, &tmp_key);  		if (IS_ERR(wc.replay_dest)) {  			ret = PTR_ERR(wc.replay_dest); + +			/* +			 * We didn't find the subvol, likely because it was +			 * deleted.  This is ok, simply skip this log and go to +			 * the next one. +			 * +			 * We need to exclude the root because we can't have +			 * other log replays overwriting this log as we'll read +			 * it back in a few more times.  This will keep our +			 * block from being modified, and we'll just bail for +			 * each subsequent pass. +			 */ +			if (ret == -ENOENT) +				ret = btrfs_pin_extent_for_log_replay(fs_info, +							log->node->start, +							log->node->len);  			free_extent_buffer(log->node);  			free_extent_buffer(log->commit_root);  			kfree(log); + +			if (!ret) +				goto next;  			btrfs_handle_fs_error(fs_info, ret,  				"Couldn't read target root for tree log recovery.");  			goto error; @@ -6328,7 +6370,6 @@ again:  						  &root->highest_objectid);  		} -		key.offset = found_key.offset - 1;  		wc.replay_dest->log_root = NULL;  		free_extent_buffer(log->node);  		free_extent_buffer(log->commit_root); @@ -6336,9 +6377,10 @@ again:  		if (ret)  			goto error; - +next:  		if (found_key.offset == 0)  			break; +		key.offset = found_key.offset - 1;  	}  	btrfs_release_path(path);  | 
