Commit 65955a09 authored by Ming Lei's avatar Ming Lei Committed by Jens Axboe
Browse files

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: default avatarMing Lei <ming.lei@redhat.com>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 93ada1b3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+53 −0
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
#!/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