mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-19 06:38:26 -05:00
selftests: ublk: add stop command with --safe option
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> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
57
tools/testing/selftests/ublk/test_generic_16.sh
Executable file
57
tools/testing/selftests/ublk/test_generic_16.sh
Executable file
@@ -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
|
||||
Reference in New Issue
Block a user