diff options
-rw-r--r-- | fs/btrfs/scrub.c | 114 |
1 files changed, 65 insertions, 49 deletions
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 6dfed0c27ac3..2d1f8909a8e1 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; |
@@ -394,6 +373,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; | 373 | struct btrfs_fs_info *fs_info = sdev->dev->dev_root->fs_info; |
395 | 374 | ||
396 | sbio->err = err; | 375 | sbio->err = err; |
376 | sbio->bio = bio; | ||
397 | 377 | ||
398 | btrfs_queue_worker(&fs_info->scrub_workers, &sbio->work); | 378 | btrfs_queue_worker(&fs_info->scrub_workers, &sbio->work); |
399 | } | 379 | } |
@@ -453,6 +433,8 @@ static void scrub_checksum(struct btrfs_work *work) | |||
453 | } | 433 | } |
454 | 434 | ||
455 | out: | 435 | out: |
436 | scrub_free_bio(sbio->bio); | ||
437 | sbio->bio = NULL; | ||
456 | spin_lock(&sdev->list_lock); | 438 | spin_lock(&sdev->list_lock); |
457 | sbio->next_free = sdev->first_free; | 439 | sbio->next_free = sdev->first_free; |
458 | sdev->first_free = sbio->index; | 440 | sdev->first_free = sbio->index; |
@@ -583,25 +565,50 @@ static int scrub_checksum_super(struct scrub_bio *sbio, void *buffer) | |||
583 | static int scrub_submit(struct scrub_dev *sdev) | 565 | static int scrub_submit(struct scrub_dev *sdev) |
584 | { | 566 | { |
585 | struct scrub_bio *sbio; | 567 | struct scrub_bio *sbio; |
568 | struct bio *bio; | ||
569 | int i; | ||
586 | 570 | ||
587 | if (sdev->curr == -1) | 571 | if (sdev->curr == -1) |
588 | return 0; | 572 | return 0; |
589 | 573 | ||
590 | sbio = sdev->bios[sdev->curr]; | 574 | sbio = sdev->bios[sdev->curr]; |
591 | 575 | ||
592 | sbio->bio->bi_sector = sbio->physical >> 9; | 576 | bio = bio_alloc(GFP_NOFS, sbio->count); |
593 | sbio->bio->bi_size = sbio->count * PAGE_SIZE; | 577 | if (!bio) |
594 | sbio->bio->bi_next = NULL; | 578 | goto nomem; |
595 | sbio->bio->bi_flags |= 1 << BIO_UPTODATE; | 579 | |
596 | sbio->bio->bi_comp_cpu = -1; | 580 | bio->bi_private = sbio; |
597 | sbio->bio->bi_bdev = sdev->dev->bdev; | 581 | bio->bi_end_io = scrub_bio_end_io; |
582 | bio->bi_bdev = sdev->dev->bdev; | ||
583 | bio->bi_sector = sbio->physical >> 9; | ||
584 | |||
585 | for (i = 0; i < sbio->count; ++i) { | ||
586 | struct page *page; | ||
587 | int ret; | ||
588 | |||
589 | page = alloc_page(GFP_NOFS); | ||
590 | if (!page) | ||
591 | goto nomem; | ||
592 | |||
593 | ret = bio_add_page(bio, page, PAGE_SIZE, 0); | ||
594 | if (!ret) { | ||
595 | __free_page(page); | ||
596 | goto nomem; | ||
597 | } | ||
598 | } | ||
599 | |||
598 | sbio->err = 0; | 600 | sbio->err = 0; |
599 | sdev->curr = -1; | 601 | sdev->curr = -1; |
600 | atomic_inc(&sdev->in_flight); | 602 | atomic_inc(&sdev->in_flight); |
601 | 603 | ||
602 | submit_bio(0, sbio->bio); | 604 | submit_bio(READ, bio); |
603 | 605 | ||
604 | return 0; | 606 | return 0; |
607 | |||
608 | nomem: | ||
609 | scrub_free_bio(bio); | ||
610 | |||
611 | return -ENOMEM; | ||
605 | } | 612 | } |
606 | 613 | ||
607 | static int scrub_page(struct scrub_dev *sdev, u64 logical, u64 len, | 614 | static int scrub_page(struct scrub_dev *sdev, u64 logical, u64 len, |
@@ -633,7 +640,11 @@ again: | |||
633 | sbio->logical = logical; | 640 | sbio->logical = logical; |
634 | } else if (sbio->physical + sbio->count * PAGE_SIZE != physical || | 641 | } else if (sbio->physical + sbio->count * PAGE_SIZE != physical || |
635 | sbio->logical + sbio->count * PAGE_SIZE != logical) { | 642 | sbio->logical + sbio->count * PAGE_SIZE != logical) { |
636 | scrub_submit(sdev); | 643 | int ret; |
644 | |||
645 | ret = scrub_submit(sdev); | ||
646 | if (ret) | ||
647 | return ret; | ||
637 | goto again; | 648 | goto again; |
638 | } | 649 | } |
639 | sbio->spag[sbio->count].flags = flags; | 650 | sbio->spag[sbio->count].flags = flags; |
@@ -645,8 +656,13 @@ again: | |||
645 | memcpy(sbio->spag[sbio->count].csum, csum, sdev->csum_size); | 656 | memcpy(sbio->spag[sbio->count].csum, csum, sdev->csum_size); |
646 | } | 657 | } |
647 | ++sbio->count; | 658 | ++sbio->count; |
648 | if (sbio->count == SCRUB_PAGES_PER_BIO || force) | 659 | if (sbio->count == SCRUB_PAGES_PER_BIO || force) { |
649 | scrub_submit(sdev); | 660 | int ret; |
661 | |||
662 | ret = scrub_submit(sdev); | ||
663 | if (ret) | ||
664 | return ret; | ||
665 | } | ||
650 | 666 | ||
651 | return 0; | 667 | return 0; |
652 | } | 668 | } |