diff options
Diffstat (limited to 'fs/btrfs/scrub.c')
| -rw-r--r-- | fs/btrfs/scrub.c | 90 |
1 files changed, 52 insertions, 38 deletions
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 79bd479317cb..4ba2a69a60ad 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, | |||
| 2126 | u8 *csum) | 2126 | u8 *csum) |
| 2127 | { | 2127 | { |
| 2128 | struct btrfs_ordered_sum *sum = NULL; | 2128 | struct btrfs_ordered_sum *sum = NULL; |
| 2129 | int ret = 0; | 2129 | unsigned long index; |
| 2130 | unsigned long i; | ||
| 2131 | unsigned long num_sectors; | 2130 | unsigned long num_sectors; |
| 2132 | 2131 | ||
| 2133 | while (!list_empty(&sctx->csum_list)) { | 2132 | while (!list_empty(&sctx->csum_list)) { |
| @@ -2146,19 +2145,14 @@ static int scrub_find_csum(struct scrub_ctx *sctx, u64 logical, u64 len, | |||
| 2146 | if (!sum) | 2145 | if (!sum) |
| 2147 | return 0; | 2146 | return 0; |
| 2148 | 2147 | ||
| 2148 | index = ((u32)(logical - sum->bytenr)) / sctx->sectorsize; | ||
| 2149 | num_sectors = sum->len / sctx->sectorsize; | 2149 | num_sectors = sum->len / sctx->sectorsize; |
| 2150 | for (i = 0; i < num_sectors; ++i) { | 2150 | memcpy(csum, sum->sums + index, sctx->csum_size); |
| 2151 | if (sum->sums[i].bytenr == logical) { | 2151 | if (index == num_sectors - 1) { |
| 2152 | memcpy(csum, &sum->sums[i].sum, sctx->csum_size); | ||
| 2153 | ret = 1; | ||
| 2154 | break; | ||
| 2155 | } | ||
| 2156 | } | ||
| 2157 | if (ret && i == num_sectors - 1) { | ||
| 2158 | list_del(&sum->list); | 2152 | list_del(&sum->list); |
| 2159 | kfree(sum); | 2153 | kfree(sum); |
| 2160 | } | 2154 | } |
| 2161 | return ret; | 2155 | return 1; |
| 2162 | } | 2156 | } |
| 2163 | 2157 | ||
| 2164 | /* scrub extent tries to collect up to 64 kB for each bio */ | 2158 | /* scrub extent tries to collect up to 64 kB for each bio */ |
| @@ -2505,6 +2499,7 @@ again: | |||
| 2505 | if (ret) | 2499 | if (ret) |
| 2506 | goto out; | 2500 | goto out; |
| 2507 | 2501 | ||
| 2502 | scrub_free_csums(sctx); | ||
| 2508 | if (extent_logical + extent_len < | 2503 | if (extent_logical + extent_len < |
| 2509 | key.objectid + bytes) { | 2504 | key.objectid + bytes) { |
| 2510 | logical += increment; | 2505 | logical += increment; |
| @@ -3204,16 +3199,18 @@ out: | |||
| 3204 | 3199 | ||
| 3205 | static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) | 3200 | static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) |
| 3206 | { | 3201 | { |
| 3207 | unsigned long index; | ||
| 3208 | struct scrub_copy_nocow_ctx *nocow_ctx = ctx; | 3202 | struct scrub_copy_nocow_ctx *nocow_ctx = ctx; |
| 3209 | int ret = 0; | 3203 | struct btrfs_fs_info *fs_info = nocow_ctx->sctx->dev_root->fs_info; |
| 3210 | struct btrfs_key key; | 3204 | struct btrfs_key key; |
| 3211 | struct inode *inode = NULL; | 3205 | struct inode *inode; |
| 3206 | struct page *page; | ||
| 3212 | struct btrfs_root *local_root; | 3207 | struct btrfs_root *local_root; |
| 3213 | u64 physical_for_dev_replace; | 3208 | u64 physical_for_dev_replace; |
| 3214 | u64 len; | 3209 | u64 len; |
| 3215 | struct btrfs_fs_info *fs_info = nocow_ctx->sctx->dev_root->fs_info; | 3210 | unsigned long index; |
| 3216 | int srcu_index; | 3211 | int srcu_index; |
| 3212 | int ret; | ||
| 3213 | int err; | ||
| 3217 | 3214 | ||
| 3218 | key.objectid = root; | 3215 | key.objectid = root; |
| 3219 | key.type = BTRFS_ROOT_ITEM_KEY; | 3216 | 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) | |||
| 3227 | return PTR_ERR(local_root); | 3224 | return PTR_ERR(local_root); |
| 3228 | } | 3225 | } |
| 3229 | 3226 | ||
| 3227 | if (btrfs_root_refs(&local_root->root_item) == 0) { | ||
| 3228 | srcu_read_unlock(&fs_info->subvol_srcu, srcu_index); | ||
| 3229 | return -ENOENT; | ||
| 3230 | } | ||
| 3231 | |||
| 3230 | key.type = BTRFS_INODE_ITEM_KEY; | 3232 | key.type = BTRFS_INODE_ITEM_KEY; |
| 3231 | key.objectid = inum; | 3233 | key.objectid = inum; |
| 3232 | key.offset = 0; | 3234 | key.offset = 0; |
| @@ -3235,19 +3237,21 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) | |||
| 3235 | if (IS_ERR(inode)) | 3237 | if (IS_ERR(inode)) |
| 3236 | return PTR_ERR(inode); | 3238 | return PTR_ERR(inode); |
| 3237 | 3239 | ||
| 3240 | /* Avoid truncate/dio/punch hole.. */ | ||
| 3241 | mutex_lock(&inode->i_mutex); | ||
| 3242 | inode_dio_wait(inode); | ||
| 3243 | |||
| 3244 | ret = 0; | ||
| 3238 | physical_for_dev_replace = nocow_ctx->physical_for_dev_replace; | 3245 | physical_for_dev_replace = nocow_ctx->physical_for_dev_replace; |
| 3239 | len = nocow_ctx->len; | 3246 | len = nocow_ctx->len; |
| 3240 | while (len >= PAGE_CACHE_SIZE) { | 3247 | while (len >= PAGE_CACHE_SIZE) { |
| 3241 | struct page *page = NULL; | ||
| 3242 | int ret_sub; | ||
| 3243 | |||
| 3244 | index = offset >> PAGE_CACHE_SHIFT; | 3248 | index = offset >> PAGE_CACHE_SHIFT; |
| 3245 | 3249 | again: | |
| 3246 | page = find_or_create_page(inode->i_mapping, index, GFP_NOFS); | 3250 | page = find_or_create_page(inode->i_mapping, index, GFP_NOFS); |
| 3247 | if (!page) { | 3251 | if (!page) { |
| 3248 | pr_err("find_or_create_page() failed\n"); | 3252 | pr_err("find_or_create_page() failed\n"); |
| 3249 | ret = -ENOMEM; | 3253 | ret = -ENOMEM; |
| 3250 | goto next_page; | 3254 | goto out; |
| 3251 | } | 3255 | } |
| 3252 | 3256 | ||
| 3253 | if (PageUptodate(page)) { | 3257 | if (PageUptodate(page)) { |
| @@ -3255,39 +3259,49 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) | |||
| 3255 | goto next_page; | 3259 | goto next_page; |
| 3256 | } else { | 3260 | } else { |
| 3257 | ClearPageError(page); | 3261 | ClearPageError(page); |
| 3258 | ret_sub = extent_read_full_page(&BTRFS_I(inode)-> | 3262 | err = extent_read_full_page(&BTRFS_I(inode)-> |
| 3259 | io_tree, | 3263 | io_tree, |
| 3260 | page, btrfs_get_extent, | 3264 | page, btrfs_get_extent, |
| 3261 | nocow_ctx->mirror_num); | 3265 | nocow_ctx->mirror_num); |
| 3262 | if (ret_sub) { | 3266 | if (err) { |
| 3263 | ret = ret_sub; | 3267 | ret = err; |
| 3264 | goto next_page; | 3268 | goto next_page; |
| 3265 | } | 3269 | } |
| 3266 | wait_on_page_locked(page); | 3270 | |
| 3271 | lock_page(page); | ||
| 3272 | /* | ||
| 3273 | * If the page has been remove from the page cache, | ||
| 3274 | * the data on it is meaningless, because it may be | ||
| 3275 | * old one, the new data may be written into the new | ||
| 3276 | * page in the page cache. | ||
| 3277 | */ | ||
| 3278 | if (page->mapping != inode->i_mapping) { | ||
| 3279 | page_cache_release(page); | ||
| 3280 | goto again; | ||
| 3281 | } | ||
| 3267 | if (!PageUptodate(page)) { | 3282 | if (!PageUptodate(page)) { |
| 3268 | ret = -EIO; | 3283 | ret = -EIO; |
| 3269 | goto next_page; | 3284 | goto next_page; |
| 3270 | } | 3285 | } |
| 3271 | } | 3286 | } |
| 3272 | ret_sub = write_page_nocow(nocow_ctx->sctx, | 3287 | err = write_page_nocow(nocow_ctx->sctx, |
| 3273 | physical_for_dev_replace, page); | 3288 | physical_for_dev_replace, page); |
| 3274 | if (ret_sub) { | 3289 | if (err) |
| 3275 | ret = ret_sub; | 3290 | ret = err; |
| 3276 | goto next_page; | ||
| 3277 | } | ||
| 3278 | |||
| 3279 | next_page: | 3291 | next_page: |
| 3280 | if (page) { | 3292 | unlock_page(page); |
| 3281 | unlock_page(page); | 3293 | page_cache_release(page); |
| 3282 | put_page(page); | 3294 | |
| 3283 | } | 3295 | if (ret) |
| 3296 | break; | ||
| 3297 | |||
| 3284 | offset += PAGE_CACHE_SIZE; | 3298 | offset += PAGE_CACHE_SIZE; |
| 3285 | physical_for_dev_replace += PAGE_CACHE_SIZE; | 3299 | physical_for_dev_replace += PAGE_CACHE_SIZE; |
| 3286 | len -= PAGE_CACHE_SIZE; | 3300 | len -= PAGE_CACHE_SIZE; |
| 3287 | } | 3301 | } |
| 3288 | 3302 | out: | |
| 3289 | if (inode) | 3303 | mutex_unlock(&inode->i_mutex); |
| 3290 | iput(inode); | 3304 | iput(inode); |
| 3291 | return ret; | 3305 | return ret; |
| 3292 | } | 3306 | } |
| 3293 | 3307 | ||
