]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/spdk/test/bdev/bdevio/bdevio.c
import 15.2.0 Octopus source
[ceph.git] / ceph / src / spdk / test / bdev / bdevio / bdevio.c
index c139b6f2983560af1af644628eb691b192ec5f33..bb8e7a9ee7227ddfbcf66cd67185fcded61a12e8 100644 (file)
@@ -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;
@@ -70,6 +74,8 @@ 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)
@@ -102,45 +108,61 @@ __get_io_channel(void *arg1, void *arg2)
 }
 
 static int
-bdevio_construct_targets(void)
+bdevio_construct_target(struct spdk_bdev *bdev)
 {
-       struct spdk_bdev *bdev;
        struct io_target *target;
        int rc;
+       uint64_t num_blocks = spdk_bdev_get_num_blocks(bdev);
+       uint32_t block_size = spdk_bdev_get_block_size(bdev);
 
-       printf("I/O targets:\n");
+       target = malloc(sizeof(struct io_target));
+       if (target == NULL) {
+               return -ENOMEM;
+       }
 
-       bdev = spdk_bdev_first_leaf();
-       while (bdev != NULL) {
-               uint64_t num_blocks = spdk_bdev_get_num_blocks(bdev);
-               uint32_t block_size = spdk_bdev_get_block_size(bdev);
+       rc = spdk_bdev_open(bdev, true, NULL, NULL, &target->bdev_desc);
+       if (rc != 0) {
+               free(target);
+               SPDK_ERRLOG("Could not open leaf bdev %s, error=%d\n", spdk_bdev_get_name(bdev), rc);
+               return rc;
+       }
 
-               target = malloc(sizeof(struct io_target));
-               if (target == NULL) {
-                       return -ENOMEM;
-               }
+       printf("  %s: %" PRIu64 " blocks of %" PRIu32 " bytes (%" PRIu64 " MiB)\n",
+              spdk_bdev_get_name(bdev),
+              num_blocks, block_size,
+              (num_blocks * block_size + 1024 * 1024 - 1) / (1024 * 1024));
 
-               rc = spdk_bdev_open(bdev, true, NULL, NULL, &target->bdev_desc);
-               if (rc != 0) {
-                       free(target);
-                       SPDK_ERRLOG("Could not open leaf bdev %s, error=%d\n", spdk_bdev_get_name(bdev), rc);
-                       bdev = spdk_bdev_next_leaf(bdev);
-                       continue;
-               }
+       target->bdev = bdev;
+       target->next = g_io_targets;
+       execute_spdk_function(__get_io_channel, target, NULL);
+       g_io_targets = target;
+
+       return 0;
+}
 
-               printf("  %s: %" PRIu64 " blocks of %" PRIu32 " bytes (%" PRIu64 " MiB)\n",
-                      spdk_bdev_get_name(bdev),
-                      num_blocks, block_size,
-                      (num_blocks * block_size + 1024 * 1024 - 1) / (1024 * 1024));
+static int
+bdevio_construct_targets(void)
+{
+       struct spdk_bdev *bdev;
+       int rc;
 
-               target->bdev = bdev;
-               target->next = g_io_targets;
-               execute_spdk_function(__get_io_channel, target, NULL);
-               g_io_targets = target;
+       printf("I/O targets:\n");
 
+       bdev = spdk_bdev_first_leaf();
+       while (bdev != NULL) {
+               rc = bdevio_construct_target(bdev);
+               if (rc < 0) {
+                       SPDK_ERRLOG("Could not construct bdev %s, error=%d\n", spdk_bdev_get_name(bdev), rc);
+                       return rc;
+               }
                bdev = spdk_bdev_next_leaf(bdev);
        }
 
+       if (g_io_targets == NULL) {
+               SPDK_ERRLOG("No bdevs to perform tests on\n");
+               return -1;
+       }
+
        return 0;
 }
 
@@ -340,48 +362,44 @@ blockdev_write_read(uint32_t data_length, uint32_t iov_len, int pattern, uint64_
        char    *rx_buf = NULL;
        int     rc;
 
-       target = g_io_targets;
-       while (target != NULL) {
-               if (data_length < spdk_bdev_get_block_size(target->bdev) ||
-                   data_length / spdk_bdev_get_block_size(target->bdev) > spdk_bdev_get_num_blocks(target->bdev)) {
-                       target = target->next;
-                       continue;
-               }
+       target = g_current_io_target;
 
-               if (!write_zeroes) {
-                       initialize_buffer(&tx_buf, pattern, data_length);
-                       initialize_buffer(&rx_buf, 0, data_length);
+       if (data_length < spdk_bdev_get_block_size(target->bdev) ||
+           data_length / spdk_bdev_get_block_size(target->bdev) > spdk_bdev_get_num_blocks(target->bdev)) {
+               return;
+       }
 
-                       blockdev_write(target, tx_buf, offset, data_length, iov_len);
-               } else {
-                       initialize_buffer(&tx_buf, 0, data_length);
-                       initialize_buffer(&rx_buf, pattern, data_length);
+       if (!write_zeroes) {
+               initialize_buffer(&tx_buf, pattern, data_length);
+               initialize_buffer(&rx_buf, 0, data_length);
 
-                       blockdev_write_zeroes(target, tx_buf, offset, data_length);
-               }
+               blockdev_write(target, tx_buf, offset, data_length, iov_len);
+       } else {
+               initialize_buffer(&tx_buf, 0, data_length);
+               initialize_buffer(&rx_buf, pattern, data_length);
 
+               blockdev_write_zeroes(target, tx_buf, offset, data_length);
+       }
 
-               if (expected_rc == 0) {
-                       CU_ASSERT_EQUAL(g_completion_success, true);
-               } else {
-                       CU_ASSERT_EQUAL(g_completion_success, false);
-               }
-               blockdev_read(target, rx_buf, offset, data_length, iov_len);
 
-               if (expected_rc == 0) {
-                       CU_ASSERT_EQUAL(g_completion_success, true);
-               } else {
-                       CU_ASSERT_EQUAL(g_completion_success, false);
-               }
+       if (expected_rc == 0) {
+               CU_ASSERT_EQUAL(g_completion_success, true);
+       } else {
+               CU_ASSERT_EQUAL(g_completion_success, false);
+       }
+       blockdev_read(target, rx_buf, offset, data_length, iov_len);
 
-               if (g_completion_success) {
-                       rc = blockdev_write_read_data_match(rx_buf, tx_buf, data_length);
-                       /* Assert the write by comparing it with values read
-                        * from each blockdev */
-                       CU_ASSERT_EQUAL(rc, 0);
-               }
+       if (expected_rc == 0) {
+               CU_ASSERT_EQUAL(g_completion_success, true);
+       } else {
+               CU_ASSERT_EQUAL(g_completion_success, false);
+       }
 
-               target = target->next;
+       if (g_completion_success) {
+               rc = blockdev_write_read_data_match(rx_buf, tx_buf, data_length);
+               /* Assert the write by comparing it with values read
+                * from each blockdev */
+               CU_ASSERT_EQUAL(rc, 0);
        }
 }
 
@@ -670,33 +688,29 @@ blockdev_write_read_offset_plus_nbytes_equals_bdev_size(void)
        uint32_t block_size;
        int rc;
 
-       target = g_io_targets;
-       while (target != NULL) {
-               bdev = target->bdev;
-
-               block_size = spdk_bdev_get_block_size(bdev);
+       target = g_current_io_target;
+       bdev = target->bdev;
 
-               /* The start offset has been set to a marginal value
-                * such that offset + nbytes == Total size of
-                * blockdev. */
-               offset = ((spdk_bdev_get_num_blocks(bdev) - 1) * block_size);
+       block_size = spdk_bdev_get_block_size(bdev);
 
-               initialize_buffer(&tx_buf, 0xA3, block_size);
-               initialize_buffer(&rx_buf, 0, block_size);
+       /* The start offset has been set to a marginal value
+        * such that offset + nbytes == Total size of
+        * blockdev. */
+       offset = ((spdk_bdev_get_num_blocks(bdev) - 1) * block_size);
 
-               blockdev_write(target, tx_buf, offset, block_size, 0);
-               CU_ASSERT_EQUAL(g_completion_success, true);
+       initialize_buffer(&tx_buf, 0xA3, block_size);
+       initialize_buffer(&rx_buf, 0, block_size);
 
-               blockdev_read(target, rx_buf, offset, block_size, 0);
-               CU_ASSERT_EQUAL(g_completion_success, true);
+       blockdev_write(target, tx_buf, offset, block_size, 0);
+       CU_ASSERT_EQUAL(g_completion_success, true);
 
-               rc = blockdev_write_read_data_match(rx_buf, tx_buf, block_size);
-               /* Assert the write by comparing it with values read
-                * from each blockdev */
-               CU_ASSERT_EQUAL(rc, 0);
+       blockdev_read(target, rx_buf, offset, block_size, 0);
+       CU_ASSERT_EQUAL(g_completion_success, true);
 
-               target = target->next;
-       }
+       rc = blockdev_write_read_data_match(rx_buf, tx_buf, block_size);
+       /* Assert the write by comparing it with values read
+        * from each blockdev */
+       CU_ASSERT_EQUAL(rc, 0);
 }
 
 static void
@@ -715,26 +729,22 @@ blockdev_write_read_offset_plus_nbytes_gt_bdev_size(void)
        CU_ASSERT_TRUE(data_length < BUFFER_SIZE);
        pattern = 0xA3;
 
-       target = g_io_targets;
-       while (target != NULL) {
-               bdev = target->bdev;
-
-               /* The start offset has been set to a valid value
-                * but offset + nbytes is greater than the Total size
-                * of the blockdev. The test should fail. */
-               offset = ((spdk_bdev_get_num_blocks(bdev) * spdk_bdev_get_block_size(bdev)) - 1024);
+       target = g_current_io_target;
+       bdev = target->bdev;
 
-               initialize_buffer(&tx_buf, pattern, data_length);
-               initialize_buffer(&rx_buf, 0, data_length);
+       /* The start offset has been set to a valid value
+        * but offset + nbytes is greater than the Total size
+        * of the blockdev. The test should fail. */
+       offset = ((spdk_bdev_get_num_blocks(bdev) * spdk_bdev_get_block_size(bdev)) - 1024);
 
-               blockdev_write(target, tx_buf, offset, data_length, 0);
-               CU_ASSERT_EQUAL(g_completion_success, false);
+       initialize_buffer(&tx_buf, pattern, data_length);
+       initialize_buffer(&rx_buf, 0, data_length);
 
-               blockdev_read(target, rx_buf, offset, data_length, 0);
-               CU_ASSERT_EQUAL(g_completion_success, false);
+       blockdev_write(target, tx_buf, offset, data_length, 0);
+       CU_ASSERT_EQUAL(g_completion_success, false);
 
-               target = target->next;
-       }
+       blockdev_read(target, rx_buf, offset, data_length, 0);
+       CU_ASSERT_EQUAL(g_completion_success, false);
 }
 
 static void
@@ -805,66 +815,229 @@ __blockdev_reset(void *arg1, void *arg2)
 }
 
 static void
-blockdev_reset(struct io_target *target)
+blockdev_test_reset(void)
 {
        struct bdevio_request req;
+       struct io_target *target;
 
+       target = g_current_io_target;
        req.target = target;
 
        g_completion_success = false;
 
        execute_spdk_function(__blockdev_reset, &req, NULL);
+
+       /* Workaround: NVMe-oF target doesn't support reset yet - so for now
+        *  don't fail the test if it's an NVMe bdev.
+        */
+       if (!spdk_bdev_io_type_supported(target->bdev, SPDK_BDEV_IO_TYPE_NVME_IO)) {
+               CU_ASSERT_EQUAL(g_completion_success, true);
+       }
 }
 
+struct bdevio_passthrough_request {
+       struct spdk_nvme_cmd cmd;
+       void *buf;
+       uint32_t len;
+       struct io_target *target;
+       int sct;
+       int sc;
+};
+
 static void
-blockdev_test_reset(void)
+nvme_pt_test_complete(struct spdk_bdev_io *bdev_io, bool success, void *arg)
 {
-       struct io_target        *target;
+       struct bdevio_passthrough_request *pt_req = arg;
 
-       target = g_io_targets;
-       while (target != NULL) {
-               blockdev_reset(target);
-               CU_ASSERT_EQUAL(g_completion_success, true);
+       spdk_bdev_io_get_nvme_status(bdev_io, &pt_req->sct, &pt_req->sc);
+       spdk_bdev_free_io(bdev_io);
+       wake_ut_thread();
+}
 
-               target = target->next;
+static void
+__blockdev_nvme_passthru(void *arg1, void *arg2)
+{
+       struct bdevio_passthrough_request *pt_req = arg1;
+       struct io_target *target = pt_req->target;
+       int rc;
+
+       rc = spdk_bdev_nvme_io_passthru(target->bdev_desc, target->ch,
+                                       &pt_req->cmd, pt_req->buf, pt_req->len,
+                                       nvme_pt_test_complete, pt_req);
+       if (rc) {
+               wake_ut_thread();
+       }
+}
+
+static void
+blockdev_test_nvme_passthru_rw(void)
+{
+       struct bdevio_passthrough_request pt_req;
+       void *write_buf, *read_buf;
+       struct io_target *target;
+
+       target = g_current_io_target;
+
+       if (!spdk_bdev_io_type_supported(target->bdev, SPDK_BDEV_IO_TYPE_NVME_IO)) {
+               return;
+       }
+
+       memset(&pt_req, 0, sizeof(pt_req));
+       pt_req.target = target;
+       pt_req.cmd.opc = SPDK_NVME_OPC_WRITE;
+       pt_req.cmd.nsid = 1;
+       *(uint64_t *)&pt_req.cmd.cdw10 = 4;
+       pt_req.cmd.cdw12 = 0;
+
+       pt_req.len = spdk_bdev_get_block_size(target->bdev);
+       write_buf = spdk_dma_malloc(pt_req.len, 0, NULL);
+       memset(write_buf, 0xA5, pt_req.len);
+       pt_req.buf = write_buf;
+
+       pt_req.sct = SPDK_NVME_SCT_VENDOR_SPECIFIC;
+       pt_req.sc = SPDK_NVME_SC_INVALID_FIELD;
+       execute_spdk_function(__blockdev_nvme_passthru, &pt_req, NULL);
+       CU_ASSERT(pt_req.sct == SPDK_NVME_SCT_GENERIC);
+       CU_ASSERT(pt_req.sc == SPDK_NVME_SC_SUCCESS);
+
+       pt_req.cmd.opc = SPDK_NVME_OPC_READ;
+       read_buf = spdk_dma_zmalloc(pt_req.len, 0, NULL);
+       pt_req.buf = read_buf;
+
+       pt_req.sct = SPDK_NVME_SCT_VENDOR_SPECIFIC;
+       pt_req.sc = SPDK_NVME_SC_INVALID_FIELD;
+       execute_spdk_function(__blockdev_nvme_passthru, &pt_req, NULL);
+       CU_ASSERT(pt_req.sct == SPDK_NVME_SCT_GENERIC);
+       CU_ASSERT(pt_req.sc == SPDK_NVME_SC_SUCCESS);
+
+       CU_ASSERT(!memcmp(read_buf, write_buf, pt_req.len));
+       spdk_dma_free(read_buf);
+       spdk_dma_free(write_buf);
+}
+
+static void
+blockdev_test_nvme_passthru_vendor_specific(void)
+{
+       struct bdevio_passthrough_request pt_req;
+       struct io_target *target;
+
+       target = g_current_io_target;
+
+       if (!spdk_bdev_io_type_supported(target->bdev, SPDK_BDEV_IO_TYPE_NVME_IO)) {
+               return;
+       }
+
+       memset(&pt_req, 0, sizeof(pt_req));
+       pt_req.target = target;
+       pt_req.cmd.opc = 0x7F; /* choose known invalid opcode */
+       pt_req.cmd.nsid = 1;
+
+       pt_req.sct = SPDK_NVME_SCT_VENDOR_SPECIFIC;
+       pt_req.sc = SPDK_NVME_SC_SUCCESS;
+       execute_spdk_function(__blockdev_nvme_passthru, &pt_req, NULL);
+       CU_ASSERT(pt_req.sct == SPDK_NVME_SCT_GENERIC);
+       CU_ASSERT(pt_req.sc == SPDK_NVME_SC_INVALID_OPCODE);
+}
+
+static void
+__blockdev_nvme_admin_passthru(void *arg1, void *arg2)
+{
+       struct bdevio_passthrough_request *pt_req = arg1;
+       struct io_target *target = pt_req->target;
+       int rc;
+
+       rc = spdk_bdev_nvme_admin_passthru(target->bdev_desc, target->ch,
+                                          &pt_req->cmd, pt_req->buf, pt_req->len,
+                                          nvme_pt_test_complete, pt_req);
+       if (rc) {
+               wake_ut_thread();
+       }
+}
+
+static void
+blockdev_test_nvme_admin_passthru(void)
+{
+       struct io_target *target;
+       struct bdevio_passthrough_request pt_req;
+
+       target = g_current_io_target;
+
+       if (!spdk_bdev_io_type_supported(target->bdev, SPDK_BDEV_IO_TYPE_NVME_ADMIN)) {
+               return;
        }
+
+       memset(&pt_req, 0, sizeof(pt_req));
+       pt_req.target = target;
+       pt_req.cmd.opc = SPDK_NVME_OPC_IDENTIFY;
+       pt_req.cmd.nsid = 0;
+       *(uint64_t *)&pt_req.cmd.cdw10 = SPDK_NVME_IDENTIFY_CTRLR;
+
+       pt_req.len = sizeof(struct spdk_nvme_ctrlr_data);
+       pt_req.buf = spdk_dma_malloc(pt_req.len, 0, NULL);
+
+       pt_req.sct = SPDK_NVME_SCT_GENERIC;
+       pt_req.sc = SPDK_NVME_SC_SUCCESS;
+       execute_spdk_function(__blockdev_nvme_admin_passthru, &pt_req, NULL);
+       CU_ASSERT(pt_req.sct == SPDK_NVME_SCT_GENERIC);
+       CU_ASSERT(pt_req.sc == SPDK_NVME_SC_SUCCESS);
 }
 
 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);
 }
 
-static void
-__run_ut_thread(void *arg1, void *arg2)
+static int
+suite_init(void)
 {
-       CU_pSuite suite = NULL;
-       unsigned num_failures;
-
-       if (CU_initialize_registry() != CUE_SUCCESS) {
-               stop_init_thread(CU_get_error());
-               return;
+       if (g_current_io_target == NULL) {
+               g_current_io_target = g_io_targets;
        }
+       return 0;
+}
+
+static int
+suite_fini(void)
+{
+       g_current_io_target = g_current_io_target->next;
+       return 0;
+}
+
+#define SUITE_NAME_MAX 64
 
-       suite = CU_add_suite("components_suite", NULL, NULL);
+static int
+__setup_ut_on_single_target(struct io_target *target)
+{
+       unsigned rc = 0;
+       CU_pSuite suite = NULL;
+       char name[SUITE_NAME_MAX];
+
+       snprintf(name, sizeof(name), "bdevio tests on: %s", spdk_bdev_get_name(target->bdev));
+       suite = CU_add_suite(name, suite_init, suite_fini);
        if (suite == NULL) {
                CU_cleanup_registry();
-               stop_init_thread(CU_get_error());
-               return;
+               rc = CU_get_error();
+               return -rc;
        }
 
        if (
@@ -873,6 +1046,8 @@ __run_ut_thread(void *arg1, void *arg2)
                || CU_add_test(suite, "blockdev write zeroes read 1m", blockdev_write_zeroes_read_1m) == NULL
                || CU_add_test(suite, "blockdev write zeroes read 3m", blockdev_write_zeroes_read_3m) == NULL
                || CU_add_test(suite, "blockdev write zeroes read 3.5m", blockdev_write_zeroes_read_3m_500k) == NULL
+               || CU_add_test(suite, "blockdev reset",
+                              blockdev_test_reset) == NULL
                || CU_add_test(suite, "blockdev write read 512 bytes",
                               blockdev_write_read_512Bytes) == NULL
                || CU_add_test(suite, "blockdev write read size > 128k",
@@ -896,23 +1071,53 @@ __run_ut_thread(void *arg1, void *arg2)
                               blockdev_writev_readv_size_gt_128k) == NULL
                || CU_add_test(suite, "blockdev writev readv size > 128k in two iovs",
                               blockdev_writev_readv_size_gt_128k_two_iov) == NULL
-               || CU_add_test(suite, "blockdev reset",
-                              blockdev_test_reset) == NULL
+               || CU_add_test(suite, "blockdev nvme passthru rw",
+                              blockdev_test_nvme_passthru_rw) == NULL
+               || CU_add_test(suite, "blockdev nvme passthru vendor specific",
+                              blockdev_test_nvme_passthru_vendor_specific) == NULL
+               || CU_add_test(suite, "blockdev nvme admin passthru",
+                              blockdev_test_nvme_admin_passthru) == NULL
        ) {
                CU_cleanup_registry();
-               stop_init_thread(CU_get_error());
-               return;
+               rc = CU_get_error();
+               return -rc;
+       }
+       return 0;
+}
+
+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;
+
+       if (CU_initialize_registry() != CUE_SUCCESS) {
+               /* CUnit error, probably won't recover */
+               rc = CU_get_error();
+               stop_init_thread(-rc, request);
        }
 
+       target = g_io_targets;
+       while (target != NULL) {
+               rc = __setup_ut_on_single_target(target);
+               if (rc < 0) {
+                       /* CUnit error, probably won't recover */
+                       stop_init_thread(-rc, request);
+               }
+               target = target->next;
+       }
        CU_basic_set_mode(CU_BRM_VERBOSE);
        CU_basic_run_tests();
        num_failures = CU_get_number_of_failures();
        CU_cleanup_registry();
-       stop_init_thread(num_failures);
+
+       stop_init_thread(num_failures, request);
 }
 
 static void
-test_main(void *arg1, void *arg2)
+test_main(void *arg1)
 {
        struct spdk_event *event;
 
@@ -930,6 +1135,11 @@ test_main(void *arg1, void *arg2)
                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;
@@ -942,12 +1152,103 @@ test_main(void *arg1, void *arg2)
 static void
 bdevio_usage(void)
 {
+       printf(" -w                        start bdevio app and wait for RPC to start the tests\n");
 }
 
-static void
+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)
@@ -956,17 +1257,16 @@ main(int argc, char **argv)
        struct spdk_app_opts    opts = {};
 
        spdk_app_opts_init(&opts);
-       opts.name = "bdevtest";
-       opts.rpc_addr = NULL;
+       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;
        }
 
-       rc = spdk_app_start(&opts, test_main, NULL, NULL);
+       rc = spdk_app_start(&opts, test_main, NULL);
        spdk_app_fini();
 
        return rc;