* [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 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
* 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
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