Skip to content

Commit 65955a0

Browse files
Ming Leiaxboe
authored andcommitted
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>
1 parent 93ada1b commit 65955a0

4 files changed

Lines changed: 112 additions & 0 deletions

File tree

tools/testing/selftests/ublk/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ TEST_PROGS += test_generic_12.sh
2323
TEST_PROGS += test_generic_13.sh
2424
TEST_PROGS += test_generic_14.sh
2525
TEST_PROGS += test_generic_15.sh
26+
TEST_PROGS += test_generic_16.sh
2627

2728
TEST_PROGS += test_null_01.sh
2829
TEST_PROGS += test_null_02.sh

tools/testing/selftests/ublk/kublk.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ static int ublk_ctrl_stop_dev(struct ublk_dev *dev)
108108
return __ublk_ctrl_cmd(dev, &data);
109109
}
110110

111+
static int ublk_ctrl_try_stop_dev(struct ublk_dev *dev)
112+
{
113+
struct ublk_ctrl_cmd_data data = {
114+
.cmd_op = UBLK_U_CMD_TRY_STOP_DEV,
115+
};
116+
117+
return __ublk_ctrl_cmd(dev, &data);
118+
}
119+
111120
static int ublk_ctrl_start_dev(struct ublk_dev *dev,
112121
int daemon_pid)
113122
{
@@ -1424,6 +1433,42 @@ static int cmd_dev_del(struct dev_ctx *ctx)
14241433
return 0;
14251434
}
14261435

1436+
static int cmd_dev_stop(struct dev_ctx *ctx)
1437+
{
1438+
int number = ctx->dev_id;
1439+
struct ublk_dev *dev;
1440+
int ret;
1441+
1442+
if (number < 0) {
1443+
ublk_err("%s: device id is required\n", __func__);
1444+
return -EINVAL;
1445+
}
1446+
1447+
dev = ublk_ctrl_init();
1448+
dev->dev_info.dev_id = number;
1449+
1450+
ret = ublk_ctrl_get_info(dev);
1451+
if (ret < 0)
1452+
goto fail;
1453+
1454+
if (ctx->safe_stop) {
1455+
ret = ublk_ctrl_try_stop_dev(dev);
1456+
if (ret < 0)
1457+
ublk_err("%s: try_stop dev %d failed ret %d\n",
1458+
__func__, number, ret);
1459+
} else {
1460+
ret = ublk_ctrl_stop_dev(dev);
1461+
if (ret < 0)
1462+
ublk_err("%s: stop dev %d failed ret %d\n",
1463+
__func__, number, ret);
1464+
}
1465+
1466+
fail:
1467+
ublk_ctrl_deinit(dev);
1468+
1469+
return ret;
1470+
}
1471+
14271472
static int __cmd_dev_list(struct dev_ctx *ctx)
14281473
{
14291474
struct ublk_dev *dev = ublk_ctrl_init();
@@ -1487,6 +1532,7 @@ static int cmd_dev_get_features(void)
14871532
FEAT_NAME(UBLK_F_PER_IO_DAEMON),
14881533
FEAT_NAME(UBLK_F_BUF_REG_OFF_DAEMON),
14891534
FEAT_NAME(UBLK_F_INTEGRITY),
1535+
FEAT_NAME(UBLK_F_SAFE_STOP_DEV)
14901536
};
14911537
struct ublk_dev *dev;
14921538
__u64 features = 0;
@@ -1616,6 +1662,8 @@ static int cmd_dev_help(char *exe)
16161662

16171663
printf("%s del [-n dev_id] -a \n", exe);
16181664
printf("\t -a delete all devices -n delete specified device\n\n");
1665+
printf("%s stop -n dev_id [--safe]\n", exe);
1666+
printf("\t --safe only stop if device has no active openers\n\n");
16191667
printf("%s list [-n dev_id] -a \n", exe);
16201668
printf("\t -a list all devices, -n list specified device, default -a \n\n");
16211669
printf("%s features\n", exe);
@@ -1653,6 +1701,7 @@ int main(int argc, char *argv[])
16531701
{ "pi_offset", 1, NULL, 0 },
16541702
{ "csum_type", 1, NULL, 0 },
16551703
{ "tag_size", 1, NULL, 0 },
1704+
{ "safe", 0, NULL, 0 },
16561705
{ 0, 0, 0, 0 }
16571706
};
16581707
const struct ublk_tgt_ops *ops = NULL;
@@ -1760,6 +1809,8 @@ int main(int argc, char *argv[])
17601809
}
17611810
if (!strcmp(longopts[option_idx].name, "tag_size"))
17621811
ctx.tag_size = strtoul(optarg, NULL, 0);
1812+
if (!strcmp(longopts[option_idx].name, "safe"))
1813+
ctx.safe_stop = 1;
17631814
break;
17641815
case '?':
17651816
/*
@@ -1842,6 +1893,8 @@ int main(int argc, char *argv[])
18421893
}
18431894
} else if (!strcmp(cmd, "del"))
18441895
ret = cmd_dev_del(&ctx);
1896+
else if (!strcmp(cmd, "stop"))
1897+
ret = cmd_dev_stop(&ctx);
18451898
else if (!strcmp(cmd, "list")) {
18461899
ctx.all = 1;
18471900
ret = cmd_dev_list(&ctx);

tools/testing/selftests/ublk/kublk.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ struct dev_ctx {
8383
__u8 pi_offset;
8484
__u8 csum_type;
8585
__u8 tag_size;
86+
unsigned int safe_stop:1;
8687

8788
int _evtfd;
8889
int _shmid;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-2.0
3+
4+
. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
5+
6+
TID="generic_16"
7+
ERR_CODE=0
8+
9+
_prep_test "null" "stop --safe command"
10+
11+
# Check if SAFE_STOP_DEV feature is supported
12+
if ! _have_feature "SAFE_STOP_DEV"; then
13+
_cleanup_test "null"
14+
exit "$UBLK_SKIP_CODE"
15+
fi
16+
17+
# Test 1: stop --safe on idle device should succeed
18+
dev_id=$(_add_ublk_dev -t null -q 2 -d 32)
19+
_check_add_dev $TID $?
20+
21+
# Device is idle (no openers), stop --safe should succeed
22+
if ! ${UBLK_PROG} stop -n "${dev_id}" --safe; then
23+
echo "stop --safe on idle device failed unexpectedly!"
24+
ERR_CODE=255
25+
fi
26+
27+
# Clean up device
28+
${UBLK_PROG} del -n "${dev_id}" > /dev/null 2>&1
29+
udevadm settle
30+
31+
# Test 2: stop --safe on device with active opener should fail
32+
dev_id=$(_add_ublk_dev -t null -q 2 -d 32)
33+
_check_add_dev $TID $?
34+
35+
# Open device in background (dd reads indefinitely)
36+
dd if=/dev/ublkb${dev_id} of=/dev/null bs=4k iflag=direct > /dev/null 2>&1 &
37+
dd_pid=$!
38+
39+
# Give dd time to start
40+
sleep 0.2
41+
42+
# Device has active opener, stop --safe should fail with -EBUSY
43+
if ${UBLK_PROG} stop -n "${dev_id}" --safe 2>/dev/null; then
44+
echo "stop --safe on busy device succeeded unexpectedly!"
45+
ERR_CODE=255
46+
fi
47+
48+
# Kill dd and clean up
49+
kill $dd_pid 2>/dev/null
50+
wait $dd_pid 2>/dev/null
51+
52+
# Now device should be idle, regular delete should work
53+
${UBLK_PROG} del -n "${dev_id}"
54+
udevadm settle
55+
56+
_cleanup_test "null"
57+
_show_result $TID $ERR_CODE

0 commit comments

Comments
 (0)