blk_rq_map_sg() just needs to move the dma_vec into the dma_address
of the sgl. Callers will need to ensure not to call dma_map_sg()
for dma-direct requests.

This will likely get less ugly with Christoph's proposed cleanup
to the DMA API. It will be much simpler if devices are just
calling a dma_map_bio() and don't have to worry about dma-direct
requests.

Signed-off-by: Logan Gunthorpe <[email protected]>
---
 block/blk-merge.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/block/blk-merge.c b/block/blk-merge.c
index a7a5453987f9..ccd6c44b9f6e 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -545,6 +545,69 @@ static int __blk_bios_map_sg(struct request_queue *q, 
struct bio *bio,
        return nsegs;
 }
 
+static unsigned blk_dvec_map_sg(struct request_queue *q,
+               struct dma_vec *dvec, struct scatterlist *sglist,
+               struct scatterlist **sg)
+{
+       unsigned nbytes = dvec->dv_len;
+       unsigned nsegs = 0, total = 0;
+
+       while (nbytes > 0) {
+               unsigned seg_size;
+
+               *sg = blk_next_sg(sg, sglist);
+
+               seg_size = get_max_segment_size(q, total);
+               seg_size = min(nbytes, seg_size);
+
+               (*sg)->dma_address = dvec->dv_addr + total;
+               sg_dma_len(*sg) = seg_size;
+
+               total += seg_size;
+               nbytes -= seg_size;
+               nsegs++;
+       }
+
+       return nsegs;
+}
+
+static inline void
+__blk_segment_dma_map_sg(struct request_queue *q, struct dma_vec *dvec,
+                        struct scatterlist *sglist, struct dma_vec *dvprv,
+                        struct scatterlist **sg, int *nsegs)
+{
+       int nbytes = dvec->dv_len;
+
+       if (*sg) {
+               if ((*sg)->length + nbytes > queue_max_segment_size(q))
+                       goto new_segment;
+               if (!dmavec_phys_mergeable(q, dvprv, dvec))
+                       goto new_segment;
+
+               (*sg)->length += nbytes;
+       } else {
+new_segment:
+               (*nsegs) += blk_dvec_map_sg(q, dvec, sglist, sg);
+       }
+       *dvprv = *dvec;
+}
+
+static int __blk_dma_bios_map_sg(struct request_queue *q, struct bio *bio,
+                                struct scatterlist *sglist,
+                                struct scatterlist **sg)
+{
+       struct dma_vec dvec, dvprv = {};
+       struct bvec_iter iter;
+       int nsegs = 0;
+
+       for_each_bio(bio)
+               bio_for_each_dvec(dvec, bio, iter)
+                       __blk_segment_dma_map_sg(q, &dvec, sglist, &dvprv,
+                                                sg, &nsegs);
+
+       return nsegs;
+}
+
 /*
  * map a request to scatterlist, return number of sg entries setup. Caller
  * must make sure sg can hold rq->nr_phys_segments entries
@@ -559,6 +622,8 @@ int blk_rq_map_sg(struct request_queue *q, struct request 
*rq,
                nsegs = __blk_bvec_map_sg(rq->special_vec, sglist, &sg);
        else if (rq->bio && bio_op(rq->bio) == REQ_OP_WRITE_SAME)
                nsegs = __blk_bvec_map_sg(bio_iovec(rq->bio), sglist, &sg);
+       else if (blk_rq_is_dma_direct(rq))
+               nsegs = __blk_dma_bios_map_sg(q, rq->bio, sglist, &sg);
        else if (rq->bio)
                nsegs = __blk_bios_map_sg(q, rq->bio, sglist, &sg);
 
-- 
2.20.1

Reply via email to