diff options
Diffstat (limited to 'fs/btrfs/scrub.c')
-rw-r--r-- | fs/btrfs/scrub.c | 123 |
1 files changed, 70 insertions, 53 deletions
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 6dfed0c27ac3..df50fd1eca8f 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c | |||
@@ -117,33 +117,37 @@ static void scrub_free_csums(struct scrub_dev *sdev) | |||
117 | } | 117 | } |
118 | } | 118 | } |
119 | 119 | ||
120 | static void scrub_free_bio(struct bio *bio) | ||
121 | { | ||
122 | int i; | ||
123 | struct page *last_page = NULL; | ||
124 | |||
125 | if (!bio) | ||
126 | return; | ||
127 | |||
128 | for (i = 0; i < bio->bi_vcnt; ++i) { | ||
129 | if (bio->bi_io_vec[i].bv_page == last_page) | ||
130 | continue; | ||
131 | last_page = bio->bi_io_vec[i].bv_page; | ||
132 | __free_page(last_page); | ||
133 | } | ||
134 | bio_put(bio); | ||
135 | } | ||
136 | |||
120 | static noinline_for_stack void scrub_free_dev(struct scrub_dev *sdev) | 137 | static noinline_for_stack void scrub_free_dev(struct scrub_dev *sdev) |
121 | { | 138 | { |
122 | int i; | 139 | int i; |
123 | int j; | ||
124 | struct page *last_page; | ||
125 | 140 | ||
126 | if (!sdev) | 141 | if (!sdev) |
127 | return; | 142 | return; |
128 | 143 | ||
129 | for (i = 0; i < SCRUB_BIOS_PER_DEV; ++i) { | 144 | for (i = 0; i < SCRUB_BIOS_PER_DEV; ++i) { |
130 | struct scrub_bio *sbio = sdev->bios[i]; | 145 | struct scrub_bio *sbio = sdev->bios[i]; |
131 | struct bio *bio; | ||
132 | 146 | ||
133 | if (!sbio) | 147 | if (!sbio) |
134 | break; | 148 | break; |
135 | 149 | ||
136 | bio = sbio->bio; | 150 | scrub_free_bio(sbio->bio); |
137 | if (bio) { | ||
138 | last_page = NULL; | ||
139 | for (j = 0; j < bio->bi_vcnt; ++j) { | ||
140 | if (bio->bi_io_vec[j].bv_page == last_page) | ||
141 | continue; | ||
142 | last_page = bio->bi_io_vec[j].bv_page; | ||
143 | __free_page(last_page); | ||
144 | } | ||
145 | bio_put(bio); | ||
146 | } | ||
147 | kfree(sbio); | 151 | kfree(sbio); |
148 | } | 152 | } |
149 | 153 | ||
@@ -156,8 +160,6 @@ struct scrub_dev *scrub_setup_dev(struct btrfs_device *dev) | |||
156 | { | 160 | { |
157 | struct scrub_dev *sdev; | 161 | struct scrub_dev *sdev; |
158 | int i; | 162 | int i; |
159 | int j; | ||
160 | int ret; | ||
161 | struct btrfs_fs_info *fs_info = dev->dev_root->fs_info; | 163 | struct btrfs_fs_info *fs_info = dev->dev_root->fs_info; |
162 | 164 | ||
163 | sdev = kzalloc(sizeof(*sdev), GFP_NOFS); | 165 | sdev = kzalloc(sizeof(*sdev), GFP_NOFS); |
@@ -165,7 +167,6 @@ struct scrub_dev *scrub_setup_dev(struct btrfs_device *dev) | |||
165 | goto nomem; | 167 | goto nomem; |
166 | sdev->dev = dev; | 168 | sdev->dev = dev; |
167 | for (i = 0; i < SCRUB_BIOS_PER_DEV; ++i) { | 169 | for (i = 0; i < SCRUB_BIOS_PER_DEV; ++i) { |
168 | struct bio *bio; | ||
169 | struct scrub_bio *sbio; | 170 | struct scrub_bio *sbio; |
170 | 171 | ||
171 | sbio = kzalloc(sizeof(*sbio), GFP_NOFS); | 172 | sbio = kzalloc(sizeof(*sbio), GFP_NOFS); |
@@ -173,32 +174,10 @@ struct scrub_dev *scrub_setup_dev(struct btrfs_device *dev) | |||
173 | goto nomem; | 174 | goto nomem; |
174 | sdev->bios[i] = sbio; | 175 | sdev->bios[i] = sbio; |
175 | 176 | ||
176 | bio = bio_kmalloc(GFP_NOFS, SCRUB_PAGES_PER_BIO); | ||
177 | if (!bio) | ||
178 | goto nomem; | ||
179 | |||
180 | sbio->index = i; | 177 | sbio->index = i; |
181 | sbio->sdev = sdev; | 178 | sbio->sdev = sdev; |
182 | sbio->bio = bio; | ||
183 | sbio->count = 0; | 179 | sbio->count = 0; |
184 | sbio->work.func = scrub_checksum; | 180 | sbio->work.func = scrub_checksum; |
185 | bio->bi_private = sdev->bios[i]; | ||
186 | bio->bi_end_io = scrub_bio_end_io; | ||
187 | bio->bi_sector = 0; | ||
188 | bio->bi_bdev = dev->bdev; | ||
189 | bio->bi_size = 0; | ||
190 | |||
191 | for (j = 0; j < SCRUB_PAGES_PER_BIO; ++j) { | ||
192 | struct page *page; | ||
193 | page = alloc_page(GFP_NOFS); | ||
194 | if (!page) | ||
195 | goto nomem; | ||
196 | |||
197 | ret = bio_add_page(bio, page, PAGE_SIZE, 0); | ||
198 | if (!ret) | ||
199 | goto nomem; | ||
200 | } | ||
201 | WARN_ON(bio->bi_vcnt != SCRUB_PAGES_PER_BIO); | ||
202 | 181 | ||
203 | if (i != SCRUB_BIOS_PER_DEV-1) | 182 | if (i != SCRUB_BIOS_PER_DEV-1) |
204 | sdev->bios[i]->next_free = i + 1; | 183 | sdev->bios[i]->next_free = i + 1; |
@@ -369,9 +348,6 @@ static int scrub_fixup_io(int rw, struct block_device *bdev, sector_t sector, | |||
369 | int ret; | 348 | int ret; |
370 | DECLARE_COMPLETION_ONSTACK(complete); | 349 | DECLARE_COMPLETION_ONSTACK(complete); |
371 | 350 | ||
372 | /* we are going to wait on this IO */ | ||
373 | rw |= REQ_SYNC; | ||
374 | |||
375 | bio = bio_alloc(GFP_NOFS, 1); | 351 | bio = bio_alloc(GFP_NOFS, 1); |
376 | bio->bi_bdev = bdev; | 352 | bio->bi_bdev = bdev; |
377 | bio->bi_sector = sector; | 353 | bio->bi_sector = sector; |
@@ -380,6 +356,7 @@ static int scrub_fixup_io(int rw, struct block_device *bdev, sector_t sector, | |||
380 | bio->bi_private = &complete; | 356 | bio->bi_private = &complete; |
381 | submit_bio(rw, bio); | 357 | submit_bio(rw, bio); |
382 | 358 | ||
359 | /* this will also unplug the queue */ | ||
383 | wait_for_completion(&complete); | 360 | wait_for_completion(&complete); |
384 | 361 | ||
385 | ret = !test_bit(BIO_UPTODATE, &bio->bi_flags); | 362 | ret = !test_bit(BIO_UPTODATE, &bio->bi_flags); |
@@ -394,6 +371,7 @@ static void scrub_bio_end_io(struct bio *bio, int err) | |||
394 | struct btrfs_fs_info *fs_info = sdev->dev->dev_root->fs_info; | 371 | struct btrfs_fs_info *fs_info = sdev->dev->dev_root->fs_info; |
395 | 372 | ||
396 | sbio->err = err; | 373 | sbio->err = err; |
374 | sbio->bio = bio; | ||
397 | 375 | ||
398 | btrfs_queue_worker(&fs_info->scrub_workers, &sbio->work); | 376 | btrfs_queue_worker(&fs_info->scrub_workers, &sbio->work); |
399 | } | 377 | } |
@@ -453,6 +431,8 @@ static void scrub_checksum(struct btrfs_work *work) | |||
453 | } | 431 | } |
454 | 432 | ||
455 | out: | 433 | out: |
434 | scrub_free_bio(sbio->bio); | ||
435 | sbio->bio = NULL; | ||
456 | spin_lock(&sdev->list_lock); | 436 | spin_lock(&sdev->list_lock); |
457 | sbio->next_free = sdev->first_free; | 437 | sbio->next_free = sdev->first_free; |
458 | sdev->first_free = sbio->index; | 438 | sdev->first_free = sbio->index; |
@@ -583,25 +563,50 @@ static int scrub_checksum_super(struct scrub_bio *sbio, void *buffer) | |||
583 | static int scrub_submit(struct scrub_dev *sdev) | 563 | static int scrub_submit(struct scrub_dev *sdev) |
584 | { | 564 | { |
585 | struct scrub_bio *sbio; | 565 | struct scrub_bio *sbio; |
566 | struct bio *bio; | ||
567 | int i; | ||
586 | 568 | ||
587 | if (sdev->curr == -1) | 569 | if (sdev->curr == -1) |
588 | return 0; | 570 | return 0; |
589 | 571 | ||
590 | sbio = sdev->bios[sdev->curr]; | 572 | sbio = sdev->bios[sdev->curr]; |
591 | 573 | ||
592 | sbio->bio->bi_sector = sbio->physical >> 9; | 574 | bio = bio_alloc(GFP_NOFS, sbio->count); |
593 | sbio->bio->bi_size = sbio->count * PAGE_SIZE; | 575 | if (!bio) |
594 | sbio->bio->bi_next = NULL; | 576 | goto nomem; |
595 | sbio->bio->bi_flags |= 1 << BIO_UPTODATE; | 577 | |
596 | sbio->bio->bi_comp_cpu = -1; | 578 | bio->bi_private = sbio; |
597 | sbio->bio->bi_bdev = sdev->dev->bdev; | 579 | bio->bi_end_io = scrub_bio_end_io; |
580 | bio->bi_bdev = sdev->dev->bdev; | ||
581 | bio->bi_sector = sbio->physical >> 9; | ||
582 | |||
583 | for (i = 0; i < sbio->count; ++i) { | ||
584 | struct page *page; | ||
585 | int ret; | ||
586 | |||
587 | page = alloc_page(GFP_NOFS); | ||
588 | if (!page) | ||
589 | goto nomem; | ||
590 | |||
591 | ret = bio_add_page(bio, page, PAGE_SIZE, 0); | ||
592 | if (!ret) { | ||
593 | __free_page(page); | ||
594 | goto nomem; | ||
595 | } | ||
596 | } | ||
597 | |||
598 | sbio->err = 0; | 598 | sbio->err = 0; |
599 | sdev->curr = -1; | 599 | sdev->curr = -1; |
600 | atomic_inc(&sdev->in_flight); | 600 | atomic_inc(&sdev->in_flight); |
601 | 601 | ||
602 | submit_bio(0, sbio->bio); | 602 | submit_bio(READ, bio); |
603 | 603 | ||
604 | return 0; | 604 | return 0; |
605 | |||
606 | nomem: | ||
607 | scrub_free_bio(bio); | ||
608 | |||
609 | return -ENOMEM; | ||
605 | } | 610 | } |
606 | 611 | ||
607 | static int scrub_page(struct scrub_dev *sdev, u64 logical, u64 len, | 612 | static int scrub_page(struct scrub_dev *sdev, u64 logical, u64 len, |
@@ -633,7 +638,11 @@ again: | |||
633 | sbio->logical = logical; | 638 | sbio->logical = logical; |
634 | } else if (sbio->physical + sbio->count * PAGE_SIZE != physical || | 639 | } else if (sbio->physical + sbio->count * PAGE_SIZE != physical || |
635 | sbio->logical + sbio->count * PAGE_SIZE != logical) { | 640 | sbio->logical + sbio->count * PAGE_SIZE != logical) { |
636 | scrub_submit(sdev); | 641 | int ret; |
642 | |||
643 | ret = scrub_submit(sdev); | ||
644 | if (ret) | ||
645 | return ret; | ||
637 | goto again; | 646 | goto again; |
638 | } | 647 | } |
639 | sbio->spag[sbio->count].flags = flags; | 648 | sbio->spag[sbio->count].flags = flags; |
@@ -645,8 +654,13 @@ again: | |||
645 | memcpy(sbio->spag[sbio->count].csum, csum, sdev->csum_size); | 654 | memcpy(sbio->spag[sbio->count].csum, csum, sdev->csum_size); |
646 | } | 655 | } |
647 | ++sbio->count; | 656 | ++sbio->count; |
648 | if (sbio->count == SCRUB_PAGES_PER_BIO || force) | 657 | if (sbio->count == SCRUB_PAGES_PER_BIO || force) { |
649 | scrub_submit(sdev); | 658 | int ret; |
659 | |||
660 | ret = scrub_submit(sdev); | ||
661 | if (ret) | ||
662 | return ret; | ||
663 | } | ||
650 | 664 | ||
651 | return 0; | 665 | return 0; |
652 | } | 666 | } |
@@ -727,6 +741,7 @@ static noinline_for_stack int scrub_stripe(struct scrub_dev *sdev, | |||
727 | struct btrfs_root *root = fs_info->extent_root; | 741 | struct btrfs_root *root = fs_info->extent_root; |
728 | struct btrfs_root *csum_root = fs_info->csum_root; | 742 | struct btrfs_root *csum_root = fs_info->csum_root; |
729 | struct btrfs_extent_item *extent; | 743 | struct btrfs_extent_item *extent; |
744 | struct blk_plug plug; | ||
730 | u64 flags; | 745 | u64 flags; |
731 | int ret; | 746 | int ret; |
732 | int slot; | 747 | int slot; |
@@ -831,6 +846,7 @@ static noinline_for_stack int scrub_stripe(struct scrub_dev *sdev, | |||
831 | * the scrub. This might currently (crc32) end up to be about 1MB | 846 | * the scrub. This might currently (crc32) end up to be about 1MB |
832 | */ | 847 | */ |
833 | start_stripe = 0; | 848 | start_stripe = 0; |
849 | blk_start_plug(&plug); | ||
834 | again: | 850 | again: |
835 | logical = base + offset + start_stripe * increment; | 851 | logical = base + offset + start_stripe * increment; |
836 | for (i = start_stripe; i < nstripes; ++i) { | 852 | for (i = start_stripe; i < nstripes; ++i) { |
@@ -972,6 +988,7 @@ next: | |||
972 | scrub_submit(sdev); | 988 | scrub_submit(sdev); |
973 | 989 | ||
974 | out: | 990 | out: |
991 | blk_finish_plug(&plug); | ||
975 | btrfs_free_path(path); | 992 | btrfs_free_path(path); |
976 | return ret < 0 ? ret : 0; | 993 | return ret < 0 ? ret : 0; |
977 | } | 994 | } |
@@ -1166,7 +1183,7 @@ int btrfs_scrub_dev(struct btrfs_root *root, u64 devid, u64 start, u64 end, | |||
1166 | int ret; | 1183 | int ret; |
1167 | struct btrfs_device *dev; | 1184 | struct btrfs_device *dev; |
1168 | 1185 | ||
1169 | if (root->fs_info->closing) | 1186 | if (btrfs_fs_closing(root->fs_info)) |
1170 | return -EINVAL; | 1187 | return -EINVAL; |
1171 | 1188 | ||
1172 | /* | 1189 | /* |