Skip to content

Commit b7175e2

Browse files
Christoph Hellwigaxboe
authored andcommitted
block: add a dma mapping iterator
blk_rq_map_sg is maze of nested loops. Untangle it by creating an iterator that returns [paddr,len] tuples for DMA mapping, and then implement the DMA logic on top of this. This not only removes code at the source level, but also generates nicer binary code: $ size block/blk-merge.o.* text data bss dec hex filename 10001 432 0 10433 28c1 block/blk-merge.o.new 10317 468 0 10785 2a21 block/blk-merge.o.old Last but not least it will be used as a building block for a new DMA mapping helper that doesn't rely on struct scatterlist. Signed-off-by: Christoph Hellwig <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent 2caca8f commit b7175e2

File tree

1 file changed

+70
-107
lines changed

1 file changed

+70
-107
lines changed

block/blk-merge.c

Lines changed: 70 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -473,137 +473,100 @@ unsigned int blk_recalc_rq_segments(struct request *rq)
473473
return nr_phys_segs;
474474
}
475475

476-
static inline struct scatterlist *blk_next_sg(struct scatterlist **sg,
477-
struct scatterlist *sglist)
478-
{
479-
if (!*sg)
480-
return sglist;
476+
struct phys_vec {
477+
phys_addr_t paddr;
478+
u32 len;
479+
};
481480

482-
/*
483-
* If the driver previously mapped a shorter list, we could see a
484-
* termination bit prematurely unless it fully inits the sg table
485-
* on each mapping. We KNOW that there must be more entries here
486-
* or the driver would be buggy, so force clear the termination bit
487-
* to avoid doing a full sg_init_table() in drivers for each command.
488-
*/
489-
sg_unmark_end(*sg);
490-
return sg_next(*sg);
491-
}
492-
493-
static unsigned blk_bvec_map_sg(struct request_queue *q,
494-
struct bio_vec *bvec, struct scatterlist *sglist,
495-
struct scatterlist **sg)
481+
static bool blk_map_iter_next(struct request *req,
482+
struct req_iterator *iter, struct phys_vec *vec)
496483
{
497-
unsigned nbytes = bvec->bv_len;
498-
unsigned nsegs = 0, total = 0;
499-
500-
while (nbytes > 0) {
501-
unsigned offset = bvec->bv_offset + total;
502-
unsigned len = get_max_segment_size(&q->limits,
503-
bvec_phys(bvec) + total, nbytes);
504-
struct page *page = bvec->bv_page;
505-
506-
/*
507-
* Unfortunately a fair number of drivers barf on scatterlists
508-
* that have an offset larger than PAGE_SIZE, despite other
509-
* subsystems dealing with that invariant just fine. For now
510-
* stick to the legacy format where we never present those from
511-
* the block layer, but the code below should be removed once
512-
* these offenders (mostly MMC/SD drivers) are fixed.
513-
*/
514-
page += (offset >> PAGE_SHIFT);
515-
offset &= ~PAGE_MASK;
516-
517-
*sg = blk_next_sg(sg, sglist);
518-
sg_set_page(*sg, page, len, offset);
484+
unsigned int max_size;
485+
struct bio_vec bv;
519486

520-
total += len;
521-
nbytes -= len;
522-
nsegs++;
487+
if (req->rq_flags & RQF_SPECIAL_PAYLOAD) {
488+
if (!iter->bio)
489+
return false;
490+
vec->paddr = bvec_phys(&req->special_vec);
491+
vec->len = req->special_vec.bv_len;
492+
iter->bio = NULL;
493+
return true;
523494
}
524495

525-
return nsegs;
526-
}
527-
528-
static inline int __blk_bvec_map_sg(struct bio_vec bv,
529-
struct scatterlist *sglist, struct scatterlist **sg)
530-
{
531-
*sg = blk_next_sg(sg, sglist);
532-
sg_set_page(*sg, bv.bv_page, bv.bv_len, bv.bv_offset);
533-
return 1;
534-
}
535-
536-
/* only try to merge bvecs into one sg if they are from two bios */
537-
static inline bool
538-
__blk_segment_map_sg_merge(struct request_queue *q, struct bio_vec *bvec,
539-
struct bio_vec *bvprv, struct scatterlist **sg)
540-
{
541-
542-
int nbytes = bvec->bv_len;
543-
544-
if (!*sg)
496+
if (!iter->iter.bi_size)
545497
return false;
546498

547-
if ((*sg)->length + nbytes > queue_max_segment_size(q))
548-
return false;
499+
bv = mp_bvec_iter_bvec(iter->bio->bi_io_vec, iter->iter);
500+
vec->paddr = bvec_phys(&bv);
501+
max_size = get_max_segment_size(&req->q->limits, vec->paddr, UINT_MAX);
502+
bv.bv_len = min(bv.bv_len, max_size);
503+
bio_advance_iter_single(iter->bio, &iter->iter, bv.bv_len);
549504

550-
if (!biovec_phys_mergeable(q, bvprv, bvec))
551-
return false;
505+
/*
506+
* If we are entirely done with this bi_io_vec entry, check if the next
507+
* one could be merged into it. This typically happens when moving to
508+
* the next bio, but some callers also don't pack bvecs tight.
509+
*/
510+
while (!iter->iter.bi_size || !iter->iter.bi_bvec_done) {
511+
struct bio_vec next;
512+
513+
if (!iter->iter.bi_size) {
514+
if (!iter->bio->bi_next)
515+
break;
516+
iter->bio = iter->bio->bi_next;
517+
iter->iter = iter->bio->bi_iter;
518+
}
552519

553-
(*sg)->length += nbytes;
520+
next = mp_bvec_iter_bvec(iter->bio->bi_io_vec, iter->iter);
521+
if (bv.bv_len + next.bv_len > max_size ||
522+
!biovec_phys_mergeable(req->q, &bv, &next))
523+
break;
524+
525+
bv.bv_len += next.bv_len;
526+
bio_advance_iter_single(iter->bio, &iter->iter, next.bv_len);
527+
}
554528

529+
vec->len = bv.bv_len;
555530
return true;
556531
}
557532

558-
static int __blk_bios_map_sg(struct request_queue *q, struct bio *bio,
559-
struct scatterlist *sglist,
560-
struct scatterlist **sg)
533+
static inline struct scatterlist *blk_next_sg(struct scatterlist **sg,
534+
struct scatterlist *sglist)
561535
{
562-
struct bio_vec bvec, bvprv = { NULL };
563-
struct bvec_iter iter;
564-
int nsegs = 0;
565-
bool new_bio = false;
566-
567-
for_each_bio(bio) {
568-
bio_for_each_bvec(bvec, bio, iter) {
569-
/*
570-
* Only try to merge bvecs from two bios given we
571-
* have done bio internal merge when adding pages
572-
* to bio
573-
*/
574-
if (new_bio &&
575-
__blk_segment_map_sg_merge(q, &bvec, &bvprv, sg))
576-
goto next_bvec;
577-
578-
if (bvec.bv_offset + bvec.bv_len <= PAGE_SIZE)
579-
nsegs += __blk_bvec_map_sg(bvec, sglist, sg);
580-
else
581-
nsegs += blk_bvec_map_sg(q, &bvec, sglist, sg);
582-
next_bvec:
583-
new_bio = false;
584-
}
585-
if (likely(bio->bi_iter.bi_size)) {
586-
bvprv = bvec;
587-
new_bio = true;
588-
}
589-
}
536+
if (!*sg)
537+
return sglist;
590538

591-
return nsegs;
539+
/*
540+
* If the driver previously mapped a shorter list, we could see a
541+
* termination bit prematurely unless it fully inits the sg table
542+
* on each mapping. We KNOW that there must be more entries here
543+
* or the driver would be buggy, so force clear the termination bit
544+
* to avoid doing a full sg_init_table() in drivers for each command.
545+
*/
546+
sg_unmark_end(*sg);
547+
return sg_next(*sg);
592548
}
593549

594550
/*
595-
* map a request to scatterlist, return number of sg entries setup. Caller
596-
* must make sure sg can hold rq->nr_phys_segments entries
551+
* Map a request to scatterlist, return number of sg entries setup. Caller
552+
* must make sure sg can hold rq->nr_phys_segments entries.
597553
*/
598554
int __blk_rq_map_sg(struct request_queue *q, struct request *rq,
599555
struct scatterlist *sglist, struct scatterlist **last_sg)
600556
{
557+
struct req_iterator iter = {
558+
.bio = rq->bio,
559+
.iter = rq->bio->bi_iter,
560+
};
561+
struct phys_vec vec;
601562
int nsegs = 0;
602563

603-
if (rq->rq_flags & RQF_SPECIAL_PAYLOAD)
604-
nsegs = __blk_bvec_map_sg(rq->special_vec, sglist, last_sg);
605-
else if (rq->bio)
606-
nsegs = __blk_bios_map_sg(q, rq->bio, sglist, last_sg);
564+
while (blk_map_iter_next(rq, &iter, &vec)) {
565+
*last_sg = blk_next_sg(last_sg, sglist);
566+
sg_set_page(*last_sg, phys_to_page(vec.paddr), vec.len,
567+
offset_in_page(vec.paddr));
568+
nsegs++;
569+
}
607570

608571
if (*last_sg)
609572
sg_mark_end(*last_sg);

0 commit comments

Comments
 (0)