diff --git a/test/bdev/bdevio/bdevio.c b/test/bdev/bdevio/bdevio.c index 8d5e23c57..416980c9d 100644 --- a/test/bdev/bdevio/bdevio.c +++ b/test/bdev/bdevio/bdevio.c @@ -39,6 +39,9 @@ #include "spdk/log.h" #include "spdk/thread.h" #include "spdk/event.h" +#include "spdk/rpc.h" +#include "spdk/util.h" +#include "spdk/string.h" #include "CUnit/Basic.h" @@ -52,6 +55,7 @@ pthread_cond_t g_test_cond; static uint32_t g_lcore_id_init; static uint32_t g_lcore_id_ut; static uint32_t g_lcore_id_io; +static bool g_wait_for_tests = false; struct io_target { struct spdk_bdev *bdev; @@ -71,6 +75,7 @@ struct bdevio_request { struct io_target *g_io_targets = NULL; struct io_target *g_current_io_target = NULL; +static void rpc_perform_tests_cb(unsigned num_failures, struct spdk_jsonrpc_request *request); static void execute_spdk_function(spdk_event_fn fn, void *arg1, void *arg2) @@ -938,18 +943,24 @@ static void __stop_init_thread(void *arg1, void *arg2) { unsigned num_failures = (unsigned)(uintptr_t)arg1; + struct spdk_jsonrpc_request *request = arg2; bdevio_cleanup_targets(); + if (g_wait_for_tests) { + /* Do not stop the app yet, wait for another RPC */ + rpc_perform_tests_cb(num_failures, request); + return; + } spdk_app_stop(num_failures); } static void -stop_init_thread(unsigned num_failures) +stop_init_thread(unsigned num_failures, struct spdk_jsonrpc_request *request) { struct spdk_event *event; event = spdk_event_allocate(g_lcore_id_init, __stop_init_thread, - (void *)(uintptr_t)num_failures, NULL); + (void *)(uintptr_t)num_failures, request); spdk_event_call(event); } @@ -1032,6 +1043,7 @@ __setup_ut_on_single_target(struct io_target *target) static void __run_ut_thread(void *arg1, void *arg2) { + struct spdk_jsonrpc_request *request = arg2; int rc = 0; struct io_target *target; unsigned num_failures; @@ -1039,7 +1051,7 @@ __run_ut_thread(void *arg1, void *arg2) if (CU_initialize_registry() != CUE_SUCCESS) { /* CUnit error, probably won't recover */ rc = CU_get_error(); - stop_init_thread(-rc); + stop_init_thread(-rc, request); } target = g_io_targets; @@ -1047,7 +1059,7 @@ __run_ut_thread(void *arg1, void *arg2) rc = __setup_ut_on_single_target(target); if (rc < 0) { /* CUnit error, probably won't recover */ - stop_init_thread(-rc); + stop_init_thread(-rc, request); } target = target->next; } @@ -1056,7 +1068,7 @@ __run_ut_thread(void *arg1, void *arg2) num_failures = CU_get_number_of_failures(); CU_cleanup_registry(); - stop_init_thread(num_failures); + stop_init_thread(num_failures, request); } static void @@ -1078,6 +1090,11 @@ test_main(void *arg1) spdk_app_stop(-1); } + if (g_wait_for_tests) { + /* Do not perform any tests until RPC is received */ + return; + } + if (bdevio_construct_targets() < 0) { spdk_app_stop(-1); return; @@ -1090,14 +1107,104 @@ test_main(void *arg1) static void bdevio_usage(void) { + printf(" -w start bdevio app and wait for RPC to start the tests\n"); } static int bdevio_parse_arg(int ch, char *arg) { + switch (ch) { + case 'w': + g_wait_for_tests = true; + break; + default: + return -EINVAL; + } return 0; } +struct rpc_perform_tests { + char *name; +}; + +static void +free_rpc_perform_tests(struct rpc_perform_tests *r) +{ + free(r->name); +} + +static const struct spdk_json_object_decoder rpc_perform_tests_decoders[] = { + {"name", offsetof(struct rpc_perform_tests, name), spdk_json_decode_string, true}, +}; + +static void +rpc_perform_tests_cb(unsigned num_failures, struct spdk_jsonrpc_request *request) +{ + struct spdk_json_write_ctx *w; + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + spdk_json_write_uint32(w, num_failures); + spdk_jsonrpc_end_result(request, w); +} + +static void +rpc_perform_tests(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) +{ + struct rpc_perform_tests req = {NULL}; + struct spdk_event *event; + struct spdk_bdev *bdev; + int rc; + + if (params && spdk_json_decode_object(params, rpc_perform_tests_decoders, + SPDK_COUNTOF(rpc_perform_tests_decoders), + &req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + goto invalid; + } + + if (req.name) { + bdev = spdk_bdev_get_by_name(req.name); + if (bdev == NULL) { + SPDK_ERRLOG("Bdev '%s' does not exist\n", req.name); + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Bdev '%s' does not exist: %s", + req.name, spdk_strerror(ENODEV)); + goto invalid; + } + rc = bdevio_construct_target(bdev); + if (rc < 0) { + SPDK_ERRLOG("Could not construct target for bdev '%s'\n", spdk_bdev_get_name(bdev)); + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Could not construct target for bdev '%s': %s", + spdk_bdev_get_name(bdev), spdk_strerror(-rc)); + goto invalid; + } + } else { + rc = bdevio_construct_targets(); + if (rc < 0) { + SPDK_ERRLOG("Could not construct targets for all bdevs\n"); + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Could not construct targets for all bdevs: %s", + spdk_strerror(-rc)); + goto invalid; + } + } + free_rpc_perform_tests(&req); + + event = spdk_event_allocate(g_lcore_id_ut, __run_ut_thread, NULL, request); + spdk_event_call(event); + + return; + +invalid: + free_rpc_perform_tests(&req); +} +SPDK_RPC_REGISTER("perform_tests", rpc_perform_tests, SPDK_RPC_RUNTIME) + int main(int argc, char **argv) { @@ -1108,7 +1215,7 @@ main(int argc, char **argv) opts.name = "bdevio"; opts.reactor_mask = "0x7"; - if ((rc = spdk_app_parse_args(argc, argv, &opts, "", NULL, + if ((rc = spdk_app_parse_args(argc, argv, &opts, "w", NULL, bdevio_parse_arg, bdevio_usage)) != SPDK_APP_PARSE_ARGS_SUCCESS) { return rc; diff --git a/test/bdev/bdevio/tests.py b/test/bdev/bdevio/tests.py new file mode 100755 index 000000000..e89a5fb04 --- /dev/null +++ b/test/bdev/bdevio/tests.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +from rpc.client import print_dict, JSONRPCException + +import logging +import argparse +import rpc +import sys +import shlex + +try: + from shlex import quote +except ImportError: + from pipes import quote + + +def print_array(a): + print(" ".join((quote(v) for v in a))) + + +def perform_tests_func(client, name=None): + """ + + Args: + name: bdev name to perform bdevio tests on (optional; if omitted, test all bdevs) + + Returns: + Number of failures in tests. 0 means no errors found. + """ + params = {} + if name: + params['name'] = name + return client.call('perform_tests', params) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description='SPDK RPC command line interface. NOTE: spdk/scripts/ is expected in PYTHONPATH') + parser.add_argument('-s', dest='server_addr', + help='RPC domain socket path or IP address', default='/var/tmp/spdk.sock') + parser.add_argument('-p', dest='port', + help='RPC port number (if server_addr is IP address)', + default=5260, type=int) + parser.add_argument('-t', dest='timeout', + help='Timeout as a floating point number expressed in seconds waiting for response. Default: 60.0', + default=60.0, type=float) + parser.add_argument('-v', dest='verbose', action='store_const', const="INFO", + help='Set verbose mode to INFO', default="ERROR") + parser.add_argument('--verbose', dest='verbose', choices=['DEBUG', 'INFO', 'ERROR'], + help="""Set verbose level. """) + subparsers = parser.add_subparsers(help='RPC methods') + + def perform_tests(args): + print_dict(perform_tests_func(args.client, name=args.name)) + + p = subparsers.add_parser('perform_tests', help='Perform all bdevio tests on select bdev') + p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1") + p.set_defaults(func=perform_tests) + + def call_rpc_func(args): + try: + args.func(args) + except JSONRPCException as ex: + print(ex.message) + exit(1) + + def execute_script(parser, client, fd): + for rpc_call in map(str.rstrip, fd): + if not rpc_call.strip(): + continue + args = parser.parse_args(shlex.split(rpc_call)) + args.client = client + call_rpc_func(args) + + args = parser.parse_args() + args.client = rpc.client.JSONRPCClient(args.server_addr, args.port, args.timeout, log_level=getattr(logging, args.verbose.upper())) + if hasattr(args, 'func'): + call_rpc_func(args) + elif sys.stdin.isatty(): + # No arguments and no data piped through stdin + parser.print_help() + exit(1) + else: + execute_script(parser, args.client, sys.stdin)