1
0
mirror of https://github.com/torvalds/linux.git synced 2025-04-09 14:45:27 +00:00

io_uring: support vectored kernel fixed buffer

io_uring has supported fixed kernel buffer via io_buffer_register_bvec()
and io_buffer_unregister_bvec().

The vectored fixed buffer has been ready, so it is natural to support
fixed kernel buffer, one use case is ublk.

Signed-off-by: Ming Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20250325135155.935398-4-ming.lei@redhat.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Ming Lei 2025-03-25 21:51:52 +08:00 committed by Jens Axboe
parent 149974fdb8
commit 1045afae4b

@ -1362,6 +1362,82 @@ static int io_estimate_bvec_size(struct iovec *iov, unsigned nr_iovs,
return max_segs;
}
static int io_vec_fill_kern_bvec(int ddir, struct iov_iter *iter,
struct io_mapped_ubuf *imu,
struct iovec *iovec, unsigned nr_iovs,
struct iou_vec *vec)
{
const struct bio_vec *src_bvec = imu->bvec;
struct bio_vec *res_bvec = vec->bvec;
unsigned res_idx = 0;
size_t total_len = 0;
unsigned iov_idx;
for (iov_idx = 0; iov_idx < nr_iovs; iov_idx++) {
size_t offset = (size_t)(uintptr_t)iovec[iov_idx].iov_base;
size_t iov_len = iovec[iov_idx].iov_len;
struct bvec_iter bi = {
.bi_size = offset + iov_len,
};
struct bio_vec bv;
bvec_iter_advance(src_bvec, &bi, offset);
for_each_mp_bvec(bv, src_bvec, bi, bi)
res_bvec[res_idx++] = bv;
total_len += iov_len;
}
iov_iter_bvec(iter, ddir, res_bvec, res_idx, total_len);
return 0;
}
static int iov_kern_bvec_size(const struct iovec *iov,
const struct io_mapped_ubuf *imu,
unsigned int *nr_seg)
{
size_t offset = (size_t)(uintptr_t)iov->iov_base;
const struct bio_vec *bvec = imu->bvec;
int start = 0, i = 0;
size_t off = 0;
int ret;
ret = validate_fixed_range(offset, iov->iov_len, imu);
if (unlikely(ret))
return ret;
for (i = 0; off < offset + iov->iov_len && i < imu->nr_bvecs;
off += bvec[i].bv_len, i++) {
if (offset >= off && offset < off + bvec[i].bv_len)
start = i;
}
*nr_seg = i - start;
return 0;
}
static int io_kern_bvec_size(struct iovec *iov, unsigned nr_iovs,
struct io_mapped_ubuf *imu, unsigned *nr_segs)
{
unsigned max_segs = 0;
size_t total_len = 0;
unsigned i;
int ret;
*nr_segs = 0;
for (i = 0; i < nr_iovs; i++) {
if (unlikely(!iov[i].iov_len))
return -EFAULT;
if (unlikely(check_add_overflow(total_len, iov[i].iov_len,
&total_len)))
return -EOVERFLOW;
ret = iov_kern_bvec_size(&iov[i], imu, &max_segs);
if (unlikely(ret))
return ret;
*nr_segs += max_segs;
}
if (total_len > MAX_RW_COUNT)
return -EINVAL;
return 0;
}
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)
@ -1376,14 +1452,20 @@ int io_import_reg_vec(int ddir, struct iov_iter *iter,
if (!node)
return -EFAULT;
imu = node->buf;
if (imu->is_kbuf)
return -EOPNOTSUPP;
if (!(imu->dir & (1 << ddir)))
return -EFAULT;
iovec_off = vec->nr - nr_iovs;
iov = vec->iovec + iovec_off;
nr_segs = io_estimate_bvec_size(iov, nr_iovs, imu);
if (imu->is_kbuf) {
int ret = io_kern_bvec_size(iov, nr_iovs, imu, &nr_segs);
if (unlikely(ret))
return ret;
} else {
nr_segs = io_estimate_bvec_size(iov, nr_iovs, imu);
}
if (sizeof(struct bio_vec) > sizeof(struct iovec)) {
size_t bvec_bytes;
@ -1410,6 +1492,9 @@ int io_import_reg_vec(int ddir, struct iov_iter *iter,
req->flags |= REQ_F_NEED_CLEANUP;
}
if (imu->is_kbuf)
return io_vec_fill_kern_bvec(ddir, iter, imu, iov, nr_iovs, vec);
return io_vec_fill_bvec(ddir, iter, imu, iov, nr_iovs, vec);
}