Premapped buffers don't require a generic bio_vec since these have
already been dma mapped. Repurpose the bi_io_vec space to strore dmabuf
maps as they are mutually exclusive.

Suggested-by: Keith Busch <[email protected]>
Signed-off-by: Pavel Begunkov <[email protected]>
---
 block/bio.c               | 15 +++++++++++++--
 block/blk-merge.c         | 37 +++++++++++++++++++++++++++++++++++++
 block/fops.c              |  2 +-
 include/linux/bio.h       |  9 +++++----
 include/linux/blk-mq.h    |  7 +++++++
 include/linux/blk_types.h | 14 +++++++++++++-
 include/linux/bvec.h      |  3 ++-
 7 files changed, 78 insertions(+), 9 deletions(-)

diff --git a/block/bio.c b/block/bio.c
index 898b2f5ef8c8..1602eab05761 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -860,7 +860,11 @@ static int __bio_clone(struct bio *bio, struct bio 
*bio_src, gfp_t gfp)
        bio->bi_write_hint = bio_src->bi_write_hint;
        bio->bi_write_stream = bio_src->bi_write_stream;
        bio->bi_iter = bio_src->bi_iter;
-       bio->bi_io_vec = bio_src->bi_io_vec;
+
+       if (op_is_dmabuf(bio->bi_opf))
+               bio->bi_dmabuf_map = bio_src->bi_dmabuf_map;
+       else
+               bio->bi_io_vec = bio_src->bi_io_vec;
 
        if (bio->bi_bdev) {
                if (bio->bi_bdev == bio_src->bi_bdev &&
@@ -1183,16 +1187,23 @@ EXPORT_SYMBOL_GPL(__bio_release_pages);
 
 bool bio_iov_iter_set(struct bio *bio, const struct iov_iter *iter)
 {
-       if (!iov_iter_is_bvec(iter))
+       if (!iov_iter_is_bvec(iter) && !iov_iter_is_dmabuf_map(iter))
                return false;
 
        WARN_ON_ONCE(bio->bi_max_vecs);
 
+       static_assert(offsetof(struct bio, bi_io_vec) ==
+                     offsetof(struct bio, bi_dmabuf_map));
+       static_assert(offsetof(struct iov_iter, bvec) ==
+                     offsetof(struct iov_iter, dmabuf_map));
+
        bio->bi_io_vec = (struct bio_vec *)iter->bvec;
        bio->bi_iter.bi_idx = 0;
        bio->bi_iter.bi_offset = iter->iov_offset;
        bio->bi_iter.bi_size = iov_iter_count(iter);
        bio_set_flag(bio, BIO_CLONED);
+       if (iov_iter_is_dmabuf_map(iter))
+               bio->bi_opf |= REQ_NOMERGE | REQ_DMABUF;
        return true;
 }
 
diff --git a/block/blk-merge.c b/block/blk-merge.c
index 258a726071d1..1beedc42a85d 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -9,6 +9,7 @@
 #include <linux/blk-integrity.h>
 #include <linux/part_stat.h>
 #include <linux/blk-cgroup.h>
+#include <linux/dma-buf-io.h>
 
 #include <trace/events/block.h>
 
@@ -319,6 +320,28 @@ static inline unsigned int bvec_seg_gap(struct bio_vec 
*bvprv,
        return bv->bv_offset | (bvprv->bv_offset + bvprv->bv_len);
 }
 
+static inline int bio_split_io_at_dmabuf(struct bio *bio,
+               const struct queue_limits *lim, unsigned *segs,
+               unsigned max_bytes, unsigned len_align_mask,
+               unsigned start_align_mask)
+{
+       unsigned bytes = min(bio->bi_iter.bi_size, max_bytes);
+       unsigned seg_shift = bio->bi_dmabuf_map->seg_shift;
+       unsigned offset = bio->bi_iter.bi_offset & ((1U << seg_shift) - 1);
+
+       if ((bio->bi_iter.bi_offset & start_align_mask) ||
+           (bio->bi_iter.bi_size & len_align_mask))
+               return -EINVAL;
+
+       /* single contiguous range into the dma-buf */
+       *segs = 1;
+
+       bytes = min(bytes, ((unsigned)lim->max_segments << seg_shift) - offset);
+       if (bytes != bio->bi_iter.bi_size)
+               return bytes;
+       return 0;
+}
+
 /**
  * bio_split_io_at - check if and where to split a bio
  * @bio:  [in] bio to be split
@@ -346,6 +369,19 @@ int bio_split_io_at(struct bio *bio, const struct 
queue_limits *lim,
                len_align_mask |= (bc->bc_key->crypto_cfg.data_unit_size - 1);
        }
 
+       if (op_is_dmabuf(bio->bi_opf)) {
+               int ret;
+
+               ret = bio_split_io_at_dmabuf(bio, lim, &nsegs, max_bytes,
+                                            len_align_mask, start_align_mask);
+               if (ret < 0)
+                       return ret;
+               if (!ret)
+                       goto out;
+               bytes = ret;
+               goto split;
+       }
+
        bio_for_each_bvec(bv, bio, iter) {
                if (bv.bv_offset & start_align_mask ||
                    bv.bv_len & len_align_mask)
@@ -376,6 +412,7 @@ int bio_split_io_at(struct bio *bio, const struct 
queue_limits *lim,
                bvprvp = &bvprv;
        }
 
+out:
        *segs = nsegs;
        bio->bi_bvec_gap_bit = ffs(gaps);
        return 0;
diff --git a/block/fops.c b/block/fops.c
index d11923053afe..56bbacf6e317 100644
--- a/block/fops.c
+++ b/block/fops.c
@@ -346,7 +346,7 @@ static ssize_t __blkdev_direct_IO_async(struct kiocb *iocb,
         * Users don't rely on the iterator being in any particular
         * state for async I/O returning -EIOCBQUEUED, hence we can
         * avoid expensive iov_iter_advance(). Bypass
-        * bio_iov_iter_get_pages() and set the bvec directly.
+        * bio_iov_iter_get_pages() and set the bvec/dmabuf directly.
         */
        if (!bio_iov_iter_set(bio, iter)) {
                ret = blkdev_iov_iter_get_pages(bio, iter, bdev);
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 0d27e0c72905..22ce9deb2feb 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -80,7 +80,8 @@ static inline bool bio_no_advance_iter(const struct bio *bio)
 {
        return bio_op(bio) == REQ_OP_DISCARD ||
               bio_op(bio) == REQ_OP_SECURE_ERASE ||
-              bio_op(bio) == REQ_OP_WRITE_ZEROES;
+              bio_op(bio) == REQ_OP_WRITE_ZEROES ||
+              op_is_dmabuf(bio->bi_opf);
 }
 
 static inline void *bio_data(struct bio *bio)
@@ -438,12 +439,12 @@ static inline void bio_wouldblock_error(struct bio *bio)
 
 /*
  * Calculate number of bvec segments that should be allocated to fit data
- * pointed by @iter. If @iter is backed by bvec it's going to be reused
- * instead of allocating a new one.
+ * pointed by @iter. If @iter is backed by a bvec or a dmabuf, the bvec array /
+ * the dma map are going to be reused, and so no extra allocation is required.
  */
 static inline int bio_iov_vecs_to_alloc(struct iov_iter *iter, int max_segs)
 {
-       if (iov_iter_is_bvec(iter))
+       if (iov_iter_is_bvec(iter) || iov_iter_is_dmabuf_map(iter))
                return 0;
        return iov_iter_npages(iter, max_segs);
 }
diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h
index af878597afb8..7c7504c84e09 100644
--- a/include/linux/blk-mq.h
+++ b/include/linux/blk-mq.h
@@ -1017,6 +1017,13 @@ static inline void *blk_mq_rq_to_pdu(struct request *rq)
        return rq + 1;
 }
 
+static inline bool blk_mq_rq_is_dmabuf(struct request *rq)
+{
+       if (!IS_ENABLED(CONFIG_DMA_SHARED_BUFFER))
+               return false;
+       return rq->bio && op_is_dmabuf(rq->bio->bi_opf);
+}
+
 static inline struct blk_mq_hw_ctx *queue_hctx(struct request_queue *q, int id)
 {
        struct blk_mq_hw_ctx *hctx;
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index d49d97a050d0..a305951f0312 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -233,7 +233,12 @@ struct bio {
        atomic_t                __bi_remaining;
 
        /* The actual vec list, preserved by bio_reset() */
-       struct bio_vec          *bi_io_vec;
+       union {
+               struct bio_vec          *bi_io_vec;
+               /* Driver specific dma map, valid IFF REQ_DMABUF is set */
+               struct dma_buf_io_map   *bi_dmabuf_map;
+       };
+
        struct bvec_iter        bi_iter;
 
        union {
@@ -402,6 +407,7 @@ enum req_flag_bits {
        __REQ_DRV,              /* for driver use */
        __REQ_FS_PRIVATE,       /* for file system (submitter) use */
        __REQ_ATOMIC,           /* for atomic write operations */
+       __REQ_DMABUF,           /* Using premmaped dma buffers */
        /*
         * Command specific flags, keep last:
         */
@@ -434,6 +440,7 @@ enum req_flag_bits {
 #define REQ_DRV                (__force blk_opf_t)(1ULL << __REQ_DRV)
 #define REQ_FS_PRIVATE (__force blk_opf_t)(1ULL << __REQ_FS_PRIVATE)
 #define REQ_ATOMIC     (__force blk_opf_t)(1ULL << __REQ_ATOMIC)
+#define REQ_DMABUF     (__force blk_opf_t)(1ULL << __REQ_DMABUF)
 
 #define REQ_NOUNMAP    (__force blk_opf_t)(1ULL << __REQ_NOUNMAP)
 
@@ -487,6 +494,11 @@ static inline bool op_is_discard(blk_opf_t op)
        return (op & REQ_OP_MASK) == REQ_OP_DISCARD;
 }
 
+static inline bool op_is_dmabuf(blk_opf_t op)
+{
+       return op & REQ_DMABUF;
+}
+
 /*
  * Check if a bio or request operation is a zone management operation.
  */
diff --git a/include/linux/bvec.h b/include/linux/bvec.h
index fc566ee1c1ff..b63914ff56e3 100644
--- a/include/linux/bvec.h
+++ b/include/linux/bvec.h
@@ -108,7 +108,8 @@ struct bvec_iter {
        unsigned int            bi_idx;
 
        /*
-        * Current offset in the bvec entry pointed to by `bi_idx`.
+        * Current offset in the bvec entry pointed to by `bi_idx` or into
+        * a dma-buf map.
         */
        unsigned int            bi_offset;
 } __packed __aligned(4);
-- 
2.54.0


Reply via email to