Re: [PATCH 5/6] ublk: add UBLK_F_IO_DESC_SIZE
Caleb Sander Mateos <[email protected]> Thu, 30 Jul 2026 08:52:32 -0700
| Newsgroups | org.kernel.vger.linux-block,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CADUfDZooDW2xd6ovtLZF8GGOZQCwkROjo30Vank5Oee6shKXHg@mail.gmail.com> |
On Thu, Jul 30, 2026 at 8:14 AM Ming Lei <[email protected]> wrote: > > On Tue, Jul 28, 2026 at 07:29:50PM -0600, Caleb Sander Mateos wrote: > > ublk passes the parameters of incoming I/O in memory shared between the > > kernel ublk driver and userspace ublk server in struct ublksrv_io_desc. > > The size of this struct is currently fixed to 24 bytes, which has been > > an obstacle to extending it with additional fields [1]. Additionally, > > with multiple ublk server threads handling I/Os from the same ublk queue > > (possible with UBLK_F_PER_IO_DAEMON or UBLK_F_BATCH_IO), false sharing > > results from adjacent io_descs sharing the same cache line. > > > > Add a ublk feature UBLK_F_IO_DESC_SIZE to allow a ublk server to > > override the size of each io_desc. The size must be at least 24 and a > > multiple of 8 to store a properly-aligned struct ublksrv_io_desc. The > > struct ublksrv_io_desc is located at the beginning of each io_desc and > > the remainder is padding. The mmap() performed for each queue must have > > a length of queue_depth * io_desc_size, rounded up to the page size. The > > mmap() offset must be q_id * UBLK_MAX_QUEUE_DEPTH * io_desc_size, also > > rounded up to the page size. > > > > [1]: https://lore.kernel.org/linux-block/aV8QfvaNO5P6vOs6@fedora/ > > > > Signed-off-by: Caleb Sander Mateos <[email protected]> > > --- > > drivers/block/ublk_drv.c | 35 ++++++++++++++++++++++++----------- > > include/uapi/linux/ublk_cmd.h | 5 ++++- > > 2 files changed, 28 insertions(+), 12 deletions(-) > > > > diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c > > index 46f5ab13f87e..1da45e382253 100644 > > --- a/drivers/block/ublk_drv.c > > +++ b/drivers/block/ublk_drv.c > > @@ -87,11 +87,12 @@ > > | UBLK_F_BUF_REG_OFF_DAEMON \ > > | (IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) ? UBLK_F_INTEGRITY : 0) \ > > | UBLK_F_SAFE_STOP_DEV \ > > | UBLK_F_BATCH_IO \ > > | UBLK_F_NO_AUTO_PART_SCAN \ > > - | UBLK_F_SHMEM_ZC) > > + | UBLK_F_SHMEM_ZC \ > > + | UBLK_F_IO_DESC_SIZE) > > > > #define UBLK_F_ALL_RECOVERY_FLAGS (UBLK_F_USER_RECOVERY \ > > | UBLK_F_USER_RECOVERY_REISSUE \ > > | UBLK_F_USER_RECOVERY_FAIL_IO) > > > > @@ -237,10 +238,11 @@ struct ublk_io { > > } ____cacheline_aligned_in_smp; > > > > struct ublk_queue { > > u16 q_id; > > u16 q_depth; > > + u16 io_desc_size; > > > > unsigned long flags; > > struct ublksrv_io_desc *io_cmd_buf; > > > > bool force_abort; > > @@ -403,11 +405,11 @@ static inline void ublk_io_evts_deinit(struct ublk_queue *q) > > } > > > > static inline struct ublksrv_io_desc * > > ublk_get_iod(const struct ublk_queue *ubq, u16 tag) > > { > > - return &ubq->io_cmd_buf[tag]; > > + return (void *)ubq->io_cmd_buf + tag * (size_t)ubq->io_desc_size; > > } > > > > static inline bool ublk_support_zero_copy(const struct ublk_queue *ubq) > > { > > return ubq->flags & UBLK_F_SUPPORT_ZERO_COPY; > > @@ -1246,23 +1248,24 @@ static inline struct ublksrv_io_desc * > > ublk_queue_cmd_buf(struct ublk_device *ub, u16 q_id) > > { > > return ublk_get_queue(ub, q_id)->io_cmd_buf; > > } > > > > -static inline int __ublk_queue_cmd_buf_size(u16 depth) > > +static inline size_t __ublk_queue_cmd_buf_size(const struct ublk_device *ub, > > + u16 depth) > > { > > - return round_up(depth * sizeof(struct ublksrv_io_desc), PAGE_SIZE); > > + return round_up(depth * (size_t)ub->dev_info.io_desc_size, PAGE_SIZE); > > } > > > > -static inline int ublk_queue_cmd_buf_size(struct ublk_device *ub) > > +static inline size_t ublk_queue_cmd_buf_size(const struct ublk_device *ub) > > { > > - return __ublk_queue_cmd_buf_size(ub->dev_info.queue_depth); > > + return __ublk_queue_cmd_buf_size(ub, ub->dev_info.queue_depth); > > } > > > > -static int ublk_max_cmd_buf_size(void) > > +static size_t ublk_max_cmd_buf_size(const struct ublk_device *ub) > > { > > - return __ublk_queue_cmd_buf_size(UBLK_MAX_QUEUE_DEPTH); > > + return __ublk_queue_cmd_buf_size(ub, UBLK_MAX_QUEUE_DEPTH); > > } > > > > /* > > * Should I/O outstanding to the ublk server when it exits be reissued? > > * If not, outstanding I/O will get errors. > > @@ -2648,11 +2651,11 @@ static int ublk_ch_release(struct inode *inode, struct file *filp) > > /* map pre-allocated per-queue cmd buffer to ublksrv daemon */ > > static int ublk_ch_mmap(struct file *filp, struct vm_area_struct *vma) > > { > > struct ublk_device *ub = filp->private_data; > > size_t sz = vma->vm_end - vma->vm_start; > > - unsigned max_sz = ublk_max_cmd_buf_size(); > > + size_t max_sz = ublk_max_cmd_buf_size(ub); > > unsigned long pfn, end, phys_off = vma->vm_pgoff << PAGE_SHIFT; > > int ret = 0; > > u16 q_id; > > > > spin_lock(&ub->lock); > > @@ -4174,11 +4177,11 @@ static const struct file_operations ublk_ch_batch_io_fops = { > > .mmap = ublk_ch_mmap, > > }; > > > > static void __ublk_deinit_queue(struct ublk_device *ub, struct ublk_queue *ubq) > > { > > - int size; > > + size_t size; > > u16 i; > > > > size = ublk_queue_cmd_buf_size(ub); > > > > for (i = 0; i < ubq->q_depth; i++) { > > @@ -4227,11 +4230,12 @@ static int ublk_init_queue(struct ublk_device *ub, u16 q_id) > > u16 depth = ub->dev_info.queue_depth; > > gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO; > > struct ublk_queue *ubq; > > struct page *page; > > int numa_node; > > - int size, ret; > > + size_t size; > > + int ret; > > u16 i; > > > > /* Determine NUMA node based on queue's CPU affinity */ > > numa_node = ublk_get_queue_numa_node(ub, q_id); > > > > @@ -4252,10 +4256,11 @@ static int ublk_init_queue(struct ublk_device *ub, u16 q_id) > > if (!page) { > > kvfree(ubq); > > return -ENOMEM; > > } > > ubq->io_cmd_buf = page_address(page); > > + ubq->io_desc_size = ub->dev_info.io_desc_size; > > > > for (i = 0; i < ubq->q_depth; i++) > > spin_lock_init(&ubq->ios[i].lock); > > > > if (ublk_dev_support_batch_io(ub)) { > > @@ -4736,10 +4741,18 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header) > > > > /* User copy is required to access integrity buffer */ > > if (info.flags & UBLK_F_INTEGRITY && !(info.flags & UBLK_F_USER_COPY)) > > return -EINVAL; > > > > + if (info.flags & UBLK_F_IO_DESC_SIZE) { > > + if (info.io_desc_size < sizeof(struct ublksrv_io_desc) || > > + info.io_desc_size % _Alignof(struct ublksrv_io_desc)) > > + return -EINVAL; > > + } else { > > + info.io_desc_size = sizeof(struct ublksrv_io_desc); > > + } > > + > > No upper bound on io_desc_size. What upper bound would you suggest? > > Also Missing selftest update — this causes generic_13 failure. > > New feature needs sanity test to show it working at least. Okay, will add a test. Thanks, Caleb