blk: add vector gaps queue parameter
Subsystems (2)
PATCH 1/1 blk: add vector gaps queue parameter
SG_GAPS queue flag checks for bio vector alignment against PAGE_SIZE, but the device may have different constraints. This patch adds a new mask to queue limits a driver with such constraints can set to allow forming requests that would have previously been unnecessarily split.
Since nvme is the only driver using this, the patch includes this driver setting the mask.
--- block/bio.c | 2 +- block/blk-merge.c | 6 ++++-- block/blk-settings.c | 14 +++++++++++++- drivers/block/nvme-core.c | 1 + include/linux/bio.h | 4 ++-- include/linux/blkdev.h | 6 ++++++ 6 files changed, 27 insertions(+), 6 deletions(-)
block/bio.c +1 -1
@@ -771,7 +771,7 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page* offset would create a gap, disallow it. */ if (q->queue_flags & (1 <<QUEUE_FLAG_SG_GAPS ) && -bvec_gap_to_prev (prev, offset)) +bvec_gap_to_prev (prev, offset,queue_virt_mask (q)))return 0 ; }
block/blk-merge.c +4 -2
@@ -361,7 +361,8 @@ static int req_gap_to_prev(struct request *req, struct request *next)struct bio *prev = req->biotail;return bvec_gap_to_prev (&prev->bi_io_vec[prev->bi_vcnt -1 ], - next->bio->bi_io_vec[0 ].bv_offset); + next->bio->bi_io_vec[0 ].bv_offset, +queue_virt_mask (req->q)); } static int ll_merge_requests_fn(struct request_queue *q, struct request *req,
@@ -594,7 +595,8 @@ bool blk_rq_merge_ok(struct request *rq, struct bio *bio)struct bio_vec *bprev; bprev = &rq->biotail->bi_io_vec[rq->biotail->bi_vcnt -1 ]; -if (bvec_gap_to_prev (bprev, bio->bi_io_vec[0 ].bv_offset)) +if (bvec_gap_to_prev (bprev, bio->bi_io_vec[0 ].bv_offset, +queue_virt_mask (q)))return false; }
block/blk-settings.c +13 -1
@@ -111,6 +111,7 @@ void blk_set_default_limits(struct queue_limits *lim) lim->max_segments =BLK_MAX_SEGMENTS ; lim->max_integrity_segments =0 ; lim->seg_boundary_mask =BLK_SEG_BOUNDARY_MASK ; + lim->virt_mask =0 ; lim->max_segment_size =BLK_MAX_SEGMENT_SIZE ; lim->max_sectors = lim->max_hw_sectors =BLK_SAFE_MAX_SECTORS ; lim->chunk_sectors =0 ;
@@ -550,7 +551,7 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, t->seg_boundary_mask =min_not_zero (t->seg_boundary_mask, b->seg_boundary_mask); - + t->virt_mask =min_not_zero (t->virt_mask, b->virt_mask); t->max_segments =min_not_zero (t->max_segments, b->max_segments); t->max_integrity_segments =min_not_zero (t->max_integrity_segments, b->max_integrity_segments);
@@ -788,6 +789,17 @@ void blk_queue_segment_boundary(struct request_queue *q, unsigned long mask)EXPORT_SYMBOL (blk_queue_segment_boundary);/** +* blk_queue_virt_boundary - set boundary rules for bio merging +* @q: the request queue for the device +* @mask: the memory boundary mask +**/ +void blk_queue_virt_boundary (struct request_queue *q,unsigned long mask) +{ + q->limits.virt_mask = mask; +} +EXPORT_SYMBOL (blk_queue_virt_boundary); + +/** * blk_queue_dma_alignment - set dma length and memory alignment * @q: the request queue for the device * @mask: alignment mask
drivers/block/nvme-core.c +1 -0
@@ -2087,6 +2087,7 @@ static void nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid)blk_queue_chunk_sectors (ns->queue, dev->stripe_size >>9 );if (dev->vwc &NVME_CTRL_VWC_PRESENT )blk_queue_flush (ns->queue,REQ_FLUSH |REQ_FUA ); +blk_queue_virt_boundary (ns->queue, dev->page_size -1 ); disk->major = nvme_major; disk->first_minor =0 ;
include/linux/bio.h +2 -2
@@ -190,9 +190,9 @@ static inline void *bio_data(struct bio *bio)* Check if adding a bio_vec after bprv with offset would create a gap in * the SG list. Most drivers don't care about this, but some do. */ -static inline bool bvec_gap_to_prev (struct bio_vec *bprv,unsigned int offset) +static inline bool bvec_gap_to_prev (struct bio_vec *bprv,unsigned int offset,unsigned long mask) { -return offset || ((bprv->bv_offset + bprv->bv_len) & (PAGE_SIZE -1 )); +return offset || ((bprv->bv_offset + bprv->bv_len) & mask); }#define bio_io_error (bio) bio_endio((bio), -EIO)
include/linux/blkdev.h +6 -0
@@ -258,6 +258,7 @@ struct blk_queue_tag {struct queue_limits {unsigned long bounce_pfn;unsigned long seg_boundary_mask; +unsigned long virt_mask;unsigned int max_hw_sectors;unsigned int chunk_sectors;
@@ -1154,6 +1155,11 @@ static inline unsigned long queue_segment_boundary(struct request_queue *q)return q->limits.seg_boundary_mask; } +static inline unsigned long queue_virt_mask (struct request_queue *q) +{ +return q->limits.virt_mask; +} +static inline unsigned int queue_max_sectors (struct request_queue *q) {return q->limits.max_sectors;
Signature · 1 line
1.7.10.4
PATCH 1/1 blk: add vector gaps queue parameter
block/blk-settings.c void blk_set_default_limits(struct queue_limits *lim) · line 114
Quoted 10 lines
block/blk-settings.c +1
-0@@ -111,6 +111,7 @@ void blk_set_default_limits(struct queue_limits *lim) lim->max_segments =BLK_MAX_SEGMENTS ; lim->max_integrity_segments =0 ; lim->seg_boundary_mask =BLK_SEG_BOUNDARY_MASK ; + lim->virt_mask =0 ;
I wander if its better to default to PAGE_SIZE to not change the existing functionality? Or do we want to force drivers to set virt_mask?
PATCH 1/1 blk: add vector gaps queue parameter
Quoted 2 lines · Sagi Grimberg
I wander if its better to default to
PAGE_SIZEto not change the existing functionality? Or do we want to force drivers to setvirt_mask?
I'd say we should remove the SG_GAPS flag (which is horribly misnamed anyway) and just have this field. 0 means we don't need to check at all, and any actual value checks for that value.
PATCH 1/1 blk: add vector gaps queue parameter
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
"Christoph" == Christoph Hellwig <hch at infradead.org> writes:
Christoph> I'd say we should remove the SG_GAPS flag (which is horribly Christoph> misnamed anyway) and just have this field. 0 means we don't Christoph> need to check at all, and any actual value checks for that Christoph> value.
I was just going to make the same observation. I agree completely.
Signature · 1 line
Martin K. Petersen Oracle Linux Engineering