* [PATCH blktests v2 1/2] src/miniublk: switch to ioctl-encoded ublk commands
@ 2026-06-23 3:27 Sebastian Chlad
2026-06-23 3:27 ` [PATCH blktests v2 2/2] src/miniublk: fall back to legacy opcodes on older kernels Sebastian Chlad
2026-06-28 11:44 ` [PATCH blktests v2 1/2] src/miniublk: switch to ioctl-encoded ublk commands Shin'ichiro Kawasaki
0 siblings, 2 replies; 3+ messages in thread
From: Sebastian Chlad @ 2026-06-23 3:27 UTC (permalink / raw)
To: linux-block; +Cc: shinichiro.kawasaki, Sebastian Chlad
Kernels built without CONFIG_BLKDEV_UBLK_LEGACY_OPCODES reject the
legacy raw UBLK_CMD_* and UBLK_IO_* opcodes. Switch miniublk to use
the ioctl-encoded UBLK_U_CMD_* and UBLK_U_IO_* variants defined in
linux/ublk_cmd.h instead.
For IO commands, the ioctl-encoded opcode is used for submission while
_IOC_NR() extracts the raw NR bits for build_user_data(), keeping the
user_data tag encoding intact.
Signed-off-by: Sebastian Chlad <sebastian.chlad@suse.com>
Co-authored-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
---
changes in v2:
1. Makefile as prepared by Shin'ichiro Kawasaki
2. restored else if to check if (io->flags & UBLKSRV_NEED_FETCH_RQ)
src/Makefile | 9 +++++++++
src/miniublk.c | 28 ++++++++++++++--------------
2 files changed, 23 insertions(+), 14 deletions(-)
diff --git a/src/Makefile b/src/Makefile
index d8833bf..adfe3ef 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -8,6 +8,10 @@ HAVE_C_MACRO = $(shell if echo "$(H)include <$(1)>" | \
$(CC) $(CFLAGS) -E - 2>&1 /dev/null | grep $(2) > /dev/null 2>&1; \
then echo 1;else echo 0; fi)
+HAVE_C_DEF = $(shell if echo -e "$(H)include <$(1)>\n#ifdef $(2)\nHAVE_$(2)\n#endif" | \
+ $(CC) $(CFLAGS) -E - 2>&1 /dev/null | grep HAVE_$(2) > /dev/null 2>&1; \
+ then echo 1;else echo 0; fi)
+
C_TARGETS := \
dio-offsets \
loblksize \
@@ -27,6 +31,7 @@ C_UBLK_TARGETS := miniublk
HAVE_LIBURING := $(call HAVE_C_MACRO,liburing.h,IORING_OP_URING_CMD)
HAVE_UBLK_HEADER := $(call HAVE_C_HEADER,linux/ublk_cmd.h,1)
+HAVE_NEW_UBLK_INTF := $(call HAVE_C_DEF,linux/ublk_cmd.h,UBLK_U_CMD_START_DEV)
CXX_TARGETS := \
discontiguous-io
@@ -37,8 +42,12 @@ SYZKALLER_TARGETS := \
TARGETS := $(C_TARGETS) $(CXX_TARGETS) $(SYZKALLER_TARGETS)
ifeq ($(HAVE_UBLK_HEADER), 1)
+ifeq ($(HAVE_NEW_UBLK_INTF), 1)
C_URING_TARGETS += $(C_UBLK_TARGETS)
else
+$(info Skip $(C_UBLK_TARGETS) build due to missing new ublk interface(v6.4+))
+endif
+else
$(info Skip $(C_UBLK_TARGETS) build due to missing kernel header(v6.0+))
endif
diff --git a/src/miniublk.c b/src/miniublk.c
index f98f850..628207a 100644
--- a/src/miniublk.c
+++ b/src/miniublk.c
@@ -294,7 +294,7 @@ static int __ublk_ctrl_cmd(struct ublk_dev *dev,
int ublk_ctrl_stop_dev(struct ublk_dev *dev)
{
struct ublk_ctrl_cmd_data data = {
- .cmd_op = UBLK_CMD_STOP_DEV,
+ .cmd_op = UBLK_U_CMD_STOP_DEV,
};
return __ublk_ctrl_cmd(dev, &data);
@@ -304,7 +304,7 @@ int ublk_ctrl_start_dev(struct ublk_dev *dev,
int daemon_pid)
{
struct ublk_ctrl_cmd_data data = {
- .cmd_op = UBLK_CMD_START_DEV,
+ .cmd_op = UBLK_U_CMD_START_DEV,
.flags = CTRL_CMD_HAS_DATA,
};
@@ -316,7 +316,7 @@ int ublk_ctrl_start_dev(struct ublk_dev *dev,
int ublk_ctrl_add_dev(struct ublk_dev *dev)
{
struct ublk_ctrl_cmd_data data = {
- .cmd_op = UBLK_CMD_ADD_DEV,
+ .cmd_op = UBLK_U_CMD_ADD_DEV,
.flags = CTRL_CMD_HAS_BUF,
.addr = (__u64)&dev->dev_info,
.len = sizeof(struct ublksrv_ctrl_dev_info),
@@ -328,7 +328,7 @@ int ublk_ctrl_add_dev(struct ublk_dev *dev)
int ublk_ctrl_del_dev(struct ublk_dev *dev)
{
struct ublk_ctrl_cmd_data data = {
- .cmd_op = UBLK_CMD_DEL_DEV,
+ .cmd_op = UBLK_U_CMD_DEL_DEV,
.flags = 0,
};
@@ -338,7 +338,7 @@ int ublk_ctrl_del_dev(struct ublk_dev *dev)
int ublk_ctrl_get_info(struct ublk_dev *dev)
{
struct ublk_ctrl_cmd_data data = {
- .cmd_op = UBLK_CMD_GET_DEV_INFO,
+ .cmd_op = UBLK_U_CMD_GET_DEV_INFO,
.flags = CTRL_CMD_HAS_BUF,
.addr = (__u64)&dev->dev_info,
.len = sizeof(struct ublksrv_ctrl_dev_info),
@@ -351,7 +351,7 @@ int ublk_ctrl_set_params(struct ublk_dev *dev,
struct ublk_params *params)
{
struct ublk_ctrl_cmd_data data = {
- .cmd_op = UBLK_CMD_SET_PARAMS,
+ .cmd_op = UBLK_U_CMD_SET_PARAMS,
.flags = CTRL_CMD_HAS_BUF,
.addr = (__u64)params,
.len = sizeof(*params),
@@ -364,7 +364,7 @@ static int ublk_ctrl_get_params(struct ublk_dev *dev,
struct ublk_params *params)
{
struct ublk_ctrl_cmd_data data = {
- .cmd_op = UBLK_CMD_GET_PARAMS,
+ .cmd_op = UBLK_U_CMD_GET_PARAMS,
.flags = CTRL_CMD_HAS_BUF,
.addr = (__u64)params,
.len = sizeof(*params),
@@ -378,7 +378,7 @@ static int ublk_ctrl_get_params(struct ublk_dev *dev,
static int ublk_ctrl_start_user_recover(struct ublk_dev *dev)
{
struct ublk_ctrl_cmd_data data = {
- .cmd_op = UBLK_CMD_START_USER_RECOVERY,
+ .cmd_op = UBLK_U_CMD_START_USER_RECOVERY,
.flags = 0,
};
@@ -389,7 +389,7 @@ static int ublk_ctrl_end_user_recover(struct ublk_dev *dev,
int daemon_pid)
{
struct ublk_ctrl_cmd_data data = {
- .cmd_op = UBLK_CMD_END_USER_RECOVERY,
+ .cmd_op = UBLK_U_CMD_END_USER_RECOVERY,
.flags = CTRL_CMD_HAS_DATA,
};
@@ -624,9 +624,9 @@ static int ublk_queue_io_cmd(struct ublk_queue *q,
return 0;
if (io->flags & UBLKSRV_NEED_COMMIT_RQ_COMP)
- cmd_op = UBLK_IO_COMMIT_AND_FETCH_REQ;
+ cmd_op = UBLK_U_IO_COMMIT_AND_FETCH_REQ;
else if (io->flags & UBLKSRV_NEED_FETCH_RQ)
- cmd_op = UBLK_IO_FETCH_REQ;
+ cmd_op = UBLK_U_IO_FETCH_REQ;
sqe = io_uring_get_sqe(&q->ring);
if (!sqe) {
@@ -637,7 +637,7 @@ static int ublk_queue_io_cmd(struct ublk_queue *q,
cmd = (struct ublksrv_io_cmd *)ublk_get_sqe_cmd(sqe);
- if (cmd_op == UBLK_IO_COMMIT_AND_FETCH_REQ)
+ if (io->flags & UBLKSRV_NEED_COMMIT_RQ_COMP)
cmd->result = io->result;
/* These fields should be written once, never change */
@@ -650,7 +650,7 @@ static int ublk_queue_io_cmd(struct ublk_queue *q,
cmd->addr = (__u64)io->buf_addr;
cmd->q_id = q->q_id;
- user_data = build_user_data(tag, cmd_op, 0, 0);
+ user_data = build_user_data(tag, _IOC_NR(cmd_op), 0, 0);
io_uring_sqe_set_data64(sqe, user_data);
io->flags = 0;
@@ -658,7 +658,7 @@ static int ublk_queue_io_cmd(struct ublk_queue *q,
q->cmd_inflight += 1;
ublk_dbg(UBLK_DBG_IO_CMD, "%s: (qid %d tag %u cmd_op %u) iof %x stopping %d\n",
- __func__, q->q_id, tag, cmd_op,
+ __func__, q->q_id, tag, _IOC_NR(cmd_op),
io->flags, !!(q->state & UBLKSRV_QUEUE_STOPPING));
return 1;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH blktests v2 2/2] src/miniublk: fall back to legacy opcodes on older kernels
2026-06-23 3:27 [PATCH blktests v2 1/2] src/miniublk: switch to ioctl-encoded ublk commands Sebastian Chlad
@ 2026-06-23 3:27 ` Sebastian Chlad
2026-06-28 11:44 ` [PATCH blktests v2 1/2] src/miniublk: switch to ioctl-encoded ublk commands Shin'ichiro Kawasaki
1 sibling, 0 replies; 3+ messages in thread
From: Sebastian Chlad @ 2026-06-23 3:27 UTC (permalink / raw)
To: linux-block; +Cc: shinichiro.kawasaki, Sebastian Chlad
Try ioctl-encoded ADD_DEV and GET_DEV_INFO first; if either fails,
retry with the legacy raw opcode. After a successful bootstrap
command, derive use_ioctl from UBLK_F_CMD_IOCTL_ENCODE in dev_info.flags
so all subsequent control and IO commands use the mode reported by the
kernel.
Signed-off-by: Sebastian Chlad <sebastian.chlad@suse.com>
---
src/miniublk.c | 47 ++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 42 insertions(+), 5 deletions(-)
diff --git a/src/miniublk.c b/src/miniublk.c
index 628207a..b0c308b 100644
--- a/src/miniublk.c
+++ b/src/miniublk.c
@@ -112,6 +112,7 @@ struct ublk_dev {
int fds[2]; /* fds[0] points to /dev/ublkcN */
int nr_fds;
int ctrl_fd;
+ bool use_ioctl;
struct io_uring ring;
};
@@ -235,7 +236,7 @@ static inline int ublk_setup_ring(struct io_uring *r, int depth,
static inline void ublk_ctrl_init_cmd(struct ublk_dev *dev,
struct io_uring_sqe *sqe,
- struct ublk_ctrl_cmd_data *data)
+ struct ublk_ctrl_cmd_data *data, __u32 cmd_op)
{
struct ublksrv_ctrl_dev_info *info = &dev->dev_info;
struct ublksrv_ctrl_cmd *cmd = (struct ublksrv_ctrl_cmd *)ublk_get_sqe_cmd(sqe);
@@ -255,25 +256,34 @@ static inline void ublk_ctrl_init_cmd(struct ublk_dev *dev,
cmd->dev_id = info->dev_id;
cmd->queue_id = -1;
- ublk_set_sqe_cmd_op(sqe, data->cmd_op);
+ ublk_set_sqe_cmd_op(sqe, cmd_op);
io_uring_sqe_set_data(sqe, cmd);
}
+static void ublk_update_ioctl_encoding(struct ublk_dev *dev)
+{
+ dev->use_ioctl = !!(dev->dev_info.flags & UBLK_F_CMD_IOCTL_ENCODE);
+}
+
static int __ublk_ctrl_cmd(struct ublk_dev *dev,
struct ublk_ctrl_cmd_data *data)
{
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
+ __u32 cmd_op = data->cmd_op;
int ret = -EINVAL;
+ if (!dev->use_ioctl)
+ cmd_op = _IOC_NR(cmd_op);
+
sqe = io_uring_get_sqe(&dev->ring);
if (!sqe) {
ublk_err("%s: can't get sqe ret %d\n", __func__, ret);
return ret;
}
- ublk_ctrl_init_cmd(dev, sqe, data);
+ ublk_ctrl_init_cmd(dev, sqe, data, cmd_op);
ret = io_uring_submit(&dev->ring);
if (ret < 0) {
@@ -321,8 +331,19 @@ int ublk_ctrl_add_dev(struct ublk_dev *dev)
.addr = (__u64)&dev->dev_info,
.len = sizeof(struct ublksrv_ctrl_dev_info),
};
+ int ret;
- return __ublk_ctrl_cmd(dev, &data);
+ ret = __ublk_ctrl_cmd(dev, &data);
+ if (ret < 0) {
+ /* retry with legacy opcode on older kernels */
+ dev->use_ioctl = false;
+ ret = __ublk_ctrl_cmd(dev, &data);
+ }
+
+ if (ret >= 0)
+ ublk_update_ioctl_encoding(dev);
+
+ return ret;
}
int ublk_ctrl_del_dev(struct ublk_dev *dev)
@@ -343,8 +364,19 @@ int ublk_ctrl_get_info(struct ublk_dev *dev)
.addr = (__u64)&dev->dev_info,
.len = sizeof(struct ublksrv_ctrl_dev_info),
};
+ int ret;
- return __ublk_ctrl_cmd(dev, &data);
+ ret = __ublk_ctrl_cmd(dev, &data);
+ if (ret < 0 && dev->use_ioctl) {
+ /* retry with legacy opcode on older kernels */
+ dev->use_ioctl = false;
+ ret = __ublk_ctrl_cmd(dev, &data);
+ }
+
+ if (ret >= 0)
+ ublk_update_ioctl_encoding(dev);
+
+ return ret;
}
int ublk_ctrl_set_params(struct ublk_dev *dev,
@@ -453,6 +485,8 @@ static struct ublk_dev *ublk_ctrl_init()
struct ublksrv_ctrl_dev_info *info = &dev->dev_info;
int ret;
+ dev->use_ioctl = true; /* use ioctl opcodes by default */
+
dev->ctrl_fd = open(CTRL_DEV, O_RDWR);
if (dev->ctrl_fd < 0) {
ublk_err("control dev %s can't be opened: %m %d\n", CTRL_DEV, errno);
@@ -628,6 +662,9 @@ static int ublk_queue_io_cmd(struct ublk_queue *q,
else if (io->flags & UBLKSRV_NEED_FETCH_RQ)
cmd_op = UBLK_U_IO_FETCH_REQ;
+ if (!q->dev->use_ioctl)
+ cmd_op = _IOC_NR(cmd_op);
+
sqe = io_uring_get_sqe(&q->ring);
if (!sqe) {
ublk_err("%s: run out of sqe %d, tag %d\n",
--
2.51.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH blktests v2 1/2] src/miniublk: switch to ioctl-encoded ublk commands
2026-06-23 3:27 [PATCH blktests v2 1/2] src/miniublk: switch to ioctl-encoded ublk commands Sebastian Chlad
2026-06-23 3:27 ` [PATCH blktests v2 2/2] src/miniublk: fall back to legacy opcodes on older kernels Sebastian Chlad
@ 2026-06-28 11:44 ` Shin'ichiro Kawasaki
1 sibling, 0 replies; 3+ messages in thread
From: Shin'ichiro Kawasaki @ 2026-06-28 11:44 UTC (permalink / raw)
To: Sebastian Chlad; +Cc: linux-block, Sebastian Chlad
On Jun 23, 2026 / 05:27, Sebastian Chlad wrote:
> Kernels built without CONFIG_BLKDEV_UBLK_LEGACY_OPCODES reject the
> legacy raw UBLK_CMD_* and UBLK_IO_* opcodes. Switch miniublk to use
> the ioctl-encoded UBLK_U_CMD_* and UBLK_U_IO_* variants defined in
> linux/ublk_cmd.h instead.
>
> For IO commands, the ioctl-encoded opcode is used for submission while
> _IOC_NR() extracts the raw NR bits for build_user_data(), keeping the
> user_data tag encoding intact.
Thanks for this v2 series. I applied the patches.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-06-28 11:45 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-23 3:27 [PATCH blktests v2 1/2] src/miniublk: switch to ioctl-encoded ublk commands Sebastian Chlad
2026-06-23 3:27 ` [PATCH blktests v2 2/2] src/miniublk: fall back to legacy opcodes on older kernels Sebastian Chlad
2026-06-28 11:44 ` [PATCH blktests v2 1/2] src/miniublk: switch to ioctl-encoded ublk commands Shin'ichiro Kawasaki
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.