diff options
Diffstat (limited to 'fs/btrfs/scrub.c')
| -rw-r--r-- | fs/btrfs/scrub.c | 92 | 
1 files changed, 53 insertions, 39 deletions
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 79bd479317cb..64a157becbe5 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -2126,8 +2126,7 @@ static int scrub_find_csum(struct scrub_ctx *sctx, u64 logical, u64 len,  			   u8 *csum)  {  	struct btrfs_ordered_sum *sum = NULL; -	int ret = 0; -	unsigned long i; +	unsigned long index;  	unsigned long num_sectors;  	while (!list_empty(&sctx->csum_list)) { @@ -2146,19 +2145,14 @@ static int scrub_find_csum(struct scrub_ctx *sctx, u64 logical, u64 len,  	if (!sum)  		return 0; +	index = ((u32)(logical - sum->bytenr)) / sctx->sectorsize;  	num_sectors = sum->len / sctx->sectorsize; -	for (i = 0; i < num_sectors; ++i) { -		if (sum->sums[i].bytenr == logical) { -			memcpy(csum, &sum->sums[i].sum, sctx->csum_size); -			ret = 1; -			break; -		} -	} -	if (ret && i == num_sectors - 1) { +	memcpy(csum, sum->sums + index, sctx->csum_size); +	if (index == num_sectors - 1) {  		list_del(&sum->list);  		kfree(sum);  	} -	return ret; +	return 1;  }  /* scrub extent tries to collect up to 64 kB for each bio */ @@ -2501,10 +2495,11 @@ again:  			ret = scrub_extent(sctx, extent_logical, extent_len,  					   extent_physical, extent_dev, flags,  					   generation, extent_mirror_num, -					   extent_physical); +					   extent_logical - logical + physical);  			if (ret)  				goto out; +			scrub_free_csums(sctx);  			if (extent_logical + extent_len <  			    key.objectid + bytes) {  				logical += increment; @@ -3204,16 +3199,18 @@ out:  static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)  { -	unsigned long index;  	struct scrub_copy_nocow_ctx *nocow_ctx = ctx; -	int ret = 0; +	struct btrfs_fs_info *fs_info = nocow_ctx->sctx->dev_root->fs_info;  	struct btrfs_key key; -	struct inode *inode = NULL; +	struct inode *inode; +	struct page *page;  	struct btrfs_root *local_root;  	u64 physical_for_dev_replace;  	u64 len; -	struct btrfs_fs_info *fs_info = nocow_ctx->sctx->dev_root->fs_info; +	unsigned long index;  	int srcu_index; +	int ret; +	int err;  	key.objectid = root;  	key.type = BTRFS_ROOT_ITEM_KEY; @@ -3227,6 +3224,11 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)  		return PTR_ERR(local_root);  	} +	if (btrfs_root_refs(&local_root->root_item) == 0) { +		srcu_read_unlock(&fs_info->subvol_srcu, srcu_index); +		return -ENOENT; +	} +  	key.type = BTRFS_INODE_ITEM_KEY;  	key.objectid = inum;  	key.offset = 0; @@ -3235,19 +3237,21 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)  	if (IS_ERR(inode))  		return PTR_ERR(inode); +	/* Avoid truncate/dio/punch hole.. */ +	mutex_lock(&inode->i_mutex); +	inode_dio_wait(inode); + +	ret = 0;  	physical_for_dev_replace = nocow_ctx->physical_for_dev_replace;  	len = nocow_ctx->len;  	while (len >= PAGE_CACHE_SIZE) { -		struct page *page = NULL; -		int ret_sub; -  		index = offset >> PAGE_CACHE_SHIFT; - +again:  		page = find_or_create_page(inode->i_mapping, index, GFP_NOFS);  		if (!page) {  			pr_err("find_or_create_page() failed\n");  			ret = -ENOMEM; -			goto next_page; +			goto out;  		}  		if (PageUptodate(page)) { @@ -3255,39 +3259,49 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)  				goto next_page;  		} else {  			ClearPageError(page); -			ret_sub = extent_read_full_page(&BTRFS_I(inode)-> +			err = extent_read_full_page(&BTRFS_I(inode)->  							 io_tree,  							page, btrfs_get_extent,  							nocow_ctx->mirror_num); -			if (ret_sub) { -				ret = ret_sub; +			if (err) { +				ret = err;  				goto next_page;  			} -			wait_on_page_locked(page); + +			lock_page(page); +			/* +			 * If the page has been remove from the page cache, +			 * the data on it is meaningless, because it may be +			 * old one, the new data may be written into the new +			 * page in the page cache. +			 */ +			if (page->mapping != inode->i_mapping) { +				page_cache_release(page); +				goto again; +			}  			if (!PageUptodate(page)) {  				ret = -EIO;  				goto next_page;  			}  		} -		ret_sub = write_page_nocow(nocow_ctx->sctx, -					   physical_for_dev_replace, page); -		if (ret_sub) { -			ret = ret_sub; -			goto next_page; -		} - +		err = write_page_nocow(nocow_ctx->sctx, +				       physical_for_dev_replace, page); +		if (err) +			ret = err;  next_page: -		if (page) { -			unlock_page(page); -			put_page(page); -		} +		unlock_page(page); +		page_cache_release(page); + +		if (ret) +			break; +  		offset += PAGE_CACHE_SIZE;  		physical_for_dev_replace += PAGE_CACHE_SIZE;  		len -= PAGE_CACHE_SIZE;  	} - -	if (inode) -		iput(inode); +out: +	mutex_unlock(&inode->i_mutex); +	iput(inode);  	return ret;  }  | 
