diff options
-rw-r--r-- | block/bio.c | 60 |
1 files changed, 31 insertions, 29 deletions
diff --git a/block/bio.c b/block/bio.c index 26853e072cd7..8d516d508ae3 100644 --- a/block/bio.c +++ b/block/bio.c | |||
@@ -665,6 +665,27 @@ static inline bool page_is_mergeable(const struct bio_vec *bv, | |||
665 | return true; | 665 | return true; |
666 | } | 666 | } |
667 | 667 | ||
668 | /* | ||
669 | * Check if the @page can be added to the current segment(@bv), and make | ||
670 | * sure to call it only if page_is_mergeable(@bv, @page) is true | ||
671 | */ | ||
672 | static bool can_add_page_to_seg(struct request_queue *q, | ||
673 | struct bio_vec *bv, struct page *page, unsigned len, | ||
674 | unsigned offset) | ||
675 | { | ||
676 | unsigned long mask = queue_segment_boundary(q); | ||
677 | phys_addr_t addr1 = page_to_phys(bv->bv_page) + bv->bv_offset; | ||
678 | phys_addr_t addr2 = page_to_phys(page) + offset + len - 1; | ||
679 | |||
680 | if ((addr1 | mask) != (addr2 | mask)) | ||
681 | return false; | ||
682 | |||
683 | if (bv->bv_len + len > queue_max_segment_size(q)) | ||
684 | return false; | ||
685 | |||
686 | return true; | ||
687 | } | ||
688 | |||
668 | /** | 689 | /** |
669 | * __bio_add_pc_page - attempt to add page to passthrough bio | 690 | * __bio_add_pc_page - attempt to add page to passthrough bio |
670 | * @q: the target queue | 691 | * @q: the target queue |
@@ -685,7 +706,6 @@ int __bio_add_pc_page(struct request_queue *q, struct bio *bio, | |||
685 | struct page *page, unsigned int len, unsigned int offset, | 706 | struct page *page, unsigned int len, unsigned int offset, |
686 | bool put_same_page) | 707 | bool put_same_page) |
687 | { | 708 | { |
688 | int retried_segments = 0; | ||
689 | struct bio_vec *bvec; | 709 | struct bio_vec *bvec; |
690 | 710 | ||
691 | /* | 711 | /* |
@@ -709,6 +729,7 @@ int __bio_add_pc_page(struct request_queue *q, struct bio *bio, | |||
709 | offset == bvec->bv_offset + bvec->bv_len) { | 729 | offset == bvec->bv_offset + bvec->bv_len) { |
710 | if (put_same_page) | 730 | if (put_same_page) |
711 | put_page(page); | 731 | put_page(page); |
732 | bvec_merge: | ||
712 | bvec->bv_len += len; | 733 | bvec->bv_len += len; |
713 | bio->bi_iter.bi_size += len; | 734 | bio->bi_iter.bi_size += len; |
714 | goto done; | 735 | goto done; |
@@ -720,11 +741,18 @@ int __bio_add_pc_page(struct request_queue *q, struct bio *bio, | |||
720 | */ | 741 | */ |
721 | if (bvec_gap_to_prev(q, bvec, offset)) | 742 | if (bvec_gap_to_prev(q, bvec, offset)) |
722 | return 0; | 743 | return 0; |
744 | |||
745 | if (page_is_mergeable(bvec, page, len, offset, false) && | ||
746 | can_add_page_to_seg(q, bvec, page, len, offset)) | ||
747 | goto bvec_merge; | ||
723 | } | 748 | } |
724 | 749 | ||
725 | if (bio_full(bio)) | 750 | if (bio_full(bio)) |
726 | return 0; | 751 | return 0; |
727 | 752 | ||
753 | if (bio->bi_phys_segments >= queue_max_segments(q)) | ||
754 | return 0; | ||
755 | |||
728 | /* | 756 | /* |
729 | * setup the new entry, we might clear it again later if we | 757 | * setup the new entry, we might clear it again later if we |
730 | * cannot add the page | 758 | * cannot add the page |
@@ -734,38 +762,12 @@ int __bio_add_pc_page(struct request_queue *q, struct bio *bio, | |||
734 | bvec->bv_len = len; | 762 | bvec->bv_len = len; |
735 | bvec->bv_offset = offset; | 763 | bvec->bv_offset = offset; |
736 | bio->bi_vcnt++; | 764 | bio->bi_vcnt++; |
737 | bio->bi_phys_segments++; | ||
738 | bio->bi_iter.bi_size += len; | 765 | bio->bi_iter.bi_size += len; |
739 | 766 | ||
740 | /* | ||
741 | * Perform a recount if the number of segments is greater | ||
742 | * than queue_max_segments(q). | ||
743 | */ | ||
744 | |||
745 | while (bio->bi_phys_segments > queue_max_segments(q)) { | ||
746 | |||
747 | if (retried_segments) | ||
748 | goto failed; | ||
749 | |||
750 | retried_segments = 1; | ||
751 | blk_recount_segments(q, bio); | ||
752 | } | ||
753 | |||
754 | /* If we may be able to merge these biovecs, force a recount */ | ||
755 | if (bio->bi_vcnt > 1 && biovec_phys_mergeable(q, bvec - 1, bvec)) | ||
756 | bio_clear_flag(bio, BIO_SEG_VALID); | ||
757 | |||
758 | done: | 767 | done: |
768 | bio->bi_phys_segments = bio->bi_vcnt; | ||
769 | bio_set_flag(bio, BIO_SEG_VALID); | ||
759 | return len; | 770 | return len; |
760 | |||
761 | failed: | ||
762 | bvec->bv_page = NULL; | ||
763 | bvec->bv_len = 0; | ||
764 | bvec->bv_offset = 0; | ||
765 | bio->bi_vcnt--; | ||
766 | bio->bi_iter.bi_size -= len; | ||
767 | blk_recount_segments(q, bio); | ||
768 | return 0; | ||
769 | } | 771 | } |
770 | EXPORT_SYMBOL(__bio_add_pc_page); | 772 | EXPORT_SYMBOL(__bio_add_pc_page); |
771 | 773 | ||