* [PATCH 1/2 blktests] src/miniublk: switch to ioctl-encoded ublk commands
2026-06-17 7:25 [PATCH 0/2 blktests] Update the miniublk to use ioctl opcodes Sebastian Chlad
@ 2026-06-17 7:25 ` Sebastian Chlad
2026-06-19 3:26 ` Shin'ichiro Kawasaki
2026-06-17 7:25 ` [PATCH 2/2 blktests] src/miniublk: fall back to legacy opcodes on older kernels Sebastian Chlad
1 sibling, 1 reply; 4+ messages in thread
From: Sebastian Chlad @ 2026-06-17 7:25 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>
---
src/miniublk.c | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/src/miniublk.c b/src/miniublk.c
index f98f850..5a35ca7 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;
- else if (io->flags & UBLKSRV_NEED_FETCH_RQ)
- cmd_op = UBLK_IO_FETCH_REQ;
+ cmd_op = UBLK_U_IO_COMMIT_AND_FETCH_REQ;
+ else
+ 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] 4+ messages in thread* [PATCH 2/2 blktests] src/miniublk: fall back to legacy opcodes on older kernels
2026-06-17 7:25 [PATCH 0/2 blktests] Update the miniublk to use ioctl opcodes Sebastian Chlad
2026-06-17 7:25 ` [PATCH 1/2 blktests] src/miniublk: switch to ioctl-encoded ublk commands Sebastian Chlad
@ 2026-06-17 7:25 ` Sebastian Chlad
1 sibling, 0 replies; 4+ messages in thread
From: Sebastian Chlad @ 2026-06-17 7:25 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 5a35ca7..494a4ae 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
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] 4+ messages in thread