* [PATCH v7 0/3] ublk: introduce UBLK_CMD_TRY_STOP_DEV
@ 2026-01-12 22:04 Yoav Cohen
2026-01-12 22:05 ` [PATCH v7 1/3] ublk: make ublk_ctrl_stop_dev return void Yoav Cohen
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Yoav Cohen @ 2026-01-12 22:04 UTC (permalink / raw)
To: Ming Lei, Jens Axboe, linux-block, csander, alex
Cc: jholzman, omril, Yoav Cohen, Yoav Cohen
Changes since v2:
- Address Ming Lei’s comments in patch 2:
- Add a feature flag and some minor comments.
- Patch 1 unchanged, keeps Reviewed-by
Changes since v3:
- Address Caleb's comments in patch 2
- add to kublk.c
- add to auto features
- set feature flag to 17
Changes since v4:
- Selftest added by Ming
- Move kublk.c change to new selftest commit
Changes since v5:
- Return -ENXIO when device is going down instead of -EBUSY
- Update the patch 2 commit msg
Changes since v6:
- Rebased on top of for-7.0
Ming Lei (1):
selftests: ublk: add stop command with --safe option
Yoav Cohen (2):
ublk: make ublk_ctrl_stop_dev return void
ublk: add UBLK_CMD_TRY_STOP_DEV command
drivers/block/ublk_drv.c | 50 ++++++++++++++--
include/uapi/linux/ublk_cmd.h | 9 ++-
tools/testing/selftests/ublk/Makefile | 1 +
tools/testing/selftests/ublk/kublk.c | 53 +++++++++++++++++
tools/testing/selftests/ublk/kublk.h | 1 +
.../testing/selftests/ublk/test_generic_16.sh | 57 +++++++++++++++++++
6 files changed, 165 insertions(+), 6 deletions(-)
create mode 100755 tools/testing/selftests/ublk/test_generic_16.sh
--
2.39.5 (Apple Git-154)
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v7 1/3] ublk: make ublk_ctrl_stop_dev return void
2026-01-12 22:04 [PATCH v7 0/3] ublk: introduce UBLK_CMD_TRY_STOP_DEV Yoav Cohen
@ 2026-01-12 22:05 ` Yoav Cohen
2026-01-12 22:05 ` [PATCH v7 2/3] ublk: add UBLK_CMD_TRY_STOP_DEV command Yoav Cohen
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Yoav Cohen @ 2026-01-12 22:05 UTC (permalink / raw)
To: Ming Lei, Jens Axboe, linux-block, csander, alex
Cc: jholzman, omril, Yoav Cohen, Yoav Cohen
This function always returns 0, so there is no need to return a value.
Signed-off-by: Yoav Cohen <yoav@nvidia.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
---
drivers/block/ublk_drv.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index ec96d5afad7a..73490890242b 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -3459,10 +3459,9 @@ static inline void ublk_ctrl_cmd_dump(struct io_uring_cmd *cmd)
header->data[0], header->addr, header->len);
}
-static int ublk_ctrl_stop_dev(struct ublk_device *ub)
+static void ublk_ctrl_stop_dev(struct ublk_device *ub)
{
ublk_stop_dev(ub);
- return 0;
}
static int ublk_ctrl_get_dev_info(struct ublk_device *ub,
@@ -3935,7 +3934,8 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
ret = ublk_ctrl_start_dev(ub, header);
break;
case UBLK_CMD_STOP_DEV:
- ret = ublk_ctrl_stop_dev(ub);
+ ublk_ctrl_stop_dev(ub);
+ ret = 0;
break;
case UBLK_CMD_GET_DEV_INFO:
case UBLK_CMD_GET_DEV_INFO2:
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v7 2/3] ublk: add UBLK_CMD_TRY_STOP_DEV command
2026-01-12 22:04 [PATCH v7 0/3] ublk: introduce UBLK_CMD_TRY_STOP_DEV Yoav Cohen
2026-01-12 22:05 ` [PATCH v7 1/3] ublk: make ublk_ctrl_stop_dev return void Yoav Cohen
@ 2026-01-12 22:05 ` Yoav Cohen
2026-01-12 22:05 ` [PATCH v7 3/3] selftests: ublk: add stop command with --safe option Yoav Cohen
2026-01-12 22:08 ` [PATCH v7 0/3] ublk: introduce UBLK_CMD_TRY_STOP_DEV Jens Axboe
3 siblings, 0 replies; 6+ messages in thread
From: Yoav Cohen @ 2026-01-12 22:05 UTC (permalink / raw)
To: Ming Lei, Jens Axboe, linux-block, csander, alex
Cc: jholzman, omril, Yoav Cohen, Yoav Cohen
Add a best-effort stop command, UBLK_CMD_TRY_STOP_DEV, which only stops a
ublk device when it has no active openers.
Unlike UBLK_CMD_STOP_DEV, this command does not disrupt existing users.
New opens are blocked only after disk_openers has reached zero; if the
device is busy, the command returns -EBUSY and leaves it running.
The ub->block_open flag is used only to close a race with an in-progress
open and does not otherwise change open behavior.
Advertise support via the UBLK_F_SAFE_STOP_DEV feature flag.
Signed-off-by: Yoav Cohen <yoav@nvidia.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
---
drivers/block/ublk_drv.c | 44 +++++++++++++++++++++++++++++++++--
include/uapi/linux/ublk_cmd.h | 9 ++++++-
2 files changed, 50 insertions(+), 3 deletions(-)
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 73490890242b..aaf94d2fb789 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -56,6 +56,7 @@
#define UBLK_CMD_DEL_DEV_ASYNC _IOC_NR(UBLK_U_CMD_DEL_DEV_ASYNC)
#define UBLK_CMD_UPDATE_SIZE _IOC_NR(UBLK_U_CMD_UPDATE_SIZE)
#define UBLK_CMD_QUIESCE_DEV _IOC_NR(UBLK_U_CMD_QUIESCE_DEV)
+#define UBLK_CMD_TRY_STOP_DEV _IOC_NR(UBLK_U_CMD_TRY_STOP_DEV)
#define UBLK_IO_REGISTER_IO_BUF _IOC_NR(UBLK_U_IO_REGISTER_IO_BUF)
#define UBLK_IO_UNREGISTER_IO_BUF _IOC_NR(UBLK_U_IO_UNREGISTER_IO_BUF)
@@ -76,7 +77,8 @@
| UBLK_F_QUIESCE \
| UBLK_F_PER_IO_DAEMON \
| UBLK_F_BUF_REG_OFF_DAEMON \
- | (IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) ? UBLK_F_INTEGRITY : 0))
+ | (IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) ? UBLK_F_INTEGRITY : 0) \
+ | UBLK_F_SAFE_STOP_DEV)
#define UBLK_F_ALL_RECOVERY_FLAGS (UBLK_F_USER_RECOVERY \
| UBLK_F_USER_RECOVERY_REISSUE \
@@ -243,6 +245,8 @@ struct ublk_device {
struct delayed_work exit_work;
struct work_struct partition_scan_work;
+ bool block_open; /* protected by open_mutex */
+
struct ublk_queue *queues[];
};
@@ -984,6 +988,9 @@ static int ublk_open(struct gendisk *disk, blk_mode_t mode)
return -EPERM;
}
+ if (ub->block_open)
+ return -ENXIO;
+
return 0;
}
@@ -3343,7 +3350,8 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
ub->dev_info.flags |= UBLK_F_CMD_IOCTL_ENCODE |
UBLK_F_URING_CMD_COMP_IN_TASK |
UBLK_F_PER_IO_DAEMON |
- UBLK_F_BUF_REG_OFF_DAEMON;
+ UBLK_F_BUF_REG_OFF_DAEMON |
+ UBLK_F_SAFE_STOP_DEV;
/* GET_DATA isn't needed any more with USER_COPY or ZERO COPY */
if (ub->dev_info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
@@ -3464,6 +3472,34 @@ static void ublk_ctrl_stop_dev(struct ublk_device *ub)
ublk_stop_dev(ub);
}
+static int ublk_ctrl_try_stop_dev(struct ublk_device *ub)
+{
+ struct gendisk *disk;
+ int ret = 0;
+
+ disk = ublk_get_disk(ub);
+ if (!disk)
+ return -ENODEV;
+
+ mutex_lock(&disk->open_mutex);
+ if (disk_openers(disk) > 0) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+ ub->block_open = true;
+ /* release open_mutex as del_gendisk() will reacquire it */
+ mutex_unlock(&disk->open_mutex);
+
+ ublk_ctrl_stop_dev(ub);
+ goto out;
+
+unlock:
+ mutex_unlock(&disk->open_mutex);
+out:
+ ublk_put_disk(disk);
+ return ret;
+}
+
static int ublk_ctrl_get_dev_info(struct ublk_device *ub,
const struct ublksrv_ctrl_cmd *header)
{
@@ -3859,6 +3895,7 @@ static int ublk_ctrl_uring_cmd_permission(struct ublk_device *ub,
case UBLK_CMD_END_USER_RECOVERY:
case UBLK_CMD_UPDATE_SIZE:
case UBLK_CMD_QUIESCE_DEV:
+ case UBLK_CMD_TRY_STOP_DEV:
mask = MAY_READ | MAY_WRITE;
break;
default:
@@ -3972,6 +4009,9 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
case UBLK_CMD_QUIESCE_DEV:
ret = ublk_ctrl_quiesce_dev(ub, header);
break;
+ case UBLK_CMD_TRY_STOP_DEV:
+ ret = ublk_ctrl_try_stop_dev(ub);
+ break;
default:
ret = -EOPNOTSUPP;
break;
diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
index 61ac5d8e1078..90f47da4f435 100644
--- a/include/uapi/linux/ublk_cmd.h
+++ b/include/uapi/linux/ublk_cmd.h
@@ -55,7 +55,8 @@
_IOWR('u', 0x15, struct ublksrv_ctrl_cmd)
#define UBLK_U_CMD_QUIESCE_DEV \
_IOWR('u', 0x16, struct ublksrv_ctrl_cmd)
-
+#define UBLK_U_CMD_TRY_STOP_DEV \
+ _IOWR('u', 0x17, struct ublksrv_ctrl_cmd)
/*
* 64bits are enough now, and it should be easy to extend in case of
* running out of feature flags
@@ -321,6 +322,12 @@
*/
#define UBLK_F_INTEGRITY (1ULL << 16)
+/*
+ * The device supports the UBLK_CMD_TRY_STOP_DEV command, which
+ * allows stopping the device only if there are no openers.
+ */
+#define UBLK_F_SAFE_STOP_DEV (1ULL << 17)
+
/* device state */
#define UBLK_S_DEV_DEAD 0
#define UBLK_S_DEV_LIVE 1
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v7 3/3] selftests: ublk: add stop command with --safe option
2026-01-12 22:04 [PATCH v7 0/3] ublk: introduce UBLK_CMD_TRY_STOP_DEV Yoav Cohen
2026-01-12 22:05 ` [PATCH v7 1/3] ublk: make ublk_ctrl_stop_dev return void Yoav Cohen
2026-01-12 22:05 ` [PATCH v7 2/3] ublk: add UBLK_CMD_TRY_STOP_DEV command Yoav Cohen
@ 2026-01-12 22:05 ` Yoav Cohen
2026-01-12 22:15 ` Caleb Sander Mateos
2026-01-12 22:08 ` [PATCH v7 0/3] ublk: introduce UBLK_CMD_TRY_STOP_DEV Jens Axboe
3 siblings, 1 reply; 6+ messages in thread
From: Yoav Cohen @ 2026-01-12 22:05 UTC (permalink / raw)
To: Ming Lei, Jens Axboe, linux-block, csander, alex
Cc: jholzman, omril, Yoav Cohen
From: Ming Lei <ming.lei@redhat.com>
Add 'stop' subcommand to kublk utility that uses the new
UBLK_CMD_TRY_STOP_DEV command when --safe option is specified.
This allows stopping a device only if it has no active openers,
returning -EBUSY otherwise.
Also add test_generic_16.sh to test the new functionality.
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
tools/testing/selftests/ublk/Makefile | 1 +
tools/testing/selftests/ublk/kublk.c | 53 +++++++++++++++++
tools/testing/selftests/ublk/kublk.h | 1 +
.../testing/selftests/ublk/test_generic_16.sh | 57 +++++++++++++++++++
4 files changed, 112 insertions(+)
create mode 100755 tools/testing/selftests/ublk/test_generic_16.sh
diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
index 036a9f01b464..3a2498089b15 100644
--- a/tools/testing/selftests/ublk/Makefile
+++ b/tools/testing/selftests/ublk/Makefile
@@ -23,6 +23,7 @@ TEST_PROGS += test_generic_12.sh
TEST_PROGS += test_generic_13.sh
TEST_PROGS += test_generic_14.sh
TEST_PROGS += test_generic_15.sh
+TEST_PROGS += test_generic_16.sh
TEST_PROGS += test_null_01.sh
TEST_PROGS += test_null_02.sh
diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c
index d95937dd6167..3472ce7426ba 100644
--- a/tools/testing/selftests/ublk/kublk.c
+++ b/tools/testing/selftests/ublk/kublk.c
@@ -108,6 +108,15 @@ static int ublk_ctrl_stop_dev(struct ublk_dev *dev)
return __ublk_ctrl_cmd(dev, &data);
}
+static int ublk_ctrl_try_stop_dev(struct ublk_dev *dev)
+{
+ struct ublk_ctrl_cmd_data data = {
+ .cmd_op = UBLK_U_CMD_TRY_STOP_DEV,
+ };
+
+ return __ublk_ctrl_cmd(dev, &data);
+}
+
static int ublk_ctrl_start_dev(struct ublk_dev *dev,
int daemon_pid)
{
@@ -1424,6 +1433,42 @@ static int cmd_dev_del(struct dev_ctx *ctx)
return 0;
}
+static int cmd_dev_stop(struct dev_ctx *ctx)
+{
+ int number = ctx->dev_id;
+ struct ublk_dev *dev;
+ int ret;
+
+ if (number < 0) {
+ ublk_err("%s: device id is required\n", __func__);
+ return -EINVAL;
+ }
+
+ dev = ublk_ctrl_init();
+ dev->dev_info.dev_id = number;
+
+ ret = ublk_ctrl_get_info(dev);
+ if (ret < 0)
+ goto fail;
+
+ if (ctx->safe_stop) {
+ ret = ublk_ctrl_try_stop_dev(dev);
+ if (ret < 0)
+ ublk_err("%s: try_stop dev %d failed ret %d\n",
+ __func__, number, ret);
+ } else {
+ ret = ublk_ctrl_stop_dev(dev);
+ if (ret < 0)
+ ublk_err("%s: stop dev %d failed ret %d\n",
+ __func__, number, ret);
+ }
+
+fail:
+ ublk_ctrl_deinit(dev);
+
+ return ret;
+}
+
static int __cmd_dev_list(struct dev_ctx *ctx)
{
struct ublk_dev *dev = ublk_ctrl_init();
@@ -1487,6 +1532,7 @@ static int cmd_dev_get_features(void)
FEAT_NAME(UBLK_F_PER_IO_DAEMON),
FEAT_NAME(UBLK_F_BUF_REG_OFF_DAEMON),
FEAT_NAME(UBLK_F_INTEGRITY),
+ FEAT_NAME(UBLK_F_SAFE_STOP_DEV)
};
struct ublk_dev *dev;
__u64 features = 0;
@@ -1616,6 +1662,8 @@ static int cmd_dev_help(char *exe)
printf("%s del [-n dev_id] -a \n", exe);
printf("\t -a delete all devices -n delete specified device\n\n");
+ printf("%s stop -n dev_id [--safe]\n", exe);
+ printf("\t --safe only stop if device has no active openers\n\n");
printf("%s list [-n dev_id] -a \n", exe);
printf("\t -a list all devices, -n list specified device, default -a \n\n");
printf("%s features\n", exe);
@@ -1653,6 +1701,7 @@ int main(int argc, char *argv[])
{ "pi_offset", 1, NULL, 0 },
{ "csum_type", 1, NULL, 0 },
{ "tag_size", 1, NULL, 0 },
+ { "safe", 0, NULL, 0 },
{ 0, 0, 0, 0 }
};
const struct ublk_tgt_ops *ops = NULL;
@@ -1760,6 +1809,8 @@ int main(int argc, char *argv[])
}
if (!strcmp(longopts[option_idx].name, "tag_size"))
ctx.tag_size = strtoul(optarg, NULL, 0);
+ if (!strcmp(longopts[option_idx].name, "safe"))
+ ctx.safe_stop = 1;
break;
case '?':
/*
@@ -1842,6 +1893,8 @@ int main(int argc, char *argv[])
}
} else if (!strcmp(cmd, "del"))
ret = cmd_dev_del(&ctx);
+ else if (!strcmp(cmd, "stop"))
+ ret = cmd_dev_stop(&ctx);
else if (!strcmp(cmd, "list")) {
ctx.all = 1;
ret = cmd_dev_list(&ctx);
diff --git a/tools/testing/selftests/ublk/kublk.h b/tools/testing/selftests/ublk/kublk.h
index 96c66b337bc0..cb757fd9bf9d 100644
--- a/tools/testing/selftests/ublk/kublk.h
+++ b/tools/testing/selftests/ublk/kublk.h
@@ -83,6 +83,7 @@ struct dev_ctx {
__u8 pi_offset;
__u8 csum_type;
__u8 tag_size;
+ unsigned int safe_stop:1;
int _evtfd;
int _shmid;
diff --git a/tools/testing/selftests/ublk/test_generic_16.sh b/tools/testing/selftests/ublk/test_generic_16.sh
new file mode 100755
index 000000000000..e08af7b685c9
--- /dev/null
+++ b/tools/testing/selftests/ublk/test_generic_16.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
+
+TID="generic_16"
+ERR_CODE=0
+
+_prep_test "null" "stop --safe command"
+
+# Check if SAFE_STOP_DEV feature is supported
+if ! _have_feature "SAFE_STOP_DEV"; then
+ _cleanup_test "null"
+ exit "$UBLK_SKIP_CODE"
+fi
+
+# Test 1: stop --safe on idle device should succeed
+dev_id=$(_add_ublk_dev -t null -q 2 -d 32)
+_check_add_dev $TID $?
+
+# Device is idle (no openers), stop --safe should succeed
+if ! ${UBLK_PROG} stop -n "${dev_id}" --safe; then
+ echo "stop --safe on idle device failed unexpectedly!"
+ ERR_CODE=255
+fi
+
+# Clean up device
+${UBLK_PROG} del -n "${dev_id}" > /dev/null 2>&1
+udevadm settle
+
+# Test 2: stop --safe on device with active opener should fail
+dev_id=$(_add_ublk_dev -t null -q 2 -d 32)
+_check_add_dev $TID $?
+
+# Open device in background (dd reads indefinitely)
+dd if=/dev/ublkb${dev_id} of=/dev/null bs=4k iflag=direct > /dev/null 2>&1 &
+dd_pid=$!
+
+# Give dd time to start
+sleep 0.2
+
+# Device has active opener, stop --safe should fail with -EBUSY
+if ${UBLK_PROG} stop -n "${dev_id}" --safe 2>/dev/null; then
+ echo "stop --safe on busy device succeeded unexpectedly!"
+ ERR_CODE=255
+fi
+
+# Kill dd and clean up
+kill $dd_pid 2>/dev/null
+wait $dd_pid 2>/dev/null
+
+# Now device should be idle, regular delete should work
+${UBLK_PROG} del -n "${dev_id}"
+udevadm settle
+
+_cleanup_test "null"
+_show_result $TID $ERR_CODE
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v7 0/3] ublk: introduce UBLK_CMD_TRY_STOP_DEV
2026-01-12 22:04 [PATCH v7 0/3] ublk: introduce UBLK_CMD_TRY_STOP_DEV Yoav Cohen
` (2 preceding siblings ...)
2026-01-12 22:05 ` [PATCH v7 3/3] selftests: ublk: add stop command with --safe option Yoav Cohen
@ 2026-01-12 22:08 ` Jens Axboe
3 siblings, 0 replies; 6+ messages in thread
From: Jens Axboe @ 2026-01-12 22:08 UTC (permalink / raw)
To: Ming Lei, linux-block, csander, alex, Yoav Cohen
Cc: jholzman, omril, Yoav Cohen
On Tue, 13 Jan 2026 00:04:59 +0200, Yoav Cohen wrote:
> Changes since v2:
> - Address Ming Lei’s comments in patch 2:
> - Add a feature flag and some minor comments.
> - Patch 1 unchanged, keeps Reviewed-by
>
> Changes since v3:
> - Address Caleb's comments in patch 2
> - add to kublk.c
> - add to auto features
> - set feature flag to 17
>
> [...]
Applied, thanks!
[1/3] ublk: make ublk_ctrl_stop_dev return void
commit: 9e386f49fa269298490b303c423c6af4645f184e
[2/3] ublk: add UBLK_CMD_TRY_STOP_DEV command
commit: 93ada1b3da398b492c45429cef1a1c9651d5c7ba
[3/3] selftests: ublk: add stop command with --safe option
commit: 65955a0993a0a9536263fea2eaae8aed496dcc9c
Best regards,
--
Jens Axboe
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v7 3/3] selftests: ublk: add stop command with --safe option
2026-01-12 22:05 ` [PATCH v7 3/3] selftests: ublk: add stop command with --safe option Yoav Cohen
@ 2026-01-12 22:15 ` Caleb Sander Mateos
0 siblings, 0 replies; 6+ messages in thread
From: Caleb Sander Mateos @ 2026-01-12 22:15 UTC (permalink / raw)
To: Yoav Cohen
Cc: Ming Lei, Jens Axboe, linux-block, alex, jholzman, omril,
Yoav Cohen
On Mon, Jan 12, 2026 at 2:06 PM Yoav Cohen <yoav@nvidia.com> wrote:
>
> From: Ming Lei <ming.lei@redhat.com>
>
> Add 'stop' subcommand to kublk utility that uses the new
> UBLK_CMD_TRY_STOP_DEV command when --safe option is specified.
> This allows stopping a device only if it has no active openers,
> returning -EBUSY otherwise.
>
> Also add test_generic_16.sh to test the new functionality.
>
> Signed-off-by: Ming Lei <ming.lei@redhat.com>
> ---
> tools/testing/selftests/ublk/Makefile | 1 +
> tools/testing/selftests/ublk/kublk.c | 53 +++++++++++++++++
> tools/testing/selftests/ublk/kublk.h | 1 +
> .../testing/selftests/ublk/test_generic_16.sh | 57 +++++++++++++++++++
> 4 files changed, 112 insertions(+)
> create mode 100755 tools/testing/selftests/ublk/test_generic_16.sh
>
> diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
> index 036a9f01b464..3a2498089b15 100644
> --- a/tools/testing/selftests/ublk/Makefile
> +++ b/tools/testing/selftests/ublk/Makefile
> @@ -23,6 +23,7 @@ TEST_PROGS += test_generic_12.sh
> TEST_PROGS += test_generic_13.sh
> TEST_PROGS += test_generic_14.sh
> TEST_PROGS += test_generic_15.sh
> +TEST_PROGS += test_generic_16.sh
>
> TEST_PROGS += test_null_01.sh
> TEST_PROGS += test_null_02.sh
> diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c
> index d95937dd6167..3472ce7426ba 100644
> --- a/tools/testing/selftests/ublk/kublk.c
> +++ b/tools/testing/selftests/ublk/kublk.c
> @@ -108,6 +108,15 @@ static int ublk_ctrl_stop_dev(struct ublk_dev *dev)
> return __ublk_ctrl_cmd(dev, &data);
> }
>
> +static int ublk_ctrl_try_stop_dev(struct ublk_dev *dev)
> +{
> + struct ublk_ctrl_cmd_data data = {
> + .cmd_op = UBLK_U_CMD_TRY_STOP_DEV,
> + };
> +
> + return __ublk_ctrl_cmd(dev, &data);
> +}
> +
> static int ublk_ctrl_start_dev(struct ublk_dev *dev,
> int daemon_pid)
> {
> @@ -1424,6 +1433,42 @@ static int cmd_dev_del(struct dev_ctx *ctx)
> return 0;
> }
>
> +static int cmd_dev_stop(struct dev_ctx *ctx)
> +{
> + int number = ctx->dev_id;
> + struct ublk_dev *dev;
> + int ret;
> +
> + if (number < 0) {
> + ublk_err("%s: device id is required\n", __func__);
> + return -EINVAL;
> + }
> +
> + dev = ublk_ctrl_init();
> + dev->dev_info.dev_id = number;
> +
> + ret = ublk_ctrl_get_info(dev);
> + if (ret < 0)
> + goto fail;
> +
> + if (ctx->safe_stop) {
> + ret = ublk_ctrl_try_stop_dev(dev);
> + if (ret < 0)
> + ublk_err("%s: try_stop dev %d failed ret %d\n",
> + __func__, number, ret);
> + } else {
> + ret = ublk_ctrl_stop_dev(dev);
> + if (ret < 0)
> + ublk_err("%s: stop dev %d failed ret %d\n",
> + __func__, number, ret);
> + }
> +
> +fail:
> + ublk_ctrl_deinit(dev);
> +
> + return ret;
> +}
> +
> static int __cmd_dev_list(struct dev_ctx *ctx)
> {
> struct ublk_dev *dev = ublk_ctrl_init();
> @@ -1487,6 +1532,7 @@ static int cmd_dev_get_features(void)
> FEAT_NAME(UBLK_F_PER_IO_DAEMON),
> FEAT_NAME(UBLK_F_BUF_REG_OFF_DAEMON),
> FEAT_NAME(UBLK_F_INTEGRITY),
> + FEAT_NAME(UBLK_F_SAFE_STOP_DEV)
> };
> struct ublk_dev *dev;
> __u64 features = 0;
> @@ -1616,6 +1662,8 @@ static int cmd_dev_help(char *exe)
>
> printf("%s del [-n dev_id] -a \n", exe);
> printf("\t -a delete all devices -n delete specified device\n\n");
> + printf("%s stop -n dev_id [--safe]\n", exe);
> + printf("\t --safe only stop if device has no active openers\n\n");
> printf("%s list [-n dev_id] -a \n", exe);
> printf("\t -a list all devices, -n list specified device, default -a \n\n");
> printf("%s features\n", exe);
> @@ -1653,6 +1701,7 @@ int main(int argc, char *argv[])
> { "pi_offset", 1, NULL, 0 },
> { "csum_type", 1, NULL, 0 },
> { "tag_size", 1, NULL, 0 },
> + { "safe", 0, NULL, 0 },
> { 0, 0, 0, 0 }
> };
> const struct ublk_tgt_ops *ops = NULL;
> @@ -1760,6 +1809,8 @@ int main(int argc, char *argv[])
> }
> if (!strcmp(longopts[option_idx].name, "tag_size"))
> ctx.tag_size = strtoul(optarg, NULL, 0);
> + if (!strcmp(longopts[option_idx].name, "safe"))
> + ctx.safe_stop = 1;
> break;
> case '?':
> /*
> @@ -1842,6 +1893,8 @@ int main(int argc, char *argv[])
> }
> } else if (!strcmp(cmd, "del"))
> ret = cmd_dev_del(&ctx);
> + else if (!strcmp(cmd, "stop"))
> + ret = cmd_dev_stop(&ctx);
> else if (!strcmp(cmd, "list")) {
> ctx.all = 1;
> ret = cmd_dev_list(&ctx);
> diff --git a/tools/testing/selftests/ublk/kublk.h b/tools/testing/selftests/ublk/kublk.h
> index 96c66b337bc0..cb757fd9bf9d 100644
> --- a/tools/testing/selftests/ublk/kublk.h
> +++ b/tools/testing/selftests/ublk/kublk.h
> @@ -83,6 +83,7 @@ struct dev_ctx {
> __u8 pi_offset;
> __u8 csum_type;
> __u8 tag_size;
> + unsigned int safe_stop:1;
Looks like it would make more sense to put this next to the other
bitfields (logging to no_ublk_fixed_fd). Not a big deal, though.
Best,
Caleb
>
> int _evtfd;
> int _shmid;
> diff --git a/tools/testing/selftests/ublk/test_generic_16.sh b/tools/testing/selftests/ublk/test_generic_16.sh
> new file mode 100755
> index 000000000000..e08af7b685c9
> --- /dev/null
> +++ b/tools/testing/selftests/ublk/test_generic_16.sh
> @@ -0,0 +1,57 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +
> +. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
> +
> +TID="generic_16"
> +ERR_CODE=0
> +
> +_prep_test "null" "stop --safe command"
> +
> +# Check if SAFE_STOP_DEV feature is supported
> +if ! _have_feature "SAFE_STOP_DEV"; then
> + _cleanup_test "null"
> + exit "$UBLK_SKIP_CODE"
> +fi
> +
> +# Test 1: stop --safe on idle device should succeed
> +dev_id=$(_add_ublk_dev -t null -q 2 -d 32)
> +_check_add_dev $TID $?
> +
> +# Device is idle (no openers), stop --safe should succeed
> +if ! ${UBLK_PROG} stop -n "${dev_id}" --safe; then
> + echo "stop --safe on idle device failed unexpectedly!"
> + ERR_CODE=255
> +fi
> +
> +# Clean up device
> +${UBLK_PROG} del -n "${dev_id}" > /dev/null 2>&1
> +udevadm settle
> +
> +# Test 2: stop --safe on device with active opener should fail
> +dev_id=$(_add_ublk_dev -t null -q 2 -d 32)
> +_check_add_dev $TID $?
> +
> +# Open device in background (dd reads indefinitely)
> +dd if=/dev/ublkb${dev_id} of=/dev/null bs=4k iflag=direct > /dev/null 2>&1 &
> +dd_pid=$!
> +
> +# Give dd time to start
> +sleep 0.2
> +
> +# Device has active opener, stop --safe should fail with -EBUSY
> +if ${UBLK_PROG} stop -n "${dev_id}" --safe 2>/dev/null; then
> + echo "stop --safe on busy device succeeded unexpectedly!"
> + ERR_CODE=255
> +fi
> +
> +# Kill dd and clean up
> +kill $dd_pid 2>/dev/null
> +wait $dd_pid 2>/dev/null
> +
> +# Now device should be idle, regular delete should work
> +${UBLK_PROG} del -n "${dev_id}"
> +udevadm settle
> +
> +_cleanup_test "null"
> +_show_result $TID $ERR_CODE
> --
> 2.39.5 (Apple Git-154)
>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-01-12 22:15 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-12 22:04 [PATCH v7 0/3] ublk: introduce UBLK_CMD_TRY_STOP_DEV Yoav Cohen
2026-01-12 22:05 ` [PATCH v7 1/3] ublk: make ublk_ctrl_stop_dev return void Yoav Cohen
2026-01-12 22:05 ` [PATCH v7 2/3] ublk: add UBLK_CMD_TRY_STOP_DEV command Yoav Cohen
2026-01-12 22:05 ` [PATCH v7 3/3] selftests: ublk: add stop command with --safe option Yoav Cohen
2026-01-12 22:15 ` Caleb Sander Mateos
2026-01-12 22:08 ` [PATCH v7 0/3] ublk: introduce UBLK_CMD_TRY_STOP_DEV Jens Axboe
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox