hotplug: use RPC instead of reading hotplug logs

A new RPC perform_tests has been added.
This request will help us avoid reading hotplug generated
logs, and instead we can stop hotplug right before
it begins generating IO, and resume it when the test
scripts are ready.

Additionally a new command line option has been added
to the hotplug application "--wait-for-rpc", which
indicates that hotplug should wait for
perform_tests RPC before starting its IO.

Change-Id: I71ca148201854ac155cc2a61171a4fb5fc427a19
Signed-off-by: Krzysztof Karas <krzysztof.karas@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13962
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Konrad Sztyber <konrad.sztyber@intel.com>
Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
This commit is contained in:
Krzysztof Karas 2022-09-15 13:29:26 +02:00 committed by Tomasz Zawadzki
parent 25fb8de082
commit ce29e0131f
4 changed files with 75 additions and 22 deletions

View File

@ -10,6 +10,9 @@
#include "spdk/string.h" #include "spdk/string.h"
#include "spdk/util.h" #include "spdk/util.h"
#include "spdk/log.h" #include "spdk/log.h"
#include "spdk/rpc.h"
static const char *g_rpc_addr = "/var/tmp/spdk.sock";
struct dev_ctx { struct dev_ctx {
TAILQ_ENTRY(dev_ctx) tailq; TAILQ_ENTRY(dev_ctx) tailq;
@ -49,6 +52,9 @@ static const char *g_iova_mode = NULL;
static uint64_t g_timeout_in_us = SPDK_SEC_TO_USEC; static uint64_t g_timeout_in_us = SPDK_SEC_TO_USEC;
static struct spdk_nvme_detach_ctx *g_detach_ctx; static struct spdk_nvme_detach_ctx *g_detach_ctx;
static bool g_wait_for_rpc = false;
static bool g_rpc_received = false;
static void task_complete(struct perf_task *task); static void task_complete(struct perf_task *task);
static void timeout_cb(void *cb_arg, struct spdk_nvme_ctrlr *ctrlr, static void timeout_cb(void *cb_arg, struct spdk_nvme_ctrlr *ctrlr,
@ -402,24 +408,34 @@ usage(char *program_name)
printf("\t[-l log level]\n"); printf("\t[-l log level]\n");
printf("\t Available log levels:\n"); printf("\t Available log levels:\n");
printf("\t disabled, error, warning, notice, info, debug\n"); printf("\t disabled, error, warning, notice, info, debug\n");
printf("\t[--wait-for-rpc wait for RPC perform_tests\n");
printf("\t to proceed with starting IO on NVMe disks]\n");
} }
static const struct option g_wait_option[] = {
#define WAIT_FOR_RPC_OPT_IDX 257
{"wait-for-rpc", no_argument, NULL, WAIT_FOR_RPC_OPT_IDX},
};
static int static int
parse_args(int argc, char **argv) parse_args(int argc, char **argv)
{ {
int op; int op, opt_idx;
long int val; long int val;
/* default value */ /* default value */
g_time_in_sec = 0; g_time_in_sec = 0;
while ((op = getopt(argc, argv, "c:i:l:m:n:r:t:")) != -1) { while ((op = getopt_long(argc, argv, "c:i:l:m:n:r:t:", g_wait_option, &opt_idx)) != -1) {
if (op == '?') { if (op == '?') {
usage(argv[0]); usage(argv[0]);
return 1; return 1;
} }
switch (op) { switch (op) {
case WAIT_FOR_RPC_OPT_IDX:
g_wait_for_rpc = true;
break;
case 'c': case 'c':
case 'i': case 'i':
case 'n': case 'n':
@ -498,6 +514,39 @@ register_controllers(void)
return 0; return 0;
} }
/* Hotplug RPC */
static void
rpc_perform_tests(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
if (params) {
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"'perform_tests' requires no arguments");
return;
}
spdk_jsonrpc_send_bool_response(request, true);
g_rpc_received = true;
}
SPDK_RPC_REGISTER("perform_tests", rpc_perform_tests, SPDK_RPC_RUNTIME);
static void
wait_for_rpc_call(void)
{
fprintf(stderr,
"Listening for perform_tests to start the application...\n");
spdk_rpc_listen(g_rpc_addr);
spdk_rpc_set_state(SPDK_RPC_RUNTIME);
while (!g_rpc_received) {
spdk_rpc_accept();
}
/* Run spdk_rpc_accept() one more time to trigger
* spdk_jsonrpv_server_poll() and send the RPC response. */
spdk_rpc_accept();
}
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -531,6 +580,10 @@ main(int argc, char **argv)
goto cleanup; goto cleanup;
} }
if (g_wait_for_rpc) {
wait_for_rpc_call();
}
fprintf(stderr, "Initialization complete. Starting I/O...\n"); fprintf(stderr, "Initialization complete. Starting I/O...\n");
io_loop(); io_loop();

View File

@ -0,0 +1,11 @@
from spdk.rpc.client import print_json
def perform_tests(args):
print_json(args.client.call('perform_tests'))
def spdk_rpc_plugin_initialize(subparsers):
p = subparsers.add_parser('perform_tests',
help='Returns true when hotplug apps starts running IO')
p.set_defaults(func=perform_tests)

View File

@ -614,7 +614,7 @@ function check_json_rpc() {
echo "Missing JSON-RPC documentation for ${rpc}" echo "Missing JSON-RPC documentation for ${rpc}"
rc=1 rc=1
fi fi
done < <(git grep -h -E "^SPDK_RPC_REGISTER\(" ':!test/*') done < <(git grep -h -E "^SPDK_RPC_REGISTER\(" ':!test/*' ':!examples/nvme/hotplug/*')
if [ $rc -eq 0 ]; then if [ $rc -eq 0 ]; then
echo " OK" echo " OK"

View File

@ -5,6 +5,9 @@ testdir=$(readlink -f $(dirname $0))
rootdir=$(readlink -f $testdir/../..) rootdir=$(readlink -f $testdir/../..)
source $rootdir/test/common/autotest_common.sh source $rootdir/test/common/autotest_common.sh
export PYTHONPATH="$rootdir/examples/nvme/hotplug/"
rpc_py=$rootdir/scripts/rpc.py
function beetle_ssh() { function beetle_ssh() {
ssh_opts="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" ssh_opts="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
if [[ -n $BEETLE_SSH_KEY ]]; then if [[ -n $BEETLE_SSH_KEY ]]; then
@ -69,25 +72,14 @@ if [ "$driver" = "uio_pci_generic" ]; then
mode="-m pa" mode="-m pa"
fi fi
exec {log}> >(tee -a "$testdir/log.txt") "$SPDK_EXAMPLE_DIR/hotplug" -i 0 -t 100 -n $((2 * nvme_count)) -r $((2 * nvme_count)) \
exec >&$log 2>&1 $mode --wait-for-rpc &
"$SPDK_EXAMPLE_DIR/hotplug" -i 0 -t 100 -n $((2 * nvme_count)) -r $((2 * nvme_count)) $mode &
hotplug_pid=$! hotplug_pid=$!
trap 'killprocess $hotplug_pid; restore_device; rm $testdir/log.txt; exit 1' SIGINT SIGTERM EXIT trap 'killprocess $hotplug_pid; restore_device; exit 1' SIGINT SIGTERM EXIT
i=0 waitforlisten $hotplug_pid
while ! grep -q "Starting I/O" $testdir/log.txt; do $rpc_py --plugin hotplug_plugin perform_tests
[ $i -lt 20 ] || break
i=$((i + 1))
sleep 1
done
if ! grep -q "Starting I/O" $testdir/log.txt; then
rm $testdir/log.txt
exit 1
fi
# Add and remove NVMe with delays between to give some time for IO to proceed # Add and remove NVMe with delays between to give some time for IO to proceed
remove_device remove_device
@ -103,14 +95,11 @@ timing_enter wait_for_example
if ! wait $hotplug_pid; then if ! wait $hotplug_pid; then
echo "Hotplug example returned error!" echo "Hotplug example returned error!"
rm $testdir/log.txt
exit 1 exit 1
fi fi
timing_exit wait_for_example timing_exit wait_for_example
rm $testdir/log.txt
trap - SIGINT SIGTERM EXIT trap - SIGINT SIGTERM EXIT
restore_device restore_device