diff options
author | Martin K. Petersen <martin.petersen@oracle.com> | 2010-09-10 14:50:10 -0400 |
---|---|---|
committer | Jens Axboe <axboe@carl.home.kernel.dk> | 2010-09-10 14:50:10 -0400 |
commit | 13f05c8d8e98bbdce89158bfdb2e380940695a88 (patch) | |
tree | 055215e7e2b1bdc684ead64daa61b30b35eaa3c5 /block/blk-integrity.c | |
parent | c8bf1336824ebd698d37b71763e1c43190f2229a (diff) |
block/scsi: Provide a limit on the number of integrity segments
Some controllers have a hardware limit on the number of protection
information scatter-gather list segments they can handle.
Introduce a max_integrity_segments limit in the block layer and provide
a new scsi_host_template setting that allows HBA drivers to provide a
value suitable for the hardware.
Add support for honoring the integrity segment limit when merging both
bios and requests.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Jens Axboe <axboe@carl.home.kernel.dk>
Diffstat (limited to 'block/blk-integrity.c')
-rw-r--r-- | block/blk-integrity.c | 93 |
1 files changed, 72 insertions, 21 deletions
diff --git a/block/blk-integrity.c b/block/blk-integrity.c index edce1ef7933d..885cbb59967e 100644 --- a/block/blk-integrity.c +++ b/block/blk-integrity.c | |||
@@ -32,24 +32,37 @@ static struct kmem_cache *integrity_cachep; | |||
32 | 32 | ||
33 | /** | 33 | /** |
34 | * blk_rq_count_integrity_sg - Count number of integrity scatterlist elements | 34 | * blk_rq_count_integrity_sg - Count number of integrity scatterlist elements |
35 | * @rq: request with integrity metadata attached | 35 | * @q: request queue |
36 | * @bio: bio with integrity metadata attached | ||
36 | * | 37 | * |
37 | * Description: Returns the number of elements required in a | 38 | * Description: Returns the number of elements required in a |
38 | * scatterlist corresponding to the integrity metadata in a request. | 39 | * scatterlist corresponding to the integrity metadata in a bio. |
39 | */ | 40 | */ |
40 | int blk_rq_count_integrity_sg(struct request *rq) | 41 | int blk_rq_count_integrity_sg(struct request_queue *q, struct bio *bio) |
41 | { | 42 | { |
42 | struct bio_vec *iv, *ivprv; | 43 | struct bio_vec *iv, *ivprv = NULL; |
43 | struct req_iterator iter; | 44 | unsigned int segments = 0; |
44 | unsigned int segments; | 45 | unsigned int seg_size = 0; |
46 | unsigned int i = 0; | ||
45 | 47 | ||
46 | ivprv = NULL; | 48 | bio_for_each_integrity_vec(iv, bio, i) { |
47 | segments = 0; | ||
48 | 49 | ||
49 | rq_for_each_integrity_segment(iv, rq, iter) { | 50 | if (ivprv) { |
51 | if (!BIOVEC_PHYS_MERGEABLE(ivprv, iv)) | ||
52 | goto new_segment; | ||
53 | |||
54 | if (!BIOVEC_SEG_BOUNDARY(q, ivprv, iv)) | ||
55 | goto new_segment; | ||
56 | |||
57 | if (seg_size + iv->bv_len > queue_max_segment_size(q)) | ||
58 | goto new_segment; | ||
50 | 59 | ||
51 | if (!ivprv || !BIOVEC_PHYS_MERGEABLE(ivprv, iv)) | 60 | seg_size += iv->bv_len; |
61 | } else { | ||
62 | new_segment: | ||
52 | segments++; | 63 | segments++; |
64 | seg_size = iv->bv_len; | ||
65 | } | ||
53 | 66 | ||
54 | ivprv = iv; | 67 | ivprv = iv; |
55 | } | 68 | } |
@@ -60,30 +73,34 @@ EXPORT_SYMBOL(blk_rq_count_integrity_sg); | |||
60 | 73 | ||
61 | /** | 74 | /** |
62 | * blk_rq_map_integrity_sg - Map integrity metadata into a scatterlist | 75 | * blk_rq_map_integrity_sg - Map integrity metadata into a scatterlist |
63 | * @rq: request with integrity metadata attached | 76 | * @q: request queue |
77 | * @bio: bio with integrity metadata attached | ||
64 | * @sglist: target scatterlist | 78 | * @sglist: target scatterlist |
65 | * | 79 | * |
66 | * Description: Map the integrity vectors in request into a | 80 | * Description: Map the integrity vectors in request into a |
67 | * scatterlist. The scatterlist must be big enough to hold all | 81 | * scatterlist. The scatterlist must be big enough to hold all |
68 | * elements. I.e. sized using blk_rq_count_integrity_sg(). | 82 | * elements. I.e. sized using blk_rq_count_integrity_sg(). |
69 | */ | 83 | */ |
70 | int blk_rq_map_integrity_sg(struct request *rq, struct scatterlist *sglist) | 84 | int blk_rq_map_integrity_sg(struct request_queue *q, struct bio *bio, |
85 | struct scatterlist *sglist) | ||
71 | { | 86 | { |
72 | struct bio_vec *iv, *ivprv; | 87 | struct bio_vec *iv, *ivprv = NULL; |
73 | struct req_iterator iter; | 88 | struct scatterlist *sg = NULL; |
74 | struct scatterlist *sg; | 89 | unsigned int segments = 0; |
75 | unsigned int segments; | 90 | unsigned int i = 0; |
76 | 91 | ||
77 | ivprv = NULL; | 92 | bio_for_each_integrity_vec(iv, bio, i) { |
78 | sg = NULL; | ||
79 | segments = 0; | ||
80 | |||
81 | rq_for_each_integrity_segment(iv, rq, iter) { | ||
82 | 93 | ||
83 | if (ivprv) { | 94 | if (ivprv) { |
84 | if (!BIOVEC_PHYS_MERGEABLE(ivprv, iv)) | 95 | if (!BIOVEC_PHYS_MERGEABLE(ivprv, iv)) |
85 | goto new_segment; | 96 | goto new_segment; |
86 | 97 | ||
98 | if (!BIOVEC_SEG_BOUNDARY(q, ivprv, iv)) | ||
99 | goto new_segment; | ||
100 | |||
101 | if (sg->length + iv->bv_len > queue_max_segment_size(q)) | ||
102 | goto new_segment; | ||
103 | |||
87 | sg->length += iv->bv_len; | 104 | sg->length += iv->bv_len; |
88 | } else { | 105 | } else { |
89 | new_segment: | 106 | new_segment: |
@@ -162,6 +179,40 @@ int blk_integrity_compare(struct gendisk *gd1, struct gendisk *gd2) | |||
162 | } | 179 | } |
163 | EXPORT_SYMBOL(blk_integrity_compare); | 180 | EXPORT_SYMBOL(blk_integrity_compare); |
164 | 181 | ||
182 | int blk_integrity_merge_rq(struct request_queue *q, struct request *req, | ||
183 | struct request *next) | ||
184 | { | ||
185 | if (blk_integrity_rq(req) != blk_integrity_rq(next)) | ||
186 | return -1; | ||
187 | |||
188 | if (req->nr_integrity_segments + next->nr_integrity_segments > | ||
189 | q->limits.max_integrity_segments) | ||
190 | return -1; | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | EXPORT_SYMBOL(blk_integrity_merge_rq); | ||
195 | |||
196 | int blk_integrity_merge_bio(struct request_queue *q, struct request *req, | ||
197 | struct bio *bio) | ||
198 | { | ||
199 | int nr_integrity_segs; | ||
200 | struct bio *next = bio->bi_next; | ||
201 | |||
202 | bio->bi_next = NULL; | ||
203 | nr_integrity_segs = blk_rq_count_integrity_sg(q, bio); | ||
204 | bio->bi_next = next; | ||
205 | |||
206 | if (req->nr_integrity_segments + nr_integrity_segs > | ||
207 | q->limits.max_integrity_segments) | ||
208 | return -1; | ||
209 | |||
210 | req->nr_integrity_segments += nr_integrity_segs; | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | EXPORT_SYMBOL(blk_integrity_merge_bio); | ||
215 | |||
165 | struct integrity_sysfs_entry { | 216 | struct integrity_sysfs_entry { |
166 | struct attribute attr; | 217 | struct attribute attr; |
167 | ssize_t (*show)(struct blk_integrity *, char *); | 218 | ssize_t (*show)(struct blk_integrity *, char *); |