diff options
| author | Niklas Cassel <cassel@kernel.org> | 2026-09-18 16:06:42 +0200 |
|---|---|---|
| committer | Jens Axboe <axboe@kernel.dk> | 2026-09-29 12:54:47 -0600 |
| commit | 684b413b5483f57c890c171b9400076a0143b918 (patch) | |
| tree | f167daddad3dce9af44773541373eafbccc9ac61 /io_uring/kbuf.h | |
| download | linux-stable-684b413b5483f57c890c171b9400076a0143b918.tar.gz linux-stable-684b413b5483f57c890c171b9400076a0143b918.zip | |
virtio_blk: set the zone write granularitygrafted
virtblk_read_zoned_limits() reads the write granularity that the device
reports in virtio_blk_zoned_characteristics and assigns it to the
physical block size and to io_min, but never to the limit that is named
after it. queue_limits.zone_write_granularity is left at zero, so
blk_validate_zoned_limits() raises it to the logical block size:
if (lim->zone_write_granularity < lim->logical_block_size)
lim->zone_write_granularity = lim->logical_block_size;
A device that reports a granularity coarser than its logical block size,
which is what the field exists to express, therefore has it silently
reduced. A 512e host managed disk passed through to a guest reports a
logical block size of 512 and a write granularity of 4096, and the guest
ends up with a zone write granularity of 512.
bio_split_alignment() returns lim->zone_write_granularity if it is non-zero
and bio_split_io_at() may split a bio with as per bio_split_alignment().
This can real to the write getting rejected by the host drive, as the write
is not aligned to the physical block size.
zonefs also takes its block size from bdev_zone_write_granularity(), so it
would incorrectly use 512 on a disk that requires 4096.
sd_zbc_read_zones() sets the limit from the physical block size for the
same reason. NVMe ZNS and null_blk leave it unset, but the fallback
gives the right answer for them, as their write granularity is the
logical block size. virtio carries a separate value that may exceed it.
Set the zone write granularity from the value that the device reports.
Fixes: 95bfec41bd3d ("virtio-blk: add support for zoned block devices")
Signed-off-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Link: https://patch.msgid.link/20260918140641.2031075-2-cassel@kernel.org
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'io_uring/kbuf.h')
| -rw-r--r-- | io_uring/kbuf.h | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/io_uring/kbuf.h b/io_uring/kbuf.h new file mode 100644 index 000000000..401773e1e --- /dev/null +++ b/io_uring/kbuf.h @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef IOU_KBUF_H +#define IOU_KBUF_H + +#include <uapi/linux/io_uring.h> +#include <linux/io_uring_types.h> + +enum { + /* ring mapped provided buffers */ + IOBL_BUF_RING = 1, + /* buffers are consumed incrementally rather than always fully */ + IOBL_INC = 2, +}; + +struct io_buffer_list { + /* + * If the IOBL_BUF_RING flag is set, then buf_ring is used. If not, then + * these are classic provided buffers and ->buf_list is used. + */ + union { + struct list_head buf_list; + struct io_uring_buf_ring *buf_ring; + }; + /* count of classic/legacy buffers in buffer list */ + int nbufs; + + __u16 bgid; + + /* below is for ring provided buffers */ + __u16 head; + __u16 mask; + + __u16 flags; + + /* + * minimum required amount to be left to reuse an incrementally + * consumed buffer. If less than this is left at consumption time, + * buffer is done and head is incremented to the next buffer. + */ + __u32 min_left_sub_one; + + struct io_mapped_region region; +}; + +struct io_buffer { + struct list_head list; + __u64 addr; + __u32 len; + __u16 bid; + __u16 bgid; +}; + +enum { + /* can alloc a bigger vec */ + KBUF_MODE_EXPAND = 1, + /* if bigger vec allocated, free old one */ + KBUF_MODE_FREE = 2, +}; + +struct buf_sel_arg { + struct iovec *iovs; + size_t out_len; + size_t max_len; + unsigned short nr_iovs; + unsigned short mode; + unsigned short buf_group; + unsigned short partial_map; +}; + +struct io_br_sel io_buffer_select(struct io_kiocb *req, size_t *len, + unsigned buf_group, unsigned int issue_flags); +int io_buffers_select(struct io_kiocb *req, struct buf_sel_arg *arg, + struct io_br_sel *sel, unsigned int issue_flags); +int io_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg, + struct io_br_sel *sel); +void io_destroy_buffers(struct io_ring_ctx *ctx); + +int io_remove_buffers_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe); +int io_provide_buffers_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe); +int io_manage_buffers_legacy(struct io_kiocb *req, unsigned int issue_flags); + +int io_register_pbuf_ring(struct io_ring_ctx *ctx, void __user *arg); +int io_unregister_pbuf_ring(struct io_ring_ctx *ctx, void __user *arg); +int io_register_pbuf_status(struct io_ring_ctx *ctx, void __user *arg); + +bool io_kbuf_recycle_legacy(struct io_kiocb *req, unsigned issue_flags); +void io_kbuf_drop_legacy(struct io_kiocb *req); + +unsigned int __io_put_kbufs(struct io_kiocb *req, struct io_buffer_list *bl, + int len, int nbufs); +bool io_kbuf_commit(struct io_kiocb *req, + struct io_buffer_list *bl, int len, int nr); + +struct io_mapped_region *io_pbuf_get_region(struct io_ring_ctx *ctx, + unsigned int bgid); + +static inline bool io_kbuf_recycle_ring(struct io_kiocb *req, + struct io_buffer_list *bl) +{ + if (bl) { + req->flags &= ~(REQ_F_BUFFER_RING|REQ_F_BUFFERS_COMMIT); + return true; + } + return false; +} + +static inline bool io_do_buffer_select(struct io_kiocb *req) +{ + if (!(req->flags & REQ_F_BUFFER_SELECT)) + return false; + return !(req->flags & (REQ_F_BUFFER_SELECTED|REQ_F_BUFFER_RING)); +} + +static inline bool io_kbuf_recycle(struct io_kiocb *req, struct io_buffer_list *bl, + unsigned issue_flags) +{ + if (req->flags & REQ_F_BL_NO_RECYCLE) + return false; + if (req->flags & REQ_F_BUFFER_RING) + return io_kbuf_recycle_ring(req, bl); + if (req->flags & REQ_F_BUFFER_SELECTED) + return io_kbuf_recycle_legacy(req, issue_flags); + return false; +} + +static inline unsigned int io_put_kbuf(struct io_kiocb *req, int len, + struct io_buffer_list *bl) +{ + if (!(req->flags & (REQ_F_BUFFER_RING | REQ_F_BUFFER_SELECTED))) + return 0; + return __io_put_kbufs(req, bl, len, 1); +} + +static inline unsigned int io_put_kbufs(struct io_kiocb *req, int len, + struct io_buffer_list *bl, int nbufs) +{ + if (!(req->flags & (REQ_F_BUFFER_RING | REQ_F_BUFFER_SELECTED))) + return 0; + return __io_put_kbufs(req, bl, len, nbufs); +} +#endif |
