Skip to content

Commit 13f05c8

Browse files
martinkpetersenJens Axboe
authored andcommitted
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>
1 parent c8bf133 commit 13f05c8

File tree

12 files changed

+167
-50
lines changed

12 files changed

+167
-50
lines changed

block/blk-integrity.c

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,37 @@ static struct kmem_cache *integrity_cachep;
3232

3333
/**
3434
* 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
3637
*
3738
* 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.
3940
*/
40-
int blk_rq_count_integrity_sg(struct request *rq)
41+
int blk_rq_count_integrity_sg(struct request_queue *q, struct bio *bio)
4142
{
42-
struct bio_vec *iv, *ivprv;
43-
struct req_iterator iter;
44-
unsigned int segments;
43+
struct bio_vec *iv, *ivprv = NULL;
44+
unsigned int segments = 0;
45+
unsigned int seg_size = 0;
46+
unsigned int i = 0;
4547

46-
ivprv = NULL;
47-
segments = 0;
48+
bio_for_each_integrity_vec(iv, bio, i) {
4849

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;
5059

51-
if (!ivprv || !BIOVEC_PHYS_MERGEABLE(ivprv, iv))
60+
seg_size += iv->bv_len;
61+
} else {
62+
new_segment:
5263
segments++;
64+
seg_size = iv->bv_len;
65+
}
5366

5467
ivprv = iv;
5568
}
@@ -60,30 +73,34 @@ EXPORT_SYMBOL(blk_rq_count_integrity_sg);
6073

6174
/**
6275
* 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
6478
* @sglist: target scatterlist
6579
*
6680
* Description: Map the integrity vectors in request into a
6781
* scatterlist. The scatterlist must be big enough to hold all
6882
* elements. I.e. sized using blk_rq_count_integrity_sg().
6983
*/
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)
7186
{
72-
struct bio_vec *iv, *ivprv;
73-
struct req_iterator iter;
74-
struct scatterlist *sg;
75-
unsigned int segments;
87+
struct bio_vec *iv, *ivprv = NULL;
88+
struct scatterlist *sg = NULL;
89+
unsigned int segments = 0;
90+
unsigned int i = 0;
7691

77-
ivprv = NULL;
78-
sg = NULL;
79-
segments = 0;
80-
81-
rq_for_each_integrity_segment(iv, rq, iter) {
92+
bio_for_each_integrity_vec(iv, bio, i) {
8293

8394
if (ivprv) {
8495
if (!BIOVEC_PHYS_MERGEABLE(ivprv, iv))
8596
goto new_segment;
8697

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+
87104
sg->length += iv->bv_len;
88105
} else {
89106
new_segment:
@@ -162,6 +179,40 @@ int blk_integrity_compare(struct gendisk *gd1, struct gendisk *gd2)
162179
}
163180
EXPORT_SYMBOL(blk_integrity_compare);
164181

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+
165216
struct integrity_sysfs_entry {
166217
struct attribute attr;
167218
ssize_t (*show)(struct blk_integrity *, char *);

block/blk-merge.c

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,19 +205,24 @@ static inline int ll_new_hw_segment(struct request_queue *q,
205205
{
206206
int nr_phys_segs = bio_phys_segments(q, bio);
207207

208-
if (req->nr_phys_segments + nr_phys_segs > queue_max_segments(q)) {
209-
req->cmd_flags |= REQ_NOMERGE;
210-
if (req == q->last_merge)
211-
q->last_merge = NULL;
212-
return 0;
213-
}
208+
if (req->nr_phys_segments + nr_phys_segs > queue_max_segments(q))
209+
goto no_merge;
210+
211+
if (bio_integrity(bio) && blk_integrity_merge_bio(q, req, bio))
212+
goto no_merge;
214213

215214
/*
216215
* This will form the start of a new hw segment. Bump both
217216
* counters.
218217
*/
219218
req->nr_phys_segments += nr_phys_segs;
220219
return 1;
220+
221+
no_merge:
222+
req->cmd_flags |= REQ_NOMERGE;
223+
if (req == q->last_merge)
224+
q->last_merge = NULL;
225+
return 0;
221226
}
222227

223228
int ll_back_merge_fn(struct request_queue *q, struct request *req,
@@ -301,6 +306,9 @@ static int ll_merge_requests_fn(struct request_queue *q, struct request *req,
301306
if (total_phys_segments > queue_max_segments(q))
302307
return 0;
303308

309+
if (blk_integrity_rq(req) && blk_integrity_merge_rq(q, req, next))
310+
return 0;
311+
304312
/* Merge is OK... */
305313
req->nr_phys_segments = total_phys_segments;
306314
return 1;
@@ -372,9 +380,6 @@ static int attempt_merge(struct request_queue *q, struct request *req,
372380
|| next->special)
373381
return 0;
374382

375-
if (blk_integrity_rq(req) != blk_integrity_rq(next))
376-
return 0;
377-
378383
/*
379384
* If we are allowed to merge, then append bio list
380385
* from next to rq and release next. merge_requests_fn

block/blk-settings.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ EXPORT_SYMBOL_GPL(blk_queue_lld_busy);
111111
void blk_set_default_limits(struct queue_limits *lim)
112112
{
113113
lim->max_segments = BLK_MAX_SEGMENTS;
114+
lim->max_integrity_segments = 0;
114115
lim->seg_boundary_mask = BLK_SEG_BOUNDARY_MASK;
115116
lim->max_segment_size = BLK_MAX_SEGMENT_SIZE;
116117
lim->max_sectors = BLK_DEF_MAX_SECTORS;
@@ -509,6 +510,8 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
509510
b->seg_boundary_mask);
510511

511512
t->max_segments = min_not_zero(t->max_segments, b->max_segments);
513+
t->max_integrity_segments = min_not_zero(t->max_integrity_segments,
514+
b->max_integrity_segments);
512515

513516
t->max_segment_size = min_not_zero(t->max_segment_size,
514517
b->max_segment_size);

block/blk-sysfs.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ static ssize_t queue_max_segments_show(struct request_queue *q, char *page)
112112
return queue_var_show(queue_max_segments(q), (page));
113113
}
114114

115+
static ssize_t queue_max_integrity_segments_show(struct request_queue *q, char *page)
116+
{
117+
return queue_var_show(q->limits.max_integrity_segments, (page));
118+
}
119+
115120
static ssize_t queue_max_segment_size_show(struct request_queue *q, char *page)
116121
{
117122
if (test_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags))
@@ -288,6 +293,11 @@ static struct queue_sysfs_entry queue_max_segments_entry = {
288293
.show = queue_max_segments_show,
289294
};
290295

296+
static struct queue_sysfs_entry queue_max_integrity_segments_entry = {
297+
.attr = {.name = "max_integrity_segments", .mode = S_IRUGO },
298+
.show = queue_max_integrity_segments_show,
299+
};
300+
291301
static struct queue_sysfs_entry queue_max_segment_size_entry = {
292302
.attr = {.name = "max_segment_size", .mode = S_IRUGO },
293303
.show = queue_max_segment_size_show,
@@ -375,6 +385,7 @@ static struct attribute *default_attrs[] = {
375385
&queue_max_hw_sectors_entry.attr,
376386
&queue_max_sectors_entry.attr,
377387
&queue_max_segments_entry.attr,
388+
&queue_max_integrity_segments_entry.attr,
378389
&queue_max_segment_size_entry.attr,
379390
&queue_iosched_entry.attr,
380391
&queue_hw_sector_size_entry.attr,

block/blk.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,6 @@ static inline int queue_congestion_off_threshold(struct request_queue *q)
132132
return q->nr_congestion_off;
133133
}
134134

135-
#if defined(CONFIG_BLK_DEV_INTEGRITY)
136-
137-
#define rq_for_each_integrity_segment(bvl, _rq, _iter) \
138-
__rq_for_each_bio(_iter.bio, _rq) \
139-
bip_for_each_vec(bvl, _iter.bio->bi_integrity, _iter.i)
140-
141-
#endif /* BLK_DEV_INTEGRITY */
142-
143135
static inline int blk_cpu_to_group(int cpu)
144136
{
145137
#ifdef CONFIG_SCHED_MC

drivers/scsi/hosts.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
376376
shost->this_id = sht->this_id;
377377
shost->can_queue = sht->can_queue;
378378
shost->sg_tablesize = sht->sg_tablesize;
379+
shost->sg_prot_tablesize = sht->sg_prot_tablesize;
379380
shost->cmd_per_lun = sht->cmd_per_lun;
380381
shost->unchecked_isa_dma = sht->unchecked_isa_dma;
381382
shost->use_clustering = sht->use_clustering;

drivers/scsi/scsi_lib.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -968,40 +968,42 @@ static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb,
968968
*/
969969
int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask)
970970
{
971-
int error = scsi_init_sgtable(cmd->request, &cmd->sdb, gfp_mask);
971+
struct request *rq = cmd->request;
972+
973+
int error = scsi_init_sgtable(rq, &cmd->sdb, gfp_mask);
972974
if (error)
973975
goto err_exit;
974976

975-
if (blk_bidi_rq(cmd->request)) {
977+
if (blk_bidi_rq(rq)) {
976978
struct scsi_data_buffer *bidi_sdb = kmem_cache_zalloc(
977979
scsi_sdb_cache, GFP_ATOMIC);
978980
if (!bidi_sdb) {
979981
error = BLKPREP_DEFER;
980982
goto err_exit;
981983
}
982984

983-
cmd->request->next_rq->special = bidi_sdb;
984-
error = scsi_init_sgtable(cmd->request->next_rq, bidi_sdb,
985-
GFP_ATOMIC);
985+
rq->next_rq->special = bidi_sdb;
986+
error = scsi_init_sgtable(rq->next_rq, bidi_sdb, GFP_ATOMIC);
986987
if (error)
987988
goto err_exit;
988989
}
989990

990-
if (blk_integrity_rq(cmd->request)) {
991+
if (blk_integrity_rq(rq)) {
991992
struct scsi_data_buffer *prot_sdb = cmd->prot_sdb;
992993
int ivecs, count;
993994

994995
BUG_ON(prot_sdb == NULL);
995-
ivecs = blk_rq_count_integrity_sg(cmd->request);
996+
ivecs = blk_rq_count_integrity_sg(rq->q, rq->bio);
996997

997998
if (scsi_alloc_sgtable(prot_sdb, ivecs, gfp_mask)) {
998999
error = BLKPREP_DEFER;
9991000
goto err_exit;
10001001
}
10011002

1002-
count = blk_rq_map_integrity_sg(cmd->request,
1003+
count = blk_rq_map_integrity_sg(rq->q, rq->bio,
10031004
prot_sdb->table.sgl);
10041005
BUG_ON(unlikely(count > ivecs));
1006+
BUG_ON(unlikely(count > queue_max_integrity_segments(rq->q)));
10051007

10061008
cmd->prot_sdb = prot_sdb;
10071009
cmd->prot_sdb->table.nents = count;
@@ -1625,6 +1627,14 @@ struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost,
16251627
blk_queue_max_segments(q, min_t(unsigned short, shost->sg_tablesize,
16261628
SCSI_MAX_SG_CHAIN_SEGMENTS));
16271629

1630+
if (scsi_host_prot_dma(shost)) {
1631+
shost->sg_prot_tablesize =
1632+
min_not_zero(shost->sg_prot_tablesize,
1633+
(unsigned short)SCSI_MAX_PROT_SG_SEGMENTS);
1634+
BUG_ON(shost->sg_prot_tablesize < shost->sg_tablesize);
1635+
blk_queue_max_integrity_segments(q, shost->sg_prot_tablesize);
1636+
}
1637+
16281638
blk_queue_max_hw_sectors(q, shost->max_sectors);
16291639
blk_queue_bounce_limit(q, scsi_calculate_bounce_limit(shost));
16301640
blk_queue_segment_boundary(q, shost->dma_boundary);

drivers/scsi/scsi_sysfs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ shost_rd_attr(host_busy, "%hu\n");
251251
shost_rd_attr(cmd_per_lun, "%hd\n");
252252
shost_rd_attr(can_queue, "%hd\n");
253253
shost_rd_attr(sg_tablesize, "%hu\n");
254+
shost_rd_attr(sg_prot_tablesize, "%hu\n");
254255
shost_rd_attr(unchecked_isa_dma, "%d\n");
255256
shost_rd_attr(prot_capabilities, "%u\n");
256257
shost_rd_attr(prot_guard_type, "%hd\n");
@@ -262,6 +263,7 @@ static struct attribute *scsi_sysfs_shost_attrs[] = {
262263
&dev_attr_cmd_per_lun.attr,
263264
&dev_attr_can_queue.attr,
264265
&dev_attr_sg_tablesize.attr,
266+
&dev_attr_sg_prot_tablesize.attr,
265267
&dev_attr_unchecked_isa_dma.attr,
266268
&dev_attr_proc_name.attr,
267269
&dev_attr_scan.attr,

include/linux/bio.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,10 @@ static inline struct bio *bio_list_get(struct bio_list *bl)
496496
#define bip_for_each_vec(bvl, bip, i) \
497497
__bip_for_each_vec(bvl, bip, i, (bip)->bip_idx)
498498

499+
#define bio_for_each_integrity_vec(_bvl, _bio, _iter) \
500+
for_each_bio(_bio) \
501+
bip_for_each_vec(_bvl, _bio->bi_integrity, _iter)
502+
499503
#define bio_integrity(bio) (bio->bi_integrity != NULL)
500504

501505
extern struct bio_integrity_payload *bio_integrity_alloc_bioset(struct bio *, gfp_t, unsigned int, struct bio_set *);

0 commit comments

Comments
 (0)