aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaurizio Lombardi <mlombard@redhat.com>2014-07-01 12:55:15 -0400
committerJens Axboe <axboe@fb.com>2014-07-01 12:55:15 -0400
commit254c4407cb84a6dec90336054615b0f0e996bb7c (patch)
tree28d91bb07d38f47d557833a2124a8e5f266bb5de
parent9b4231bf995996d6459c57959ead5a1829ff2c57 (diff)
bio: modify __bio_add_page() to accept pages that don't start a new segment
The original behaviour is to refuse to add a new page if the maximum number of segments has been reached, regardless of the fact the page we are going to add can be merged into the last segment or not. Unfortunately, when the system runs under heavy memory fragmentation conditions, a driver may try to add multiple pages to the last segment. The original code won't accept them and EBUSY will be reported to userspace. This patch modifies the function so it refuses to add a page only in case the latter starts a new segment and the maximum number of segments has already been reached. The bug can be easily reproduced with the st driver: 1) set CONFIG_SCSI_MPT2SAS_MAX_SGE or CONFIG_SCSI_MPT3SAS_MAX_SGE to 16 2) modprobe st buffer_kbs=1024 3) #dd if=/dev/zero of=/dev/st0 bs=1M count=10 dd: error writing `/dev/st0': Device or resource busy [ming.lei@canonical.com: update bi_iter.bi_size before recounting segments] Signed-off-by: Maurizio Lombardi <mlombard@redhat.com> Signed-off-by: Ming Lei <ming.lei@canonical.com> Tested-by: Dongsu Park <dongsu.park@profitbricks.com> Tested-by: Jet Chen <jet.chen@intel.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Christoph Hellwig <hch@lst.de> Cc: Kent Overstreet <kmo@daterainc.com> Cc: Jens Axboe <axboe@kernel.dk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Jens Axboe <axboe@fb.com>
-rw-r--r--block/bio.c52
1 files changed, 29 insertions, 23 deletions
diff --git a/block/bio.c b/block/bio.c
index 0ec61c9e536c..fb12df9af0fc 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -744,6 +744,7 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page
744 } 744 }
745 } 745 }
746 746
747 bio->bi_iter.bi_size += len;
747 goto done; 748 goto done;
748 } 749 }
749 750
@@ -760,29 +761,32 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page
760 return 0; 761 return 0;
761 762
762 /* 763 /*
763 * we might lose a segment or two here, but rather that than 764 * setup the new entry, we might clear it again later if we
764 * make this too complex. 765 * cannot add the page
766 */
767 bvec = &bio->bi_io_vec[bio->bi_vcnt];
768 bvec->bv_page = page;
769 bvec->bv_len = len;
770 bvec->bv_offset = offset;
771 bio->bi_vcnt++;
772 bio->bi_phys_segments++;
773 bio->bi_iter.bi_size += len;
774
775 /*
776 * Perform a recount if the number of segments is greater
777 * than queue_max_segments(q).
765 */ 778 */
766 779
767 while (bio->bi_phys_segments >= queue_max_segments(q)) { 780 while (bio->bi_phys_segments > queue_max_segments(q)) {
768 781
769 if (retried_segments) 782 if (retried_segments)
770 return 0; 783 goto failed;
771 784
772 retried_segments = 1; 785 retried_segments = 1;
773 blk_recount_segments(q, bio); 786 blk_recount_segments(q, bio);
774 } 787 }
775 788
776 /* 789 /*
777 * setup the new entry, we might clear it again later if we
778 * cannot add the page
779 */
780 bvec = &bio->bi_io_vec[bio->bi_vcnt];
781 bvec->bv_page = page;
782 bvec->bv_len = len;
783 bvec->bv_offset = offset;
784
785 /*
786 * if queue has other restrictions (eg varying max sector size 790 * if queue has other restrictions (eg varying max sector size
787 * depending on offset), it can specify a merge_bvec_fn in the 791 * depending on offset), it can specify a merge_bvec_fn in the
788 * queue to get further control 792 * queue to get further control
@@ -799,23 +803,25 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page
799 * merge_bvec_fn() returns number of bytes it can accept 803 * merge_bvec_fn() returns number of bytes it can accept
800 * at this offset 804 * at this offset
801 */ 805 */
802 if (q->merge_bvec_fn(q, &bvm, bvec) < bvec->bv_len) { 806 if (q->merge_bvec_fn(q, &bvm, bvec) < bvec->bv_len)
803 bvec->bv_page = NULL; 807 goto failed;
804 bvec->bv_len = 0;
805 bvec->bv_offset = 0;
806 return 0;
807 }
808 } 808 }
809 809
810 /* If we may be able to merge these biovecs, force a recount */ 810 /* If we may be able to merge these biovecs, force a recount */
811 if (bio->bi_vcnt && (BIOVEC_PHYS_MERGEABLE(bvec-1, bvec))) 811 if (bio->bi_vcnt > 1 && (BIOVEC_PHYS_MERGEABLE(bvec-1, bvec)))
812 bio->bi_flags &= ~(1 << BIO_SEG_VALID); 812 bio->bi_flags &= ~(1 << BIO_SEG_VALID);
813 813
814 bio->bi_vcnt++;
815 bio->bi_phys_segments++;
816 done: 814 done:
817 bio->bi_iter.bi_size += len;
818 return len; 815 return len;
816
817 failed:
818 bvec->bv_page = NULL;
819 bvec->bv_len = 0;
820 bvec->bv_offset = 0;
821 bio->bi_vcnt--;
822 bio->bi_iter.bi_size -= len;
823 blk_recount_segments(q, bio);
824 return 0;
819} 825}
820 826
821/** 827/**