Add dmabuf read/write via io_uring
Subsystems (6)
v8 cover Add dmabuf read/write via io_uring
The patch set allows to register a dmabuf to an io_uring instance for a specified file and use it with io_uring read / write requests. The infrastructure is not tied to io_uring and there could be more users in the future. A similar idea was attempted some years ago by Keith [1], from where I borrowed a number of changes. Later it was brough back up to life by Tushar and Vishal.
It's an opt-in feature for files, and they need to implement a new file operation to use it. Only NVMe block devices are supported in this series. The user API is built on top of io_uring's "registered buffers", where a dmabuf is registered in a special way, but after it can be used as any other "registered buffer" with IORING_OP_{READ,WRITE}_FIXED requests. It's created via a new file operation and the resulted map is then passed through the I/O stack in a new iterator type. There is some additional infrastructure to glue it together, count requests, manage lifetime and implement invalidation.
Tushar, William, Phil did a lot of testing and experimentation on various devices with previous versions of the patch set, and I've received lots of help from Anuj, Kanchan and Nitesh with investigations, testing, and patching. Earlier benchmarks by Anuj for IOMMU optimisations with udmabuf showed:
STRICT: before = 570 KIOPS, after = 5.01 MIOPS
LAZY: before = 1.93 MIOPS, after = 5.01 MIOPS
PASSTHROUGH: before = 5.01 MIOPS, after = 5.01 MIOPS
# Patch set structure:
- Patches 1-2 introduce internal API and infrastructure mediating io_uring and target subsystem / devices
- Patches 3-5: block layer support + prep patches
- Patches 6-8 implement NVMe support
- Patches 9-13 add io_uring support and uapi.
There are some liburing tests that can serve as an example:
git: https://github.com/isilence/liburing.git rw-dmabuf-tests-v8
url: https://github.com/isilence/liburing/tree/rw-dmabuf-tests-v8
The patches are based on Jens' for-next. Also available as a branch:
git: https://github.com/isilence/linux.git rw-dmabuf-v8
url: https://github.com/isilence/linux/tree/rw-dmabuf-v8
[1] https://lore.kernel.org/io-uring/[email protected]/
v8: - fix blkdev_buffered_write() returning 0
- fix kvmalloc-kfree mismatch
- fix invalidation hang
- remove cpu dma sync as unnecessary for dev-to-dev transfers
- move nvme dma-buf code into a new file
- tighten dma-buf [re]export around io_uring issue
- simplify dma-buf-io locking/sync as proposed by Matthew Brost
v7: - refcount *_io_ctx
- wait rcu grace period for ctx->map before freeing maps
- increment map counters under dma resv lock
- more comments about bio splitting
- make ->unmap responsible for freeing maps to match ->map
- replace dma_sync_single* with full table sync
- detach dma-buf on nvme reset after cancel nvme_cancel_tagset
v6: - remove fences and wait on invalidation synchronously
- report all map requests are detached earlier, outside of wq
- handle nvme removal
- gate nvme_free_descriptors() on iod->nr_descriptors
- fix error handling in nvme_rq_setup_dmabuf_map()
- fix iov_iter_alignment()
- relax the 1G io_uring registered dma-buf limit
- fix type overflow issue in bio split
- allocate struct dma_buf_io_ctx inside the dma-buf-io code
- harden block checks against dma-buf + buffered io
v5: - Reject dma-buf with buffered IO for raw bdev
- Add lim->max_segments bio splitting
- Add bio_iov_iter_set() helper
- Fix io_uring uapi validation
- Other minor changes
NVMe:
- Rename nvme_pci_sgl_set_data() to nvme_pci_dma_iter_set_sgl(), split into its own prep patch.
- Convert segment walk loops to do-while.
- Drop adjacent segment coalescing logic.
- Drop first_dma/first_len from nvme_pci_dmabuf_sgl_nents()
- Remove entries > NVME_MAX_SEGS bailout; moving to the block layer.
- Factor SGL vs PRP decision making into a helper.
v4: - https://lore.kernel.org/all/[email protected]/
- Add sgl support from Anuj
- Move it under drivers/dma-buf/ and rename
- Fix mis-sized allocations
- Fix io_uring re-import mishandling
- Drop map before io_uring "task work"
- Move blk-mq callback to block_device_operations
- Convert bio flag to REQ_OP*
- Other small changes
v3: https://lore.kernel.org/io-uring/[email protected]/
- Rework io_uring registration
- Move token/map infrastructure code out of blk-mq
- Simplify callbacks: remove a separate blk-mq table, which was mostly just forwarding calls (to nvme).
- Don't skip dma sync depending on request direction
- Fix a couple of hangs
- Rename s/dma/dmabuf/
- Other small changes
v2: - Don't pass raw dma addresses, wrap it into a driver specific object
- Split into two objects: token and map
- Implement move_notify
Anuj Gupta (2):
nvme-pci: rename nvme_pci_sgl_set_data to nvme_pci_dma_iter_set_sgl
nvme-pci: add SGL support for the dmabuf path
Pavel Begunkov (11):
block: always adjust bi_offset on bio_advance_iter
block: introduce dma map backed bio type
block: add dma-buf support for raw bdev
nvme-pci: implement dma-buf backed requests nvme seaparate file
nvme sgl fixup
io_uring/rsrc: introduce buf registration structure io_uring/rsrc: extend buffer update io_uring/rsrc: add uncloneable regbuf flag io_uring/rsrc: add regbuf import flags io_uring/rsrc: add dmabuf backed registered buffers
block/bio.c | 15 +- block/blk-merge.c | 50 +++++++ block/fops.c | 24 +++- drivers/md/dm-io-rewind.c | 6 +- drivers/nvme/host/Makefile | 1 + drivers/nvme/host/core.c | 12 ++ drivers/nvme/host/nvme.h | 2 + drivers/nvme/host/pci-dmabuf.c | 164 ++++++++++++++++++++++ drivers/nvme/host/pci-dmabuf.h | 73 ++++++++++ drivers/nvme/host/pci.c | 249 ++++++++++++++++++++++++++++++++- include/linux/bio.h | 21 +-- include/linux/blk-mq.h | 7 + include/linux/blk_types.h | 14 +- include/linux/blkdev.h | 2 + include/linux/bvec.h | 3 +- include/linux/io_uring_types.h | 5 + include/uapi/linux/io_uring.h | 31 +++- io_uring/Makefile | 1 + io_uring/dma-buf.c | 68 +++++++++ io_uring/dma-buf.h | 67 +++++++++ io_uring/rsrc.c | 172 +++++++++++++++++------ io_uring/rsrc.h | 32 ++++- io_uring/rw.c | 33 +++-- 23 files changed, 974 insertions(+), 78 deletions(-) create mode 100644 drivers/nvme/host/pci-dmabuf.c create mode 100644 drivers/nvme/host/pci-dmabuf.h create mode 100644 io_uring/dma-buf.c create mode 100644 io_uring/dma-buf.h
Signature · 1 line
2.54.0
PATCH v8 1/13 block: always adjust bi_offset on bio_advance_iter
Extend bio_no_advance_iter() iteration to also increment the offset. It's not currently needed because the currently listed request types don't have a buffer, but will be useful once we add bios backed by dma-buf, which don't have a bvec but work with a single range.
--- drivers/md/dm-io-rewind.c | 6 ++++-- include/linux/bio.h | 12 ++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-)
drivers/md/dm-io-rewind.c +4 -2
@@ -113,10 +113,12 @@ static inline void dm_bio_rewind_iter(const struct bio *bio, iter->bi_sector -= bytes >>9 ;/* No advance means no rewind */ -if (bio_no_advance_iter (bio)) +if (bio_no_advance_iter (bio)) { iter->bi_size += bytes; -else + iter->bi_offset -= bytes; + }else {dm_bvec_iter_rewind (bio->bi_io_vec, iter, bytes); + } }/**
include/linux/bio.h +8 -4
@@ -113,11 +113,13 @@ static inline void bio_advance_iter(const struct bio *bio, { iter->bi_sector += bytes >>9 ; -if (bio_no_advance_iter (bio)) +if (bio_no_advance_iter (bio)) { iter->bi_size -= bytes; -else + iter->bi_offset += bytes; + }else {bvec_iter_advance (bio->bi_io_vec, iter, bytes);/* TODO: It is reasonable to complete bio with error here. */ + } }/* @bytes should be less or equal to bvec[i->bi_idx].bv_len */
@@ -127,10 +129,12 @@ static inline void bio_advance_iter_single(const struct bio *bio, { iter->bi_sector += bytes >>9 ; -if (bio_no_advance_iter (bio)) +if (bio_no_advance_iter (bio)) { iter->bi_size -= bytes; -else + iter->bi_offset += bytes; + }else {bvec_iter_advance_single (bio->bi_io_vec, iter, bytes); + } }void __bio_advance (struct bio *,unsigned bytes);
Signature · 1 line
2.54.0
PATCH v8 2/13 block: introduce dma map backed bio type
In reply to Pavel BegunkovPremapped buffers don't require a generic bio_vec since these have already been dma mapped. Repurpose the bi_io_vec space to store dmabuf maps as they are mutually exclusive.
The bio splitting differs from the normal path because it's already pre-mapped and for the block layer it's just an offset into the dma-buf. The actual segmentation is only available to the importer driver and not the block layer, however, it doesn't contain alignment gaps and we don't have to check it. For the same reason we can't precisely split by the number of segments, but we use ->min_seg_shift stored in the map to calculate the minimum number of bytes a request consisting of lim->max_segments full segments can cover and split by that. It's stricter and can add extra splitting. E.g. for a {4K, 4G} segmentation, the min segment size is 4K, and we'll split it into bios of (4K * lim->max_segments) bytes each, but it should be good enough for now to cover the most popular use cases.
--- block/bio.c | 15 ++++++++++-- block/blk-merge.c | 50 +++++++++++++++++++++++++++++++++++++++ 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, 91 insertions(+), 9 deletions(-)
block/bio.c +13 -2
@@ -881,7 +881,11 @@ static int __bio_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp) bio->bi_write_stream = bio_src->bi_write_stream; bio->bi_bvec_gap_bit = bio_src->bi_bvec_gap_bit; 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 &&
@@ -1204,16 +1208,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; }
block/blk-merge.c +50 -0
74 lines not on this page · raw message
block/fops.c +1 -1
@@ -362,7 +362,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);
include/linux/bio.h +5 -4
@@ -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); }
include/linux/blk-mq.h +7 -0
@@ -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;
include/linux/blk_types.h +13 -1
@@ -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. */
include/linux/bvec.h +2 -1
@@ -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) ;
Signature · 1 line
2.54.0
PATCH v8 3/13 block: add dma-buf support for raw bdev
In reply to Pavel BegunkovAdd a simple proxy implementation of init_dma_buf_io_ctx() forwarding the call to a new struct block_device_operations operation. Also reject dma-buf backed iterators for buffered IO.
--- block/fops.c | 22 ++++++++++++++++++++++ include/linux/blkdev.h | 2 ++ 2 files changed, 24 insertions(+)
block/fops.c +22 -0
@@ -730,6 +730,9 @@ blkdev_direct_write(struct kiocb *iocb, struct iov_iter *from)static ssize_t blkdev_buffered_write (struct kiocb *iocb,struct iov_iter *from) { +if (unlikely (iov_iter_is_dmabuf_map (from))) +return -EOPNOTSUPP ; +return iomap_file_buffered_write (iocb, from, &blkdev_iomap_ops,NULL ,NULL ); }
@@ -855,6 +858,11 @@ static ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to)if (ret <0 || !count) goto reexpand; } +if (unlikely (iov_iter_is_dmabuf_map (to))) { +if (!ret) + ret = -EOPNOTSUPP ; + goto reexpand; + }/* * Take i_rwsem and invalidate_lock to avoid racing with set_blocksize
@@ -958,6 +966,19 @@ static int blkdev_mmap_prepare(struct vm_area_desc *desc)return generic_file_mmap_prepare (desc); } +static int blkdev_init_dma_buf_io_ctx (struct file *file, +struct dma_buf_io_ctx *ctx) +{ +struct block_device *bdev =file_bdev (file); +struct gendisk *disk = bdev->bd_disk; + +if (!(file->f_flags &O_DIRECT )) +return -EINVAL ; +if (!disk->fops->init_dma_buf_io_ctx) +return -EOPNOTSUPP ; +return disk->fops->init_dma_buf_io_ctx (bdev, ctx); +} +const struct file_operations def_blk_fops = { .open = blkdev_open, .release = blkdev_release,
@@ -976,6 +997,7 @@ const struct file_operations def_blk_fops = { .fallocate = blkdev_fallocate, .uring_cmd = blkdev_uring_cmd, .fop_flags = FOP_BUFFER_RASYNC | FOP_DONTCACHE, + .init_dma_buf_io_ctx = blkdev_init_dma_buf_io_ctx, };static __init int blkdev_init (void )
include/linux/blkdev.h +2 -0
@@ -1599,6 +1599,8 @@ struct block_device_operations {/* returns the length of the identifier or a negative errno: */ int (*get_unique_id)(struct gendisk *disk,u8 id[16 ],enum blk_unique_id id_type); +int (*init_dma_buf_io_ctx)(struct block_device *, +struct dma_buf_io_ctx *);struct module *owner;const struct pr_ops *pr_ops;
Signature · 1 line
2.54.0
[email protected]
AI reviewRe: [PATCH v8 03/13] block: add dma-buf support for raw bdev
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] vfs: build failure due to missing init_dma_buf_io_ctx and iov_iter_is_dmabuf_map definitions --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] vfs: build failure due to missing init_dma_buf_io_ctx and iov_iter_is_dmabuf_map definitions --
commit d99a726724959c24584b1b7d60b6cbfd1f565d00
Author: Pavel Begunkov <[email protected]>
block: add dma-buf support for raw bdev
This commit adds a proxy implementation of init_dma_buf_io_ctx() that forwards the call to a new block_device_operations operation. It also rejects dma-buf backed iterators for buffered IO.
block/fops.c blkdev_direct_write(struct kiocb *iocb, struct iov_iter *from) · line 734
Quoted 9 lines
block/fops.c +1
-0@@ -731,6 +731,9 @@ blkdev_direct_write(struct kiocb *iocb, struct iov_iter *from)static ssize_t blkdev_buffered_write (struct kiocb *iocb,struct iov_iter *from) { +if (unlikely (iov_iter_is_dmabuf_map (from)))
[Severity: Low]
Is this regression caused by a missing prerequisite patch? The function iov_iter_is_dmabuf_map() appears to be undeclared in the kernel headers, which results in an implicit declaration error during the build.
Quoted 5 lines
block/fops.c +2
-0+return -EOPNOTSUPP ; +return iomap_file_buffered_write (iocb, from, &blkdev_iomap_ops,NULL ,NULL ); }
[ ... ]
block/fops.c const struct file_operations def_blk_fops = { · line 1001
Quoted 5 lines
block/fops.c +1
-0@@ -977,6 +998,7 @@ const struct file_operations def_blk_fops = { .fallocate = blkdev_fallocate, .uring_cmd = blkdev_uring_cmd, .fop_flags = FOP_BUFFER_RASYNC | FOP_DONTCACHE, + .init_dma_buf_io_ctx = blkdev_init_dma_buf_io_ctx,
[Severity: Low]
Does struct file_operations include init_dma_buf_io_ctx in this tree? It seems this member does not exist, which causes a build regression due to the initialization of a non-existent member.
Quoted 3 lines
block/fops.c +0
-0};static __init int blkdev_init (void )
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
PATCH v8 4/13 nvme-pci: rename nvme_pci_sgl_set_data to nvme_pci_dma_iter_set_sgl
In reply to Pavel BegunkovFrom: Anuj Gupta <[email protected]>
Rename the blk_dma_iter based nvme_pci_sgl_set_data() to nvme_pci_dma_iter_set_sgl().
--- drivers/nvme/host/pci.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/nvme/host/pci.c +5 -5
@@ -1147,7 +1147,7 @@ static blk_status_t nvme_pci_setup_data_prp(struct request *req,return BLK_STS_IOERR ; } -static void nvme_pci_sgl_set_data (struct nvme_sgl_desc *sge, +static void nvme_pci_dma_iter_set_sgl (struct nvme_sgl_desc *sge,struct blk_dma_iter *iter) { sge->addr =cpu_to_le64 (iter->addr);
@@ -1177,7 +1177,7 @@ static blk_status_t nvme_pci_setup_data_sgl(struct request *req, iod->cmd.common.flags =NVME_CMD_SGL_METABUF ;if (entries ==1 ||blk_rq_dma_map_coalesce (&iod->dma_state)) { -nvme_pci_sgl_set_data (&iod->cmd.common.dptr.sgl, iter); +nvme_pci_dma_iter_set_sgl (&iod->cmd.common.dptr.sgl, iter); iod->total_len += iter->len;return BLK_STS_OK ; }
@@ -1199,7 +1199,7 @@ static blk_status_t nvme_pci_setup_data_sgl(struct request *req, iter->status =BLK_STS_IOERR ;break ; } -nvme_pci_sgl_set_data (&sg_list[mapped++], iter); +nvme_pci_dma_iter_set_sgl (&sg_list[mapped++], iter); iod->total_len += iter->len; }while (blk_rq_dma_map_iter_next (req, nvmeq->dev->dev, iter));
@@ -1359,13 +1359,13 @@ static blk_status_t nvme_pci_setup_meta_iter(struct request *req) iod->cmd.common.metadata =cpu_to_le64 (sgl_dma);if (entries ==1 ) { iod->meta_total_len = iter.len; -nvme_pci_sgl_set_data (sg_list, &iter); +nvme_pci_dma_iter_set_sgl (sg_list, &iter);return BLK_STS_OK ; } sgl_dma +=sizeof (*sg_list);do { -nvme_pci_sgl_set_data (&sg_list[++i], &iter); +nvme_pci_dma_iter_set_sgl (&sg_list[++i], &iter); iod->meta_total_len += iter.len; }while (blk_rq_integrity_dma_map_iter_next (req, dev->dev, &iter));
Signature · 1 line
2.54.0
PATCH v8 5/13 nvme-pci: implement dma-buf backed requests
In reply to Pavel BegunkovEnable BIO_DMABUF_MAP backed requests. On registration we map the dma-buf and store it as a prp list, which is then used to initialise requests. All attached contexts are stored in a new list dmabuf_ctxs, and additions/removals are synchronised with dmabuf_lock.
--- drivers/nvme/host/core.c | 12 ++ drivers/nvme/host/nvme.h | 2 + drivers/nvme/host/pci.c | 305 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+)
drivers/nvme/host/core.c +12 -0
@@ -2708,6 +2708,17 @@ static int nvme_report_zones(struct gendisk *disk, sector_t sector,#define nvme_report_zones NULL#endif /* CONFIG_BLK_DEV_ZONED */ +static int nvme_init_dma_buf_io_ctx (struct block_device *bdev, +struct dma_buf_io_ctx *ctx) +{ +struct nvme_ns *ns = bdev->bd_disk->private_data; +struct nvme_ctrl *ctrl = ns->ctrl; + +if (!ctrl->ops->init_dma_buf_io_ctx) +return -EINVAL ; +return ctrl->ops->init_dma_buf_io_ctx (ctrl, ctx); +} +const struct block_device_operations nvme_bdev_ops = { .owner =THIS_MODULE , .ioctl = nvme_ioctl,
@@ -2718,6 +2729,7 @@ const struct block_device_operations nvme_bdev_ops = { .get_unique_id = nvme_get_unique_id, .report_zones = nvme_report_zones, .pr_ops = &nvme_pr_ops, + .init_dma_buf_io_ctx = nvme_init_dma_buf_io_ctx, }; static int nvme_wait_ready(struct nvme_ctrl *ctrl, u32 mask, u32 val,
drivers/nvme/host/nvme.h +2 -0
@@ -668,6 +668,8 @@ struct nvme_ctrl_ops {int (*get_address)(struct nvme_ctrl *ctrl,char *buf,int size);void (*print_device_info)(struct nvme_ctrl *ctrl);bool (*supports_pci_p2pdma)(struct nvme_ctrl *ctrl); +int (*init_dma_buf_io_ctx)(struct nvme_ctrl *ctrl, +struct dma_buf_io_ctx *ctx);unsigned long (*get_virt_boundary)(struct nvme_ctrl *ctrl,bool is_admin); };
drivers/nvme/host/pci.c +305 -0
370 lines not on this page · raw message
Signature · 1 line
2.54.0
PATCH v8 6/13 nvme seaparate file
In reply to Pavel Begunkov--- drivers/nvme/host/Makefile | 1 + drivers/nvme/host/pci-dmabuf.c | 164 ++++++++++++++++++++++ drivers/nvme/host/pci-dmabuf.h | 73 ++++++++++ drivers/nvme/host/pci.c | 246 +++------------------------------ 4 files changed, 255 insertions(+), 229 deletions(-) create mode 100644 drivers/nvme/host/pci-dmabuf.c create mode 100644 drivers/nvme/host/pci-dmabuf.h
drivers/nvme/host/Makefile +1 -0
@@ -21,6 +21,7 @@ nvme-core-$(CONFIG_NVME_HWMON) += hwmon.o nvme-core-$(CONFIG_NVME_HOST_AUTH) += auth.o nvme-y += pci.o +nvme-$(CONFIG_DMA_SHARED_BUFFER) += pci-dmabuf.o nvme-fabrics-y += fabrics.o
drivers/nvme/host/pci-dmabuf.c new file +164 -0
164 lines not on this page · raw message
drivers/nvme/host/pci-dmabuf.h new file +73 -0
74 lines not on this page · raw message
drivers/nvme/host/pci.c +17 -229
326 lines not on this page · raw message
Signature · 1 line
2.54.0
PATCH v8 7/13 nvme-pci: add SGL support for the dmabuf path
In reply to Pavel BegunkovFrom: Anuj Gupta <[email protected]>
Add SGL support in addition to PRP for dmabuf-backed requests, building the descriptor list from the mapping's sg_table.
--- drivers/nvme/host/pci.c | 153 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 149 insertions(+), 4 deletions(-)
drivers/nvme/host/pci.c +149 -4
@@ -1223,12 +1223,18 @@ static blk_status_t nvme_pci_setup_data_prp(struct request *req,return BLK_STS_IOERR ; } +static void nvme_pci_sgl_set_data (struct nvme_sgl_desc *sge, +dma_addr_t addr,u32 len) +{ + sge->addr =cpu_to_le64 (addr); + sge->length =cpu_to_le32 (len); + sge->type =NVME_SGL_FMT_DATA_DESC <<4 ; +} +static void nvme_pci_dma_iter_set_sgl (struct nvme_sgl_desc *sge,struct blk_dma_iter *iter) { - sge->addr =cpu_to_le64 (iter->addr); - sge->length =cpu_to_le32 (iter->len); - sge->type =NVME_SGL_FMT_DATA_DESC <<4 ; +nvme_pci_sgl_set_data (sge, iter->addr, iter->len); } static void nvme_pci_sgl_set_seg(struct nvme_sgl_desc *sge,
@@ -1239,6 +1245,145 @@ static void nvme_pci_sgl_set_seg(struct nvme_sgl_desc *sge, sge->type =NVME_SGL_FMT_LAST_SEG_DESC <<4 ; } +static unsigned int nvme_pci_dmabuf_sgl_nents (struct request *req) +{ +struct bio *bio = req->bio; +struct nvme_dmabuf_map *map =to_nvme_dmabuf_map (bio->bi_dmabuf_map); +struct scatterlist *sg; +unsigned long tmp; +size_t offset = bio->bi_iter.bi_offset; +size_t remaining =blk_rq_payload_bytes (req); +unsigned int nents =0 ; + +for_each_sgtable_dma_sg (map->sgt, sg, tmp) { +size_t sg_len =sg_dma_len (sg); + +if (!remaining) +break ; +if (offset >= sg_len) { + offset -= sg_len; +continue ; + } + + sg_len -= offset; + offset =0 ; + +do { +size_t chunk =min (remaining, sg_len); + + nents++; + sg_len -= chunk; + remaining -= chunk; + }while (sg_len && remaining); + } + +if (unlikely (remaining)) +return 0 ; + +return nents; +} + +static blk_status_t nvme_rq_setup_dmabuf_sgl (struct request *req, +struct nvme_queue *nvmeq,unsigned int entries) +{ +struct nvme_iod *iod =blk_mq_rq_to_pdu (req); +struct bio *bio = req->bio; +struct nvme_dmabuf_map *map =to_nvme_dmabuf_map (bio->bi_dmabuf_map); +size_t length =blk_rq_payload_bytes (req); +struct nvme_sgl_desc *sg_list = &iod->cmd.common.dptr.sgl; +bool pooled = entries >1 ; +dma_addr_t sgl_dma =0 ; +unsigned int mapped =0 ; +unsigned long tmp; +struct scatterlist *sg; +size_t offset = bio->bi_iter.bi_offset; +size_t remaining = length; + +if (!entries) +return BLK_STS_IOERR ; + + iod->cmd.common.flags =NVME_CMD_SGL_METABUF ; + iod->total_len = length; + +nvme_dmabuf_map_sync_for_device (nvmeq->dev, req); + +/* entries == 1 fits in the inline descriptor; more needs a pool. */ +if (pooled) { +if (entries <=NVME_SMALL_POOL_SIZE /sizeof (*sg_list)) + iod->flags |=IOD_SMALL_DESCRIPTOR ; + + sg_list =dma_pool_alloc (nvme_dma_pool (nvmeq, iod),GFP_ATOMIC , + &sgl_dma); +if (!sg_list) +return BLK_STS_RESOURCE ; + iod->descriptors[iod->nr_descriptors++] = sg_list; + } + +for_each_sgtable_dma_sg (map->sgt, sg, tmp) { +size_t sg_len =sg_dma_len (sg); +dma_addr_t addr =sg_dma_address (sg); + +if (!remaining) +break ; +if (offset >= sg_len) { + offset -= sg_len; +continue ; + } + + addr += offset; + sg_len -= offset; + offset =0 ; + +do { +u32 chunk =min_t (size_t, remaining, sg_len); + +if (WARN_ON_ONCE (mapped == entries)) + goto err_free; +nvme_pci_sgl_set_data (&sg_list[mapped++], addr, chunk); + + addr += chunk; + sg_len -= chunk; + remaining -= chunk; + }while (sg_len && remaining); + } + +if (unlikely (remaining)) + goto err_free; + +if (pooled) +nvme_pci_sgl_set_seg (&iod->cmd.common.dptr.sgl, sgl_dma, + mapped); +return BLK_STS_OK ; +err_free: +if (pooled) { + iod->nr_descriptors--; +dma_pool_free (nvme_dma_pool (nvmeq, iod), sg_list, sgl_dma); + } +return BLK_STS_IOERR ; +} + +static blk_status_t nvme_rq_setup_dmabuf (struct request *req, +struct nvme_queue *nvmeq,enum nvme_use_sgl use_sgl) +{ +unsigned int entries; +size_t avg_seg; + +if (use_sgl ==SGL_UNSUPPORTED ) +return nvme_rq_setup_dmabuf_map (req, nvmeq); + + entries =nvme_pci_dmabuf_sgl_nents (req); + +if (use_sgl ==SGL_FORCED ) +return nvme_rq_setup_dmabuf_sgl (req, nvmeq, entries); + + avg_seg = entries ? +DIV_ROUND_UP (blk_rq_payload_bytes (req), entries) :0 ; +if (sgl_threshold && avg_seg >= sgl_threshold) +return nvme_rq_setup_dmabuf_sgl (req, nvmeq, entries); + +return nvme_rq_setup_dmabuf_map (req, nvmeq); +} +static blk_status_t nvme_pci_setup_data_sgl (struct request *req,struct blk_dma_iter *iter) {
@@ -1334,7 +1479,7 @@ static blk_status_t nvme_map_data(struct request *req)blk_status_t ret;if (blk_mq_rq_is_dmabuf (req)) -return nvme_rq_setup_dmabuf_map (req, nvmeq); +return nvme_rq_setup_dmabuf (req, nvmeq, use_sgl);/* * Try to skip the DMA iterator for single segment requests, as that
Signature · 1 line
2.54.0
PATCH v8 8/13 nvme sgl fixup
In reply to Pavel Begunkov--- drivers/nvme/host/pci.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)
drivers/nvme/host/pci.c +2 -5
@@ -1287,16 +1287,15 @@ static blk_status_t nvme_rq_setup_dmabuf_sgl(struct request *req, struct nvme_queue *nvmeq, unsigned int entries) {struct nvme_iod *iod =blk_mq_rq_to_pdu (req); -struct bio *bio = req->bio; -struct nvme_dmabuf_map *map =to_nvme_dmabuf_map (bio->bi_dmabuf_map); +struct nvme_dmabuf_map *map =nvme_req_to_map (req);size_t length =blk_rq_payload_bytes (req);struct nvme_sgl_desc *sg_list = &iod->cmd.common.dptr.sgl; +size_t offset = req->bio->bi_iter.bi_offset;bool pooled = entries >1 ;dma_addr_t sgl_dma =0 ;unsigned int mapped =0 ;unsigned long tmp;struct scatterlist *sg; -size_t offset = bio->bi_iter.bi_offset;size_t remaining = length;if (!entries)
@@ -1305,8 +1304,6 @@ static blk_status_t nvme_rq_setup_dmabuf_sgl(struct request *req, iod->cmd.common.flags =NVME_CMD_SGL_METABUF ; iod->total_len = length; -nvme_dmabuf_map_sync_for_device (nvmeq->dev, req); -/* entries == 1 fits in the inline descriptor; more needs a pool. */ if (pooled) {if (entries <=NVME_SMALL_POOL_SIZE /sizeof (*sg_list))
Signature · 1 line
2.54.0
PATCH v8 9/13 io_uring/rsrc: introduce buf registration structure
In reply to Pavel BegunkovIn preparation to following changes, instead of passing an iovec for buffer registration introduce a new structure. It'll be moved to uapi later, but for now it's initialised early from a user provided iovec.
--- io_uring/rsrc.c | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-)
io_uring/rsrc.c +32 -15
@@ -27,8 +27,13 @@ struct io_rsrc_update {u32 offset; }; +struct io_uring_regbuf_desc { +__u64 uaddr; +__u64 size; +}; +static struct io_rsrc_node *io_sqe_buffer_register (struct io_ring_ctx *ctx, -struct iovec *iov); +struct io_uring_regbuf_desc *desc);static int hpage_acct_ref (struct io_ring_ctx *ctx,struct page *hpage,bool *acct_new)
@@ -81,6 +86,15 @@ static bool hpage_acct_unref(struct io_ring_ctx *ctx, struct page *hpage)#define IO_CACHED_BVECS_SEGS 32 +static void io_iov_to_regbuf_desc (const struct iovec *iov, +struct io_uring_regbuf_desc *desc) +{ + *desc = (struct io_uring_regbuf_desc ) { + .uaddr = (u64 )(uintptr_t )iov->iov_base, + .size = iov->iov_len, + }; +} +int __io_account_mem (struct user_struct *user,unsigned long nr_pages) {unsigned long page_limit, cur_pages, new_pages;
@@ -398,6 +412,7 @@ static int __io_sqe_buffers_update(struct io_ring_ctx *ctx,return -EINVAL ;for (done =0 ; done < nr_args; done++) { +struct io_uring_regbuf_desc desc;struct io_rsrc_node *node;u64 tag =0 ;
@@ -411,7 +426,9 @@ static int __io_sqe_buffers_update(struct io_ring_ctx *ctx, err = -EFAULT ;break ; } - node =io_sqe_buffer_register (ctx, iov); + +io_iov_to_regbuf_desc (iov, &desc); + node =io_sqe_buffer_register (ctx, &desc);if (IS_ERR (node)) { err =PTR_ERR (node);break ;
@@ -870,26 +887,26 @@ bool io_check_coalesce_buffer(struct page **page_array, int nr_pages, }static struct io_rsrc_node *io_sqe_buffer_register (struct io_ring_ctx *ctx, -struct iovec *iov) +struct io_uring_regbuf_desc *desc) { +unsigned long uaddr = (unsigned long )desc->uaddr; +size_t size = desc->size;struct io_mapped_ubuf *imu =NULL ;struct page **pages =NULL ;struct io_rsrc_node *node;unsigned long off; -size_t size;int ret, nr_pages, i;struct io_imu_folio_data data;bool coalesced = false; -if (!iov->iov_base) { -if (iov->iov_len) +if (!uaddr) { +if (size)return ERR_PTR (-EFAULT );/* remove the buffer without installing a new one */ return NULL ; } - ret =io_validate_user_buf_range ((unsigned long )iov->iov_base, - iov->iov_len); + ret =io_validate_user_buf_range (uaddr, size);if (ret)return ERR_PTR (ret);
@@ -898,8 +915,7 @@ static struct io_rsrc_node *io_sqe_buffer_register(struct io_ring_ctx *ctx,return ERR_PTR (-ENOMEM ); ret = -ENOMEM ; - pages =io_pin_pages ((unsigned long ) iov->iov_base, iov->iov_len, - &nr_pages); + pages =io_pin_pages (uaddr, size, &nr_pages);if (IS_ERR (pages)) { ret =PTR_ERR (pages); pages =NULL ;
@@ -921,10 +937,9 @@ static struct io_rsrc_node *io_sqe_buffer_register(struct io_ring_ctx *ctx,if (ret) goto done; - size = iov->iov_len;/* store original address for later verification */ - imu->ubuf = (unsigned long ) iov->iov_base; - imu->len = iov->iov_len; + imu->ubuf = uaddr; + imu->len = size; imu->folio_shift =PAGE_SHIFT ; imu->release = io_release_ubuf; imu->priv = imu;
@@ -934,7 +949,7 @@ static struct io_rsrc_node *io_sqe_buffer_register(struct io_ring_ctx *ctx, imu->folio_shift = data.folio_shift;refcount_set (&imu->refs,1 ); - off = (unsigned long )iov->iov_base & ~PAGE_MASK ; + off = uaddr & ~PAGE_MASK ;if (coalesced) off += data.first_folio_page_idx <<PAGE_SHIFT ;
@@ -986,6 +1001,7 @@ int io_sqe_buffers_register(struct io_ring_ctx *ctx, void __user *arg,memset (iov,0 ,sizeof (*iov));for (i =0 ; i < nr_args; i++) { +struct io_uring_regbuf_desc desc;struct io_rsrc_node *node;u64 tag =0 ;
@@ -1009,7 +1025,8 @@ int io_sqe_buffers_register(struct io_ring_ctx *ctx, void __user *arg, } } - node =io_sqe_buffer_register (ctx, iov); +io_iov_to_regbuf_desc (iov, &desc); + node =io_sqe_buffer_register (ctx, &desc);if (IS_ERR (node)) { ret =PTR_ERR (node);break ;
Signature · 1 line
2.54.0
PATCH v8 10/13 io_uring/rsrc: extend buffer update
In reply to Pavel BegunkovWe need to pass more information to buffer registration than we can fit into a single struct iovec. This patch allows users to optionally pass struct io_uring_regbuf_desc. Apart from having more space for future use cases, it also introduces registration types.
Currently, the type can be either of IO_REGBUF_TYPE_UADDR, which mirrors the iovec path, or IO_REGBUF_TYPE_EMPTY for leaving a buffer table slot empty. The next patch introduces a dmabuf backed type, and can be useful for other extensions like splicing a list of user addresses (i.e. iovec[]), interoperability with zcrx, kernel allocated memory like was brough up by Cristoph. Note, the type only represents a registration option, which is distinct from how io_uring internally stores it.
The flags field is not used yet but always useful to have, e.g. we can encode read-only / write-only restrictions using it.
--- include/uapi/linux/io_uring.h | 27 +++++++++++++- io_uring/rsrc.c | 69 ++++++++++++++++++++++------------- 2 files changed, 69 insertions(+), 27 deletions(-)
include/uapi/linux/io_uring.h +26 -1
@@ -791,13 +791,38 @@ struct io_uring_rsrc_update {struct io_uring_rsrc_update2 {__u32 offset; -__u32 resv; +__u32 flags;__aligned_u64 data;__aligned_u64 tags;__u32 nr;__u32 resv2; }; +/* struct io_uring_rsrc_update2::flags */ +enum io_uring_rsrc_reg_flags { +/* +* Use the extended descriptor format for buffer updates, +* see struct io_uring_regbuf_desc +*/ +IORING_RSRC_UPDATE_EXTENDED =1U <<1 , +}; + +/* Buffer registration type, passed in struct io_uring_regbuf_desc::type */ +enum io_uring_regbuf_type { +IO_REGBUF_TYPE_EMPTY , +IO_REGBUF_TYPE_UADDR , + + __IO_REGBUF_TYPE_MAX, +}; + +struct io_uring_regbuf_desc { +__u32 type;/* enum io_uring_regbuf_type */ +__u32 flags; +__u64 size; +__u64 uaddr; +__u64 __resv[7 ]; +}; +/* Skip updating fd indexes set to this value in the fd table */ #define IORING_REGISTER_FILES_SKIP (-2)
io_uring/rsrc.c +43 -26
151 lines not on this page · raw message
Signature · 1 line
2.54.0
PATCH v8 11/13 io_uring/rsrc: add uncloneable regbuf flag
In reply to Pavel BegunkovIt's hard to implement cloning if the internal structure needs to be mutable and/or relies on other ring resources. In preparation to such buffer types, add a flag indicating that the buffer can't be cloned. It might be possible to add cloning in the future for them, but that would likely need reallocating the structure and reacquiring resources in case by case manner.
--- io_uring/rsrc.c | 5 +++++ io_uring/rsrc.h | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-)
io_uring/rsrc.c +5 -0
@@ -1456,6 +1456,11 @@ static int io_clone_buffers(struct io_ring_ctx *ctx, struct io_ring_ctx *src_ctxif (!src_node) { dst_node =NULL ; }else { +if (src_node->buf->flags &IO_REGBUF_F_UNCLONEABLE ) { +io_rsrc_data_free (ctx, &data); +return -EOPNOTSUPP ; + } + dst_node =io_rsrc_node_alloc (ctx,IORING_RSRC_BUFFER );if (!dst_node) {io_rsrc_data_free (ctx, &data);
io_uring/rsrc.h +2 -1
@@ -26,7 +26,8 @@ struct io_rsrc_node { };enum { -IO_REGBUF_F_KBUF =1 , +IO_REGBUF_F_KBUF =1 <<0 , +IO_REGBUF_F_UNCLONEABLE =1 <<1 , };struct io_mapped_ubuf {
Signature · 1 line
2.54.0
PATCH v8 12/13 io_uring/rsrc: add regbuf import flags
In reply to Pavel BegunkovWe'll have special registered buffer types that can't be used with all opcodes and need special handling. Add separate flags to control registered buffer import, which will be used to specify what kind of buffers the caller can handle.
--- io_uring/rsrc.c | 8 ++++---- io_uring/rsrc.h | 24 ++++++++++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-)
io_uring/rsrc.c +4 -4
@@ -1315,9 +1315,9 @@ inline struct io_rsrc_node *io_find_buf_node(struct io_kiocb *req,return NULL ; } -int io_import_reg_buf (struct io_kiocb *req,struct iov_iter *iter, +int __io_import_reg_buf (struct io_kiocb *req,struct iov_iter *iter,u64 buf_addr,size_t len,int ddir, -unsigned issue_flags) +unsigned issue_flags,unsigned import_flags) {struct io_rsrc_node *node;
@@ -1731,9 +1731,9 @@ static int io_kern_bvec_size(struct iovec *iov, unsigned nr_iovs,return 0 ; } -int io_import_reg_vec (int ddir,struct iov_iter *iter, +int __io_import_reg_vec (int ddir,struct iov_iter *iter,struct io_kiocb *req,struct iou_vec *vec, -unsigned nr_iovs,unsigned issue_flags) +unsigned nr_iovs,unsigned issue_flags,unsigned import_flags) {struct io_rsrc_node *node;struct io_mapped_ubuf *imu;
io_uring/rsrc.h +20 -4
@@ -62,12 +62,28 @@ int io_rsrc_data_alloc(struct io_rsrc_data *data, unsigned nr);struct io_rsrc_node *io_find_buf_node (struct io_kiocb *req,unsigned issue_flags); -int io_import_reg_buf (struct io_kiocb *req,struct iov_iter *iter, +int __io_import_reg_buf (struct io_kiocb *req,struct iov_iter *iter,u64 buf_addr,size_t len,int ddir, -unsigned issue_flags); -int io_import_reg_vec (int ddir,struct iov_iter *iter, +unsigned issue_flags,unsigned import_flags); +int __io_import_reg_vec (int ddir,struct iov_iter *iter,struct io_kiocb *req,struct iou_vec *vec, -unsigned nr_iovs,unsigned issue_flags); +unsigned nr_iovs,unsigned issue_flags, +unsigned import_flags); + +static inline int io_import_reg_buf (struct io_kiocb *req,struct iov_iter *iter, +u64 buf_addr,size_t len,int ddir, +unsigned issue_flags) +{ +return __io_import_reg_buf (req, iter, buf_addr, len, ddir, issue_flags,0 ); +} + +static inline int io_import_reg_vec (int ddir,struct iov_iter *iter, +struct io_kiocb *req,struct iou_vec *vec, +unsigned nr_iovs,unsigned issue_flags) +{ +return __io_import_reg_vec (ddir, iter, req, vec, nr_iovs, issue_flags,0 ); +} +int io_prep_reg_iovec (struct io_kiocb *req,struct iou_vec *iv,const struct iovec __user *uvec,size_t uvec_segs);
Signature · 1 line
2.54.0
PATCH v8 13/13 io_uring/rsrc: add dmabuf backed registered buffers
In reply to Pavel BegunkovImplement dmabuf backed registered buffers. To register them, the user should specify IO_REGBUF_TYPE_DMABUF for the regitration and pass the desired dmabuf fd and a file for which it should be registered.
From there, it can be used with io_uring read/write requests IORING_OP_{READ,WRITE}_FIXED) as normal. The requests should be issued against the file specified during registration, and otherwise they'll be failed. The user should also be prepared to handle spurious -EAGAIN by reissuing the request.
Internally, dmabuf registered buffers is an optin feature for io_uring request opcodes and they should pass a special flag on import to use it.
--- include/linux/io_uring_types.h | 5 +++ include/uapi/linux/io_uring.h | 6 ++- io_uring/Makefile | 1 + io_uring/dma-buf.c | 68 ++++++++++++++++++++++++++++++++++ io_uring/dma-buf.h | 67 +++++++++++++++++++++++++++++++++ io_uring/rsrc.c | 59 +++++++++++++++++++++++++++-- io_uring/rsrc.h | 5 +++ io_uring/rw.c | 33 ++++++++++++----- 8 files changed, 230 insertions(+), 14 deletions(-) create mode 100644 io_uring/dma-buf.c create mode 100644 io_uring/dma-buf.h
include/linux/io_uring_types.h +5 -0
@@ -11,6 +11,7 @@struct iou_loop_params ;struct io_uring_bpf_ops ; +struct dma_buf_io_map ;enum {/*
@@ -610,6 +611,7 @@ enum {REQ_F_IMPORT_BUFFER_BIT ,REQ_F_SQE_COPIED_BIT ,REQ_F_IOPOLL_BIT , +REQ_F_DMABUF_BIT ,/* not a real bit, just to check we're not overflowing the space */ __REQ_F_LAST_BIT,
@@ -705,6 +707,8 @@ enum {REQ_F_SQE_COPIED =IO_REQ_FLAG (REQ_F_SQE_COPIED_BIT ),/* request must be iopolled to completion (set in ->issue()) */ REQ_F_IOPOLL =IO_REQ_FLAG (REQ_F_IOPOLL_BIT ), +/* there is a dma map attached to request that needs to be dropped */ + REQ_F_DMABUF = IO_REQ_FLAG(REQ_F_DMABUF_BIT), }; struct io_tw_req {
@@ -827,6 +831,7 @@ struct io_kiocb {/* custom credentials, valid IFF REQ_F_CREDS is set */ const struct cred *creds;struct io_wq_work work; +struct dma_buf_io_map *dmabuf_map;struct io_big_cqe {u64 extra1;
include/uapi/linux/io_uring.h +5 -1
@@ -811,6 +811,7 @@ enum io_uring_rsrc_reg_flags {enum io_uring_regbuf_type {IO_REGBUF_TYPE_EMPTY ,IO_REGBUF_TYPE_UADDR , +IO_REGBUF_TYPE_DMABUF , __IO_REGBUF_TYPE_MAX, };
@@ -820,7 +821,10 @@ struct io_uring_regbuf_desc {__u32 flags;__u64 size;__u64 uaddr; -__u64 __resv[7 ]; + +__s32 dmabuf_fd; +__s32 target_fd; +__u64 __resv[6 ]; };/* Skip updating fd indexes set to this value in the fd table */
io_uring/Makefile +1 -0
@@ -26,3 +26,4 @@ obj-$(CONFIG_PROC_FS) += fdinfo.o obj-$(CONFIG_IO_URING_MOCK_FILE) += mock_file.o obj-$(CONFIG_IO_URING_BPF) += bpf_filter.o obj-$(CONFIG_IO_URING_BPF_OPS) += bpf-ops.o +obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o
io_uring/dma-buf.c new file +68 -0
68 lines not on this page · raw message
io_uring/dma-buf.h new file +67 -0
67 lines not on this page · raw message
io_uring/rsrc.c +55 -4
108 lines not on this page · raw message
io_uring/rsrc.h +5 -0
@@ -28,6 +28,11 @@ struct io_rsrc_node {enum {IO_REGBUF_F_KBUF =1 <<0 ,IO_REGBUF_F_UNCLONEABLE =1 <<1 , +IO_REGBUF_F_DMABUF =1 <<3 , +}; + +enum { +IO_REGBUF_IMPORT_ALLOW_DMABUF =1 <<1 , };struct io_mapped_ubuf {
io_uring/rw.c +24 -9
@@ -23,6 +23,7 @@#include "rsrc.h" #include "poll.h" #include "rw.h" +#include "dma-buf.h" static void io_complete_rw (struct kiocb *kiocb,long res);static void io_complete_rw_iopoll (struct kiocb *kiocb,long res);
@@ -358,13 +359,18 @@ static int io_init_rw_fixed(struct io_kiocb *req, unsigned int issue_flags,struct io_async_rw *io = req->async_data;int ret; -if (io->bytes_done) -return 0 ; +if (!io->bytes_done) { + ret =__io_import_reg_buf (req, &io->iter, rw->addr, rw->len, ddir, + issue_flags,IO_REGBUF_IMPORT_ALLOW_DMABUF ); +if (ret) +return ret; +iov_iter_save_state (&io->iter, &io->iter_state); + } - ret =io_import_reg_buf (req, &io->iter, rw->addr, rw->len, ddir, - issue_flags); -iov_iter_save_state (&io->iter, &io->iter_state); -return ret; + ret =io_attach_dmabuf_map (req, &io->iter, issue_flags); +if (ret) +return ret; +return 0 ; }int io_prep_read_fixed (struct io_kiocb *req,const struct io_uring_sqe *sqe)
@@ -577,6 +583,8 @@ static void io_complete_rw(struct kiocb *kiocb, long res) struct io_rw *rw = container_of(kiocb, struct io_rw, kiocb);struct io_kiocb *req =cmd_to_io_kiocb (rw); +io_detach_dmabuf_map (req); +/* ring owner may block in freeze_super() before task_work runs */ if (kiocb->ki_flags &IOCB_WRITE )io_req_end_write (req);
@@ -698,7 +706,7 @@ static ssize_t loop_rw_iter(int ddir, struct io_rw *rw, struct iov_iter *iter) !(kiocb->ki_filp->f_flags & O_NONBLOCK))return -EAGAIN ;if ((req->flags &REQ_F_BUF_NODE ) && - (req->buf_node->buf->flags &IO_REGBUF_F_KBUF )) + (req->buf_node->buf->flags & (IO_REGBUF_F_KBUF |IO_REGBUF_F_DMABUF )))return -EFAULT ; ppos =io_kiocb_ppos (kiocb);
@@ -1226,7 +1234,10 @@ int io_read_fixed(struct io_kiocb *req, unsigned int issue_flags)if (unlikely (ret))return ret; -return io_read (req, issue_flags); + ret =io_read (req, issue_flags); +if (ret != -EIOCBQUEUED ) +io_detach_dmabuf_map (req); +return ret; }int io_write_fixed (struct io_kiocb *req,unsigned int issue_flags)
@@ -1237,7 +1248,11 @@ int io_write_fixed(struct io_kiocb *req, unsigned int issue_flags)if (unlikely (ret))return ret; -return io_write (req, issue_flags); + ret =io_write (req, issue_flags); +if (ret != -EIOCBQUEUED ) +io_detach_dmabuf_map (req); +return ret; + }void io_rw_fail (struct io_kiocb *req)
Signature · 1 line
2.54.0
Quoted 6 lines · Pavel Begunkov
The patch set allows to register a dmabuf to an
io_uringinstance for a specified file and use it withio_uringread / write requests. The infrastructure is not tied toio_uringand there could be more users in the future. A similar idea was attempted some years ago by Keith [1], from where I borrowed a number of changes. Later it was brough back up to life by Tushar and Vishal.
Apologies for a messed up submission, please ignore. I'm going to send v9.
Signature · 1 line
Pavel Begunkov