* [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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox