From 6aa4edc27dee8d544c3c47164e2fe90e19bcedf8 Mon Sep 17 00:00:00 2001 From: Richael Zhuang Date: Tue, 20 Sep 2022 15:12:47 +0800 Subject: [PATCH] bdev/nvme: select io path according to outstanding io numbder Support selecting io path according to number of outstanding io of each path in a channel. It's optional, and can be set by calling RPC "bdev_nvme_set_multipath_policy -s queue_depth". Change-Id: I82cdfbd69b3e105c973844c4f34dc98f0dca2faf Signed-off-by: Richael Zhuang Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/14734 Tested-by: SPDK CI Jenkins Reviewed-by: Shuhei Matsumoto Reviewed-by: Aleksey Marchuk Reviewed-by: Jim Harris --- CHANGELOG.md | 7 ++ doc/jsonrpc.md | 4 +- module/bdev/nvme/bdev_nvme.c | 58 +++++++++++++- module/bdev/nvme/bdev_nvme.h | 9 +++ module/bdev/nvme/bdev_nvme_rpc.c | 28 ++++++- python/spdk/rpc/bdev.py | 5 +- scripts/rpc.py | 4 +- .../lib/bdev/nvme/bdev_nvme.c/bdev_nvme_ut.c | 75 ++++++++++++++++++- 8 files changed, 182 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0643f2ab..153e94580 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,9 @@ Added spdk_rpc_set_allowlist to restrict allowed RPCs to the specified list. Changed `bdev_raid_get_bdevs` RPC output format to include raid_bdev details. +Added `selector` parameter to bdev_nvme_set_multipath_policy RPC to set path selector for multipath. +Option `round_robin` and `queue_depth` are available. + ### bdevperf Promoted the application to example to match similar programs: fio_plugin and perf. @@ -150,6 +153,10 @@ a specified qpair. Updated `bdev_nvme_set_options` RPC (and rpc.py) to support the new `transport_tos` parameter. +For the active-active policy of the multipath mode, in addition to the default round-robin path +selector, the minimum queue depth path selector was added. The minimum queue depth path selector +selects an I/O path according to the number of outstanding requests of each nvme qpair. + ## v22.09 ### accel diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index 803864363..4b16c88db 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -4135,7 +4135,8 @@ Example response: ### bdev_nvme_set_multipath_policy {#rpc_bdev_nvme_set_multipath_policy} -Set multipath policy of the NVMe bdev in multipath mode. +Set multipath policy of the NVMe bdev in multipath mode or set multipath +selector for active-active multipath policy. #### Parameters @@ -4143,6 +4144,7 @@ Name | Optional | Type | Description ----------------------- | -------- | ----------- | ----------- name | Required | string | Name of the NVMe bdev policy | Required | string | Multipath policy: active_active or active_passive +selector | Optional | string | Multipath selector: round_robin or queue_depth, used in active-active mode. Default is round_robin #### Example diff --git a/module/bdev/nvme/bdev_nvme.c b/module/bdev/nvme/bdev_nvme.c index ed923064d..204cb81cf 100644 --- a/module/bdev/nvme/bdev_nvme.c +++ b/module/bdev/nvme/bdev_nvme.c @@ -655,6 +655,7 @@ bdev_nvme_create_bdev_channel_cb(void *io_device, void *ctx_buf) pthread_mutex_lock(&nbdev->mutex); nbdev_ch->mp_policy = nbdev->mp_policy; + nbdev_ch->mp_selector = nbdev->mp_selector; TAILQ_FOREACH(nvme_ns, &nbdev->nvme_ns_list, tailq) { rc = _bdev_nvme_add_io_path(nbdev_ch, nvme_ns); @@ -873,6 +874,51 @@ _bdev_nvme_find_io_path(struct nvme_bdev_channel *nbdev_ch) return non_optimized; } +static struct nvme_io_path * +_bdev_nvme_find_io_path_min_qd(struct nvme_bdev_channel *nbdev_ch) +{ + struct nvme_io_path *io_path; + struct nvme_io_path *optimized = NULL, *non_optimized = NULL; + uint32_t opt_min_qd = UINT32_MAX, non_opt_min_qd = UINT32_MAX; + uint32_t num_outstanding_reqs; + + STAILQ_FOREACH(io_path, &nbdev_ch->io_path_list, stailq) { + if (spdk_unlikely(!nvme_io_path_is_connected(io_path))) { + /* The device is currently resetting. */ + continue; + } + + if (spdk_unlikely(io_path->nvme_ns->ana_state_updating)) { + continue; + } + + num_outstanding_reqs = spdk_nvme_qpair_get_num_outstanding_reqs(io_path->qpair->qpair); + switch (io_path->nvme_ns->ana_state) { + case SPDK_NVME_ANA_OPTIMIZED_STATE: + if (num_outstanding_reqs < opt_min_qd) { + opt_min_qd = num_outstanding_reqs; + optimized = io_path; + } + break; + case SPDK_NVME_ANA_NON_OPTIMIZED_STATE: + if (num_outstanding_reqs < non_opt_min_qd) { + non_opt_min_qd = num_outstanding_reqs; + non_optimized = io_path; + } + break; + default: + break; + } + } + + /* don't cache io path for BDEV_NVME_MP_SELECTOR_QUEUE_DEPTH selector */ + if (optimized != NULL) { + return optimized; + } + + return non_optimized; +} + static inline struct nvme_io_path * bdev_nvme_find_io_path(struct nvme_bdev_channel *nbdev_ch) { @@ -881,7 +927,12 @@ bdev_nvme_find_io_path(struct nvme_bdev_channel *nbdev_ch) return nbdev_ch->current_io_path; } - return _bdev_nvme_find_io_path(nbdev_ch); + if (nbdev_ch->mp_policy == BDEV_NVME_MP_POLICY_ACTIVE_PASSIVE || + nbdev_ch->mp_selector == BDEV_NVME_MP_SELECTOR_ROUND_ROBIN) { + return _bdev_nvme_find_io_path(nbdev_ch); + } else { + return _bdev_nvme_find_io_path_min_qd(nbdev_ch); + } } /* Return true if there is any io_path whose qpair is active or ctrlr is not failed, @@ -3301,6 +3352,7 @@ nvme_bdev_create(struct nvme_ctrlr *nvme_ctrlr, struct nvme_ns *nvme_ns) bdev->ref = 1; bdev->mp_policy = BDEV_NVME_MP_POLICY_ACTIVE_PASSIVE; + bdev->mp_selector = BDEV_NVME_MP_SELECTOR_ROUND_ROBIN; TAILQ_INIT(&bdev->nvme_ns_list); TAILQ_INSERT_TAIL(&bdev->nvme_ns_list, nvme_ns, tailq); bdev->opal = nvme_ctrlr->opal_dev != NULL; @@ -4110,6 +4162,7 @@ _bdev_nvme_set_multipath_policy(struct spdk_io_channel_iter *i) struct nvme_bdev *nbdev = spdk_io_channel_get_io_device(_ch); nbdev_ch->mp_policy = nbdev->mp_policy; + nbdev_ch->mp_selector = nbdev->mp_selector; nbdev_ch->current_io_path = NULL; spdk_for_each_channel_continue(i, 0); @@ -4117,7 +4170,7 @@ _bdev_nvme_set_multipath_policy(struct spdk_io_channel_iter *i) void bdev_nvme_set_multipath_policy(const char *name, enum bdev_nvme_multipath_policy policy, - bdev_nvme_set_multipath_policy_cb cb_fn, void *cb_arg) + enum bdev_nvme_multipath_selector selector, bdev_nvme_set_multipath_policy_cb cb_fn, void *cb_arg) { struct bdev_nvme_set_multipath_policy_ctx *ctx; struct spdk_bdev *bdev; @@ -4153,6 +4206,7 @@ bdev_nvme_set_multipath_policy(const char *name, enum bdev_nvme_multipath_policy pthread_mutex_lock(&nbdev->mutex); nbdev->mp_policy = policy; + nbdev->mp_selector = selector; pthread_mutex_unlock(&nbdev->mutex); spdk_for_each_channel(nbdev, diff --git a/module/bdev/nvme/bdev_nvme.h b/module/bdev/nvme/bdev_nvme.h index 2a80e00ce..2798ab7c7 100644 --- a/module/bdev/nvme/bdev_nvme.h +++ b/module/bdev/nvme/bdev_nvme.h @@ -28,6 +28,11 @@ enum bdev_nvme_multipath_policy { BDEV_NVME_MP_POLICY_ACTIVE_ACTIVE, }; +enum bdev_nvme_multipath_selector { + BDEV_NVME_MP_SELECTOR_ROUND_ROBIN = 1, + BDEV_NVME_MP_SELECTOR_QUEUE_DEPTH, +}; + typedef void (*spdk_bdev_create_nvme_fn)(void *ctx, size_t bdev_count, int rc); typedef void (*spdk_bdev_nvme_start_discovery_fn)(void *ctx, int status); typedef void (*spdk_bdev_nvme_stop_discovery_fn)(void *ctx); @@ -158,6 +163,7 @@ struct nvme_bdev { pthread_mutex_t mutex; int ref; enum bdev_nvme_multipath_policy mp_policy; + enum bdev_nvme_multipath_selector mp_selector; TAILQ_HEAD(, nvme_ns) nvme_ns_list; bool opal; TAILQ_ENTRY(nvme_bdev) tailq; @@ -196,6 +202,7 @@ struct nvme_io_path { struct nvme_bdev_channel { struct nvme_io_path *current_io_path; enum bdev_nvme_multipath_policy mp_policy; + enum bdev_nvme_multipath_selector mp_selector; STAILQ_HEAD(, nvme_io_path) io_path_list; TAILQ_HEAD(retry_io_head, spdk_bdev_io) retry_io_list; struct spdk_poller *retry_io_poller; @@ -345,10 +352,12 @@ typedef void (*bdev_nvme_set_multipath_policy_cb)(void *cb_arg, int rc); * * \param name NVMe bdev name * \param policy Multipath policy (active-passive or active-active) + * \param selector Multipath selector (round_robin, queue_depth) * \param cb_fn Function to be called back after completion. */ void bdev_nvme_set_multipath_policy(const char *name, enum bdev_nvme_multipath_policy policy, + enum bdev_nvme_multipath_selector selector, bdev_nvme_set_multipath_policy_cb cb_fn, void *cb_arg); diff --git a/module/bdev/nvme/bdev_nvme_rpc.c b/module/bdev/nvme/bdev_nvme_rpc.c index a563ac122..f443c4b07 100644 --- a/module/bdev/nvme/bdev_nvme_rpc.c +++ b/module/bdev/nvme/bdev_nvme_rpc.c @@ -2209,6 +2209,7 @@ SPDK_RPC_REGISTER("bdev_nvme_set_preferred_path", rpc_bdev_nvme_set_preferred_pa struct rpc_set_multipath_policy { char *name; enum bdev_nvme_multipath_policy policy; + enum bdev_nvme_multipath_selector selector; }; static void @@ -2234,9 +2235,27 @@ rpc_decode_mp_policy(const struct spdk_json_val *val, void *out) return 0; } +static int +rpc_decode_mp_selector(const struct spdk_json_val *val, void *out) +{ + enum bdev_nvme_multipath_selector *selector = out; + + if (spdk_json_strequal(val, "round_robin") == true) { + *selector = BDEV_NVME_MP_SELECTOR_ROUND_ROBIN; + } else if (spdk_json_strequal(val, "queue_depth") == true) { + *selector = BDEV_NVME_MP_SELECTOR_QUEUE_DEPTH; + } else { + SPDK_NOTICELOG("Invalid parameter value: selector\n"); + return -EINVAL; + } + + return 0; +} + static const struct spdk_json_object_decoder rpc_set_multipath_policy_decoders[] = { {"name", offsetof(struct rpc_set_multipath_policy, name), spdk_json_decode_string}, {"policy", offsetof(struct rpc_set_multipath_policy, policy), rpc_decode_mp_policy}, + {"selector", offsetof(struct rpc_set_multipath_policy, selector), rpc_decode_mp_selector, true}, }; struct rpc_set_multipath_policy_ctx { @@ -2282,7 +2301,14 @@ rpc_bdev_nvme_set_multipath_policy(struct spdk_jsonrpc_request *request, ctx->request = request; - bdev_nvme_set_multipath_policy(ctx->req.name, ctx->req.policy, + if (ctx->req.policy != BDEV_NVME_MP_POLICY_ACTIVE_ACTIVE && ctx->req.selector > 0) { + SPDK_ERRLOG("selector only works in active_active mode\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "spdk_json_decode_object failed"); + goto cleanup; + } + + bdev_nvme_set_multipath_policy(ctx->req.name, ctx->req.policy, ctx->req.selector, rpc_bdev_nvme_set_multipath_policy_done, ctx); return; diff --git a/python/spdk/rpc/bdev.py b/python/spdk/rpc/bdev.py index 9d7cbe435..d8f0a2ed6 100644 --- a/python/spdk/rpc/bdev.py +++ b/python/spdk/rpc/bdev.py @@ -943,16 +943,19 @@ def bdev_nvme_set_preferred_path(client, name, cntlid): return client.call('bdev_nvme_set_preferred_path', params) -def bdev_nvme_set_multipath_policy(client, name, policy): +def bdev_nvme_set_multipath_policy(client, name, policy, selector): """Set multipath policy of the NVMe bdev Args: name: NVMe bdev name policy: Multipath policy (active_passive or active_active) + selector: Multipath selector (round_robin, queue_depth) """ params = {'name': name, 'policy': policy} + if selector: + params['selector'] = selector return client.call('bdev_nvme_set_multipath_policy', params) diff --git a/scripts/rpc.py b/scripts/rpc.py index acafef1da..7a43a5288 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -860,12 +860,14 @@ if __name__ == "__main__": def bdev_nvme_set_multipath_policy(args): rpc.bdev.bdev_nvme_set_multipath_policy(args.client, name=args.name, - policy=args.policy) + policy=args.policy, + selector=args.selector) p = subparsers.add_parser('bdev_nvme_set_multipath_policy', help="""Set multipath policy of the NVMe bdev""") p.add_argument('-b', '--name', help='Name of the NVMe bdev', required=True) p.add_argument('-p', '--policy', help='Multipath policy (active_passive or active_active)', required=True) + p.add_argument('-s', '--selector', help='Multipath selector (round_robin, queue_depth)', required=False) p.set_defaults(func=bdev_nvme_set_multipath_policy) def bdev_nvme_cuse_register(args): diff --git a/test/unit/lib/bdev/nvme/bdev_nvme.c/bdev_nvme_ut.c b/test/unit/lib/bdev/nvme/bdev_nvme.c/bdev_nvme_ut.c index 162c147eb..3ebb2937c 100644 --- a/test/unit/lib/bdev/nvme/bdev_nvme.c/bdev_nvme_ut.c +++ b/test/unit/lib/bdev/nvme/bdev_nvme.c/bdev_nvme_ut.c @@ -305,6 +305,12 @@ spdk_nvme_ctrlr_get_next_active_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid) return 0; } +uint32_t +spdk_nvme_qpair_get_num_outstanding_reqs(struct spdk_nvme_qpair *qpair) +{ + return qpair->num_outstanding_reqs; +} + static TAILQ_HEAD(, spdk_nvme_ctrlr) g_ut_init_ctrlrs = TAILQ_HEAD_INITIALIZER(g_ut_init_ctrlrs); static TAILQ_HEAD(, spdk_nvme_ctrlr) g_ut_attached_ctrlrs = TAILQ_HEAD_INITIALIZER( g_ut_attached_ctrlrs); @@ -5845,6 +5851,7 @@ test_find_next_io_path(void) struct nvme_bdev_channel nbdev_ch = { .io_path_list = STAILQ_HEAD_INITIALIZER(nbdev_ch.io_path_list), .mp_policy = BDEV_NVME_MP_POLICY_ACTIVE_ACTIVE, + .mp_selector = BDEV_NVME_MP_SELECTOR_ROUND_ROBIN, }; struct spdk_nvme_qpair qpair1 = {}, qpair2 = {}, qpair3 = {}; struct spdk_nvme_ctrlr ctrlr1 = {}, ctrlr2 = {}, ctrlr3 = {}; @@ -5866,7 +5873,9 @@ test_find_next_io_path(void) STAILQ_INSERT_TAIL(&nbdev_ch.io_path_list, &io_path2, stailq); STAILQ_INSERT_TAIL(&nbdev_ch.io_path_list, &io_path3, stailq); - /* nbdev_ch->current_io_path is filled always when bdev_nvme_find_next_io_path() is called. */ + /* test the case when nbdev_ch->current_io_path is filled, the case of current_io_path = NULL + * is covered in test_find_io_path. + */ nbdev_ch.current_io_path = &io_path2; nvme_ns1.ana_state = SPDK_NVME_ANA_INACCESSIBLE_STATE; @@ -5891,6 +5900,59 @@ test_find_next_io_path(void) CU_ASSERT(bdev_nvme_find_io_path(&nbdev_ch) == &io_path2); } +static void +test_find_io_path_min_qd(void) +{ + struct nvme_bdev_channel nbdev_ch = { + .io_path_list = STAILQ_HEAD_INITIALIZER(nbdev_ch.io_path_list), + .mp_policy = BDEV_NVME_MP_POLICY_ACTIVE_ACTIVE, + .mp_selector = BDEV_NVME_MP_SELECTOR_QUEUE_DEPTH, + }; + struct spdk_nvme_qpair qpair1 = {}, qpair2 = {}, qpair3 = {}; + struct spdk_nvme_ctrlr ctrlr1 = {}, ctrlr2 = {}, ctrlr3 = {}; + struct nvme_ctrlr nvme_ctrlr1 = { .ctrlr = &ctrlr1, }; + struct nvme_ctrlr nvme_ctrlr2 = { .ctrlr = &ctrlr2, }; + struct nvme_ctrlr nvme_ctrlr3 = { .ctrlr = &ctrlr3, }; + struct nvme_ctrlr_channel ctrlr_ch1 = {}; + struct nvme_ctrlr_channel ctrlr_ch2 = {}; + struct nvme_ctrlr_channel ctrlr_ch3 = {}; + struct nvme_qpair nvme_qpair1 = { .ctrlr_ch = &ctrlr_ch1, .ctrlr = &nvme_ctrlr1, .qpair = &qpair1, }; + struct nvme_qpair nvme_qpair2 = { .ctrlr_ch = &ctrlr_ch2, .ctrlr = &nvme_ctrlr2, .qpair = &qpair2, }; + struct nvme_qpair nvme_qpair3 = { .ctrlr_ch = &ctrlr_ch3, .ctrlr = &nvme_ctrlr3, .qpair = &qpair3, }; + struct nvme_ns nvme_ns1 = {}, nvme_ns2 = {}, nvme_ns3 = {}; + struct nvme_io_path io_path1 = { .qpair = &nvme_qpair1, .nvme_ns = &nvme_ns1, }; + struct nvme_io_path io_path2 = { .qpair = &nvme_qpair2, .nvme_ns = &nvme_ns2, }; + struct nvme_io_path io_path3 = { .qpair = &nvme_qpair3, .nvme_ns = &nvme_ns3, }; + + STAILQ_INSERT_TAIL(&nbdev_ch.io_path_list, &io_path1, stailq); + STAILQ_INSERT_TAIL(&nbdev_ch.io_path_list, &io_path2, stailq); + STAILQ_INSERT_TAIL(&nbdev_ch.io_path_list, &io_path3, stailq); + + /* Test if the minumum io_outstanding or the ANA optimized state is + * prioritized when using least queue depth selector + */ + qpair1.num_outstanding_reqs = 2; + qpair2.num_outstanding_reqs = 1; + qpair3.num_outstanding_reqs = 0; + nvme_ns1.ana_state = SPDK_NVME_ANA_OPTIMIZED_STATE; + nvme_ns2.ana_state = SPDK_NVME_ANA_OPTIMIZED_STATE; + nvme_ns3.ana_state = SPDK_NVME_ANA_NON_OPTIMIZED_STATE; + CU_ASSERT(bdev_nvme_find_io_path(&nbdev_ch) == &io_path2); + + nvme_ns1.ana_state = SPDK_NVME_ANA_NON_OPTIMIZED_STATE; + nvme_ns2.ana_state = SPDK_NVME_ANA_NON_OPTIMIZED_STATE; + nvme_ns3.ana_state = SPDK_NVME_ANA_INACCESSIBLE_STATE; + CU_ASSERT(bdev_nvme_find_io_path(&nbdev_ch) == &io_path2); + + nvme_ns1.ana_state = SPDK_NVME_ANA_OPTIMIZED_STATE; + nvme_ns2.ana_state = SPDK_NVME_ANA_OPTIMIZED_STATE; + nvme_ns3.ana_state = SPDK_NVME_ANA_INACCESSIBLE_STATE; + CU_ASSERT(bdev_nvme_find_io_path(&nbdev_ch) == &io_path2); + + qpair2.num_outstanding_reqs = 4; + CU_ASSERT(bdev_nvme_find_io_path(&nbdev_ch) == &io_path1); +} + static void test_disable_auto_failback(void) { @@ -6115,29 +6177,35 @@ test_set_multipath_policy(void) */ done = -1; bdev_nvme_set_multipath_policy(bdev->disk.name, BDEV_NVME_MP_POLICY_ACTIVE_ACTIVE, + BDEV_NVME_MP_SELECTOR_QUEUE_DEPTH, ut_set_multipath_policy_done, &done); poll_threads(); CU_ASSERT(done == 0); CU_ASSERT(bdev->mp_policy == BDEV_NVME_MP_POLICY_ACTIVE_ACTIVE); + CU_ASSERT(bdev->mp_selector == BDEV_NVME_MP_SELECTOR_QUEUE_DEPTH); ch = spdk_get_io_channel(bdev); SPDK_CU_ASSERT_FATAL(ch != NULL); nbdev_ch = spdk_io_channel_get_ctx(ch); CU_ASSERT(nbdev_ch->mp_policy == BDEV_NVME_MP_POLICY_ACTIVE_ACTIVE); + CU_ASSERT(nbdev_ch->mp_selector == BDEV_NVME_MP_SELECTOR_QUEUE_DEPTH); /* If multipath policy is updated while a I/O channel is active, * the update should be applied to the I/O channel immediately. */ done = -1; bdev_nvme_set_multipath_policy(bdev->disk.name, BDEV_NVME_MP_POLICY_ACTIVE_PASSIVE, + BDEV_NVME_MP_SELECTOR_ROUND_ROBIN, ut_set_multipath_policy_done, &done); poll_threads(); CU_ASSERT(done == 0); CU_ASSERT(bdev->mp_policy == BDEV_NVME_MP_POLICY_ACTIVE_PASSIVE); CU_ASSERT(nbdev_ch->mp_policy == BDEV_NVME_MP_POLICY_ACTIVE_PASSIVE); + CU_ASSERT(bdev->mp_selector == BDEV_NVME_MP_SELECTOR_ROUND_ROBIN); + CU_ASSERT(nbdev_ch->mp_selector == BDEV_NVME_MP_SELECTOR_ROUND_ROBIN); spdk_put_io_channel(ch); @@ -6257,17 +6325,19 @@ test_retry_io_to_same_path(void) done = -1; bdev_nvme_set_multipath_policy(bdev->disk.name, BDEV_NVME_MP_POLICY_ACTIVE_ACTIVE, - ut_set_multipath_policy_done, &done); + BDEV_NVME_MP_SELECTOR_ROUND_ROBIN, ut_set_multipath_policy_done, &done); poll_threads(); CU_ASSERT(done == 0); CU_ASSERT(bdev->mp_policy == BDEV_NVME_MP_POLICY_ACTIVE_ACTIVE); + CU_ASSERT(bdev->mp_selector == BDEV_NVME_MP_SELECTOR_ROUND_ROBIN); ch = spdk_get_io_channel(bdev); SPDK_CU_ASSERT_FATAL(ch != NULL); nbdev_ch = spdk_io_channel_get_ctx(ch); CU_ASSERT(nbdev_ch->mp_policy == BDEV_NVME_MP_POLICY_ACTIVE_ACTIVE); + CU_ASSERT(bdev->mp_selector == BDEV_NVME_MP_SELECTOR_ROUND_ROBIN); bdev_io = ut_alloc_bdev_io(SPDK_BDEV_IO_TYPE_WRITE, bdev, ch); ut_bdev_io_set_buf(bdev_io); @@ -6408,6 +6478,7 @@ main(int argc, const char **argv) CU_ADD_TEST(suite, test_ana_transition); CU_ADD_TEST(suite, test_set_preferred_path); CU_ADD_TEST(suite, test_find_next_io_path); + CU_ADD_TEST(suite, test_find_io_path_min_qd); CU_ADD_TEST(suite, test_disable_auto_failback); CU_ADD_TEST(suite, test_set_multipath_policy); CU_ADD_TEST(suite, test_uuid_generation);