per Intel policy to include file commit date using git cmd below. The policy does not apply to non-Intel (C) notices. git log --follow -C90% --format=%ad --date default <file> | tail -1 and then pull just the 4 digit year from the result. Intel copyrights were not added to files where Intel either had no contribution ot the contribution lacked substance (ie license header updates, formatting changes, etc). Contribution date used "--follow -C95%" to get the most accurate date. Note that several files in this patch didn't end the license/(c) block with a blank comment line so these were added as the vast majority of files do have this last blank line. Simply there for consistency. Signed-off-by: paul luse <paul.e.luse@intel.com> Change-Id: Id5b7ce4f658fe87132f14139ead58d6e285c04d4 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15192 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Community-CI: Mellanox Build Bot
938 lines
26 KiB
C
938 lines
26 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright (C) 2020 Intel Corporation.
|
|
* Copyright (c) 2018-2019 Broadcom. All Rights Reserved.
|
|
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
*/
|
|
|
|
/* NVMF FC LS Command Processor Unit Test */
|
|
|
|
#include "spdk/env.h"
|
|
#include "spdk_cunit.h"
|
|
#include "spdk/nvmf.h"
|
|
#include "spdk/endian.h"
|
|
#include "spdk/trace.h"
|
|
#include "spdk/log.h"
|
|
|
|
#include "ut_multithread.c"
|
|
|
|
#include "transport.h"
|
|
#include "nvmf_internal.h"
|
|
#include "nvmf_fc.h"
|
|
|
|
#include "fc_ls.c"
|
|
|
|
#define LAST_RSLT_STOP_TEST 999
|
|
|
|
void spdk_set_thread(struct spdk_thread *thread);
|
|
|
|
/*
|
|
* SPDK Stuff
|
|
*/
|
|
|
|
DEFINE_STUB(spdk_nvmf_request_complete, int, (struct spdk_nvmf_request *req), -ENOSPC);
|
|
DEFINE_STUB(spdk_nvmf_subsystem_host_allowed, bool,
|
|
(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn), true);
|
|
DEFINE_STUB_V(spdk_nvme_trid_populate_transport, (struct spdk_nvme_transport_id *trid,
|
|
enum spdk_nvme_transport_type trtype));
|
|
DEFINE_STUB(rte_hash_del_key, int32_t, (const struct rte_hash *h, const void *key), 0);
|
|
DEFINE_STUB(rte_hash_lookup_data, int, (const struct rte_hash *h, const void *key, void **data),
|
|
-ENOENT);
|
|
DEFINE_STUB(rte_hash_add_key_data, int, (const struct rte_hash *h, const void *key, void *data), 0);
|
|
DEFINE_STUB(rte_hash_create, struct rte_hash *, (const struct rte_hash_parameters *params),
|
|
(void *)1);
|
|
DEFINE_STUB_V(rte_hash_free, (struct rte_hash *h));
|
|
DEFINE_STUB(nvmf_fc_poll_group_valid, bool, (struct spdk_nvmf_fc_poll_group *fgroup), true);
|
|
|
|
static const char *fc_ut_subsystem_nqn =
|
|
"nqn.2017-11.io.spdk:sn.390c0dc7c87011e786b300a0989adc53:subsystem.good";
|
|
static struct spdk_nvmf_host fc_ut_initiator = {
|
|
.nqn = "nqn.2017-11.fc_host",
|
|
};
|
|
static struct spdk_nvmf_host *fc_ut_host = &fc_ut_initiator;
|
|
static struct spdk_nvmf_tgt g_nvmf_tgt;
|
|
static struct spdk_nvmf_transport_opts g_nvmf_transport_opts = {
|
|
.max_queue_depth = 128,
|
|
.max_qpairs_per_ctrlr = 4,
|
|
.max_aq_depth = 32,
|
|
};
|
|
static struct spdk_nvmf_subsystem g_nvmf_subsystem;
|
|
|
|
void nvmf_fc_request_abort(struct spdk_nvmf_fc_request *fc_req, bool send_abts,
|
|
spdk_nvmf_fc_caller_cb cb, void *cb_args);
|
|
void spdk_bdev_io_abort(struct spdk_bdev_io *bdev_io, void *ctx);
|
|
void nvmf_fc_request_abort_complete(void *arg1);
|
|
bool nvmf_fc_req_in_xfer(struct spdk_nvmf_fc_request *fc_req);
|
|
|
|
struct spdk_nvmf_subsystem *
|
|
spdk_nvmf_tgt_find_subsystem(struct spdk_nvmf_tgt *tgt, const char *subnqn)
|
|
{
|
|
if (!strcmp(subnqn, g_nvmf_subsystem.subnqn)) {
|
|
return &g_nvmf_subsystem;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
spdk_nvmf_poll_group_add(struct spdk_nvmf_poll_group *group,
|
|
struct spdk_nvmf_qpair *qpair)
|
|
{
|
|
qpair->state = SPDK_NVMF_QPAIR_ACTIVE;
|
|
return 0;
|
|
}
|
|
|
|
const struct spdk_nvmf_transport_ops spdk_nvmf_transport_fc = {
|
|
.type = (enum spdk_nvme_transport_type) SPDK_NVMF_TRTYPE_FC,
|
|
.create = NULL,
|
|
.destroy = NULL,
|
|
|
|
.listen = NULL,
|
|
.stop_listen = NULL,
|
|
|
|
.listener_discover = NULL,
|
|
|
|
.poll_group_create = NULL,
|
|
.poll_group_destroy = NULL,
|
|
.poll_group_add = NULL,
|
|
.poll_group_poll = NULL,
|
|
|
|
.req_complete = NULL,
|
|
|
|
.qpair_fini = NULL,
|
|
|
|
};
|
|
|
|
struct spdk_nvmf_transport g_nvmf_transport = {
|
|
.ops = &spdk_nvmf_transport_fc,
|
|
.tgt = &g_nvmf_tgt,
|
|
};
|
|
|
|
struct spdk_nvmf_transport *
|
|
spdk_nvmf_tgt_get_transport(struct spdk_nvmf_tgt *tgt, const char *transport_name)
|
|
{
|
|
return &g_nvmf_transport;
|
|
}
|
|
|
|
int
|
|
spdk_nvmf_qpair_disconnect(struct spdk_nvmf_qpair *qpair, nvmf_qpair_disconnect_cb cb_fn, void *ctx)
|
|
{
|
|
cb_fn(ctx);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
spdk_nvmf_tgt_new_qpair(struct spdk_nvmf_tgt *tgt, struct spdk_nvmf_qpair *qpair)
|
|
{
|
|
struct spdk_nvmf_fc_conn *fc_conn;
|
|
struct spdk_nvmf_fc_hwqp *hwqp = NULL;
|
|
struct spdk_nvmf_fc_ls_add_conn_api_data *api_data = NULL;
|
|
struct spdk_nvmf_fc_port *fc_port;
|
|
static int hwqp_idx = 0;
|
|
|
|
fc_conn = SPDK_CONTAINEROF(qpair, struct spdk_nvmf_fc_conn, qpair);
|
|
api_data = &fc_conn->create_opd->u.add_conn;
|
|
|
|
fc_port = fc_conn->fc_assoc->tgtport->fc_port;
|
|
hwqp = &fc_port->io_queues[hwqp_idx];
|
|
|
|
if (!nvmf_fc_assign_conn_to_hwqp(hwqp,
|
|
&fc_conn->conn_id,
|
|
fc_conn->max_queue_depth)) {
|
|
goto err;
|
|
}
|
|
|
|
fc_conn->hwqp = hwqp;
|
|
|
|
/* If this is for ADMIN connection, then update assoc ID. */
|
|
if (fc_conn->qpair.qid == 0) {
|
|
fc_conn->fc_assoc->assoc_id = fc_conn->conn_id;
|
|
}
|
|
|
|
nvmf_fc_poller_api_func(hwqp, SPDK_NVMF_FC_POLLER_API_ADD_CONNECTION, &api_data->args);
|
|
hwqp_idx++;
|
|
return;
|
|
err:
|
|
nvmf_fc_ls_add_conn_failure(api_data->assoc, api_data->ls_rqst,
|
|
api_data->args.fc_conn, api_data->aq_conn);
|
|
}
|
|
|
|
void
|
|
nvmf_fc_free_conn_reqpool(struct spdk_nvmf_fc_conn *fc_conn)
|
|
{
|
|
}
|
|
|
|
int
|
|
nvmf_fc_create_conn_reqpool(struct spdk_nvmf_fc_conn *fc_conn)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* LLD functions
|
|
*/
|
|
|
|
bool
|
|
nvmf_fc_assign_conn_to_hwqp(struct spdk_nvmf_fc_hwqp *hwqp,
|
|
uint64_t *conn_id, uint32_t sq_size)
|
|
{
|
|
static uint16_t conn_cnt = 0;
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_ls, "Assign connection to HWQP\n");
|
|
|
|
/* create connection ID */
|
|
*conn_id = ((uint64_t)hwqp->hwqp_id | (conn_cnt++ << 8));
|
|
|
|
SPDK_DEBUGLOG(nvmf_fc_ls,
|
|
"New connection assigned to HWQP%d, conn_id 0x%lx\n",
|
|
hwqp->hwqp_id, *conn_id);
|
|
return true;
|
|
}
|
|
|
|
struct spdk_nvmf_fc_hwqp *
|
|
nvmf_fc_get_hwqp_from_conn_id(struct spdk_nvmf_fc_hwqp *queues,
|
|
uint32_t num_queues, uint64_t conn_id)
|
|
{
|
|
return &queues[(conn_id & 0xff) % num_queues];
|
|
}
|
|
|
|
struct spdk_nvmf_fc_srsr_bufs *
|
|
nvmf_fc_alloc_srsr_bufs(size_t rqst_len, size_t rsp_len)
|
|
{
|
|
struct spdk_nvmf_fc_srsr_bufs *srsr_bufs;
|
|
|
|
srsr_bufs = calloc(1, sizeof(struct spdk_nvmf_fc_srsr_bufs));
|
|
if (!srsr_bufs) {
|
|
return NULL;
|
|
}
|
|
|
|
srsr_bufs->rqst = calloc(1, rqst_len + rsp_len);
|
|
if (srsr_bufs->rqst) {
|
|
srsr_bufs->rqst_len = rqst_len;
|
|
srsr_bufs->rsp = srsr_bufs->rqst + rqst_len;
|
|
srsr_bufs->rsp_len = rsp_len;
|
|
} else {
|
|
free(srsr_bufs);
|
|
srsr_bufs = NULL;
|
|
}
|
|
|
|
return srsr_bufs;
|
|
}
|
|
|
|
void
|
|
nvmf_fc_free_srsr_bufs(struct spdk_nvmf_fc_srsr_bufs *srsr_bufs)
|
|
{
|
|
if (srsr_bufs) {
|
|
free(srsr_bufs->rqst);
|
|
free(srsr_bufs);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The Tests
|
|
*/
|
|
|
|
enum _test_run_type {
|
|
TEST_RUN_TYPE_CREATE_ASSOC = 1,
|
|
TEST_RUN_TYPE_CREATE_CONN,
|
|
TEST_RUN_TYPE_DISCONNECT,
|
|
TEST_RUN_TYPE_CONN_BAD_ASSOC,
|
|
TEST_RUN_TYPE_FAIL_LS_RSP,
|
|
TEST_RUN_TYPE_DISCONNECT_BAD_ASSOC,
|
|
TEST_RUN_TYPE_CREATE_MAX_ASSOC,
|
|
};
|
|
|
|
static uint32_t g_test_run_type = 0;
|
|
static uint64_t g_curr_assoc_id = 0;
|
|
static uint16_t g_create_conn_test_cnt = 0;
|
|
static int g_last_rslt = 0;
|
|
static bool g_spdk_nvmf_fc_xmt_srsr_req = false;
|
|
static struct spdk_nvmf_fc_remote_port_info g_rem_port;
|
|
|
|
static void
|
|
run_create_assoc_test(const char *subnqn,
|
|
struct spdk_nvmf_host *host,
|
|
struct spdk_nvmf_fc_nport *tgt_port)
|
|
{
|
|
struct spdk_nvmf_fc_ls_rqst ls_rqst;
|
|
struct spdk_nvmf_fc_ls_cr_assoc_rqst ca_rqst;
|
|
uint8_t respbuf[128];
|
|
|
|
memset(&ca_rqst, 0, sizeof(struct spdk_nvmf_fc_ls_cr_assoc_rqst));
|
|
|
|
ca_rqst.w0.ls_cmd = FCNVME_LS_CREATE_ASSOCIATION;
|
|
to_be32(&ca_rqst.desc_list_len,
|
|
sizeof(struct spdk_nvmf_fc_ls_cr_assoc_rqst) -
|
|
(2 * sizeof(uint32_t)));
|
|
to_be32(&ca_rqst.assoc_cmd.desc_tag, FCNVME_LSDESC_CREATE_ASSOC_CMD);
|
|
to_be32(&ca_rqst.assoc_cmd.desc_len,
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_cr_assoc_cmd) -
|
|
(2 * sizeof(uint32_t)));
|
|
to_be16(&ca_rqst.assoc_cmd.ersp_ratio, (g_nvmf_transport.opts.max_aq_depth / 2));
|
|
to_be16(&ca_rqst.assoc_cmd.sqsize, g_nvmf_transport.opts.max_aq_depth - 1);
|
|
snprintf(&ca_rqst.assoc_cmd.subnqn[0], strlen(subnqn) + 1, "%s", subnqn);
|
|
snprintf(&ca_rqst.assoc_cmd.hostnqn[0], strlen(host->nqn) + 1, "%s", host->nqn);
|
|
ls_rqst.rqstbuf.virt = &ca_rqst;
|
|
ls_rqst.rspbuf.virt = respbuf;
|
|
ls_rqst.rqst_len = sizeof(struct spdk_nvmf_fc_ls_cr_assoc_rqst);
|
|
ls_rqst.rsp_len = 0;
|
|
ls_rqst.rpi = 5000;
|
|
ls_rqst.private_data = NULL;
|
|
ls_rqst.s_id = 0;
|
|
ls_rqst.nport = tgt_port;
|
|
ls_rqst.rport = &g_rem_port;
|
|
ls_rqst.nvmf_tgt = &g_nvmf_tgt;
|
|
|
|
nvmf_fc_handle_ls_rqst(&ls_rqst);
|
|
poll_thread(0);
|
|
}
|
|
|
|
static void
|
|
run_create_conn_test(struct spdk_nvmf_host *host,
|
|
struct spdk_nvmf_fc_nport *tgt_port,
|
|
uint64_t assoc_id,
|
|
uint16_t qid)
|
|
{
|
|
struct spdk_nvmf_fc_ls_rqst ls_rqst;
|
|
struct spdk_nvmf_fc_ls_cr_conn_rqst cc_rqst;
|
|
uint8_t respbuf[128];
|
|
|
|
memset(&cc_rqst, 0, sizeof(struct spdk_nvmf_fc_ls_cr_conn_rqst));
|
|
|
|
/* fill in request descriptor */
|
|
cc_rqst.w0.ls_cmd = FCNVME_LS_CREATE_CONNECTION;
|
|
to_be32(&cc_rqst.desc_list_len,
|
|
sizeof(struct spdk_nvmf_fc_ls_cr_conn_rqst) -
|
|
(2 * sizeof(uint32_t)));
|
|
|
|
/* fill in connect command descriptor */
|
|
to_be32(&cc_rqst.connect_cmd.desc_tag, FCNVME_LSDESC_CREATE_CONN_CMD);
|
|
to_be32(&cc_rqst.connect_cmd.desc_len,
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_cr_conn_cmd) -
|
|
(2 * sizeof(uint32_t)));
|
|
|
|
to_be16(&cc_rqst.connect_cmd.ersp_ratio, (g_nvmf_transport.opts.max_queue_depth / 2));
|
|
to_be16(&cc_rqst.connect_cmd.sqsize, g_nvmf_transport.opts.max_queue_depth - 1);
|
|
to_be16(&cc_rqst.connect_cmd.qid, qid);
|
|
|
|
/* fill in association id descriptor */
|
|
to_be32(&cc_rqst.assoc_id.desc_tag, FCNVME_LSDESC_ASSOC_ID),
|
|
to_be32(&cc_rqst.assoc_id.desc_len,
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id) -
|
|
(2 * sizeof(uint32_t)));
|
|
cc_rqst.assoc_id.association_id = assoc_id; /* already be64 */
|
|
|
|
ls_rqst.rqstbuf.virt = &cc_rqst;
|
|
ls_rqst.rspbuf.virt = respbuf;
|
|
ls_rqst.rqst_len = sizeof(struct spdk_nvmf_fc_ls_cr_conn_rqst);
|
|
ls_rqst.rsp_len = 0;
|
|
ls_rqst.rpi = 5000;
|
|
ls_rqst.private_data = NULL;
|
|
ls_rqst.s_id = 0;
|
|
ls_rqst.nport = tgt_port;
|
|
ls_rqst.rport = &g_rem_port;
|
|
ls_rqst.nvmf_tgt = &g_nvmf_tgt;
|
|
|
|
nvmf_fc_handle_ls_rqst(&ls_rqst);
|
|
poll_thread(0);
|
|
}
|
|
|
|
static void
|
|
run_disconn_test(struct spdk_nvmf_fc_nport *tgt_port,
|
|
uint64_t assoc_id)
|
|
{
|
|
struct spdk_nvmf_fc_ls_rqst ls_rqst;
|
|
struct spdk_nvmf_fc_ls_disconnect_rqst dc_rqst;
|
|
uint8_t respbuf[128];
|
|
|
|
memset(&dc_rqst, 0, sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst));
|
|
|
|
/* fill in request descriptor */
|
|
dc_rqst.w0.ls_cmd = FCNVME_LS_DISCONNECT;
|
|
to_be32(&dc_rqst.desc_list_len,
|
|
sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst) -
|
|
(2 * sizeof(uint32_t)));
|
|
|
|
/* fill in disconnect command descriptor */
|
|
to_be32(&dc_rqst.disconn_cmd.desc_tag, FCNVME_LSDESC_DISCONN_CMD);
|
|
to_be32(&dc_rqst.disconn_cmd.desc_len,
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_disconn_cmd) -
|
|
(2 * sizeof(uint32_t)));
|
|
|
|
/* fill in association id descriptor */
|
|
to_be32(&dc_rqst.assoc_id.desc_tag, FCNVME_LSDESC_ASSOC_ID),
|
|
to_be32(&dc_rqst.assoc_id.desc_len,
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id) -
|
|
(2 * sizeof(uint32_t)));
|
|
dc_rqst.assoc_id.association_id = assoc_id; /* already be64 */
|
|
|
|
ls_rqst.rqstbuf.virt = &dc_rqst;
|
|
ls_rqst.rspbuf.virt = respbuf;
|
|
ls_rqst.rqst_len = sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst);
|
|
ls_rqst.rsp_len = 0;
|
|
ls_rqst.rpi = 5000;
|
|
ls_rqst.private_data = NULL;
|
|
ls_rqst.s_id = 0;
|
|
ls_rqst.nport = tgt_port;
|
|
ls_rqst.rport = &g_rem_port;
|
|
ls_rqst.nvmf_tgt = &g_nvmf_tgt;
|
|
|
|
nvmf_fc_handle_ls_rqst(&ls_rqst);
|
|
poll_thread(0);
|
|
}
|
|
|
|
static int
|
|
handle_ca_rsp(struct spdk_nvmf_fc_ls_rqst *ls_rqst, bool max_assoc_test)
|
|
{
|
|
struct spdk_nvmf_fc_ls_acc_hdr *acc_hdr =
|
|
(struct spdk_nvmf_fc_ls_acc_hdr *) ls_rqst->rspbuf.virt;
|
|
|
|
|
|
if (acc_hdr->rqst.w0.ls_cmd == FCNVME_LS_CREATE_ASSOCIATION) {
|
|
if (acc_hdr->w0.ls_cmd == FCNVME_LS_ACC) {
|
|
struct spdk_nvmf_fc_ls_cr_assoc_acc *acc =
|
|
(struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt;
|
|
|
|
CU_ASSERT(from_be32(&acc_hdr->desc_list_len) ==
|
|
sizeof(struct spdk_nvmf_fc_ls_cr_assoc_acc) - 8);
|
|
CU_ASSERT(from_be32(&acc_hdr->rqst.desc_len) ==
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_rqst) - 8);
|
|
CU_ASSERT(from_be32(&acc_hdr->rqst.desc_tag) ==
|
|
FCNVME_LSDESC_RQST);
|
|
CU_ASSERT(from_be32(&acc->assoc_id.desc_tag) ==
|
|
FCNVME_LSDESC_ASSOC_ID);
|
|
CU_ASSERT(from_be32(&acc->assoc_id.desc_len) ==
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id) - 8);
|
|
CU_ASSERT(from_be32(&acc->conn_id.desc_tag) ==
|
|
FCNVME_LSDESC_CONN_ID);
|
|
CU_ASSERT(from_be32(&acc->conn_id.desc_len) ==
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_conn_id) - 8);
|
|
|
|
g_curr_assoc_id = acc->assoc_id.association_id;
|
|
g_create_conn_test_cnt++;
|
|
return 0;
|
|
} else if (max_assoc_test) {
|
|
/* reject reason code should be insufficient resources */
|
|
struct spdk_nvmf_fc_ls_rjt *rjt =
|
|
(struct spdk_nvmf_fc_ls_rjt *)ls_rqst->rspbuf.virt;
|
|
if (rjt->rjt.reason_code == FCNVME_RJT_RC_INSUFF_RES) {
|
|
return LAST_RSLT_STOP_TEST;
|
|
}
|
|
}
|
|
CU_FAIL("Unexpected reject response for create association");
|
|
} else {
|
|
CU_FAIL("Response not for create association");
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_cc_rsp(struct spdk_nvmf_fc_ls_rqst *ls_rqst)
|
|
{
|
|
struct spdk_nvmf_fc_ls_acc_hdr *acc_hdr =
|
|
(struct spdk_nvmf_fc_ls_acc_hdr *) ls_rqst->rspbuf.virt;
|
|
|
|
if (acc_hdr->rqst.w0.ls_cmd == FCNVME_LS_CREATE_CONNECTION) {
|
|
if (acc_hdr->w0.ls_cmd == FCNVME_LS_ACC) {
|
|
struct spdk_nvmf_fc_ls_cr_conn_acc *acc =
|
|
(struct spdk_nvmf_fc_ls_cr_conn_acc *)ls_rqst->rspbuf.virt;
|
|
|
|
CU_ASSERT(from_be32(&acc_hdr->desc_list_len) ==
|
|
sizeof(struct spdk_nvmf_fc_ls_cr_conn_acc) - 8);
|
|
CU_ASSERT(from_be32(&acc_hdr->rqst.desc_len) ==
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_rqst) - 8);
|
|
CU_ASSERT(from_be32(&acc_hdr->rqst.desc_tag) ==
|
|
FCNVME_LSDESC_RQST);
|
|
CU_ASSERT(from_be32(&acc->conn_id.desc_tag) ==
|
|
FCNVME_LSDESC_CONN_ID);
|
|
CU_ASSERT(from_be32(&acc->conn_id.desc_len) ==
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_conn_id) - 8);
|
|
g_create_conn_test_cnt++;
|
|
return 0;
|
|
}
|
|
|
|
if (acc_hdr->w0.ls_cmd == FCNVME_LS_RJT) {
|
|
struct spdk_nvmf_fc_ls_rjt *rjt =
|
|
(struct spdk_nvmf_fc_ls_rjt *)ls_rqst->rspbuf.virt;
|
|
if (g_create_conn_test_cnt == g_nvmf_transport.opts.max_qpairs_per_ctrlr) {
|
|
/* expected to get reject for too many connections */
|
|
CU_ASSERT(rjt->rjt.reason_code ==
|
|
FCNVME_RJT_RC_INV_PARAM);
|
|
CU_ASSERT(rjt->rjt.reason_explanation ==
|
|
FCNVME_RJT_EXP_INV_Q_ID);
|
|
}
|
|
} else {
|
|
CU_FAIL("Unexpected response code for create connection");
|
|
}
|
|
} else {
|
|
CU_FAIL("Response not for create connection");
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_disconn_rsp(struct spdk_nvmf_fc_ls_rqst *ls_rqst)
|
|
{
|
|
struct spdk_nvmf_fc_ls_acc_hdr *acc_hdr =
|
|
(struct spdk_nvmf_fc_ls_acc_hdr *) ls_rqst->rspbuf.virt;
|
|
|
|
if (acc_hdr->rqst.w0.ls_cmd == FCNVME_LS_DISCONNECT) {
|
|
if (acc_hdr->w0.ls_cmd == FCNVME_LS_ACC) {
|
|
CU_ASSERT(from_be32(&acc_hdr->desc_list_len) ==
|
|
sizeof(struct spdk_nvmf_fc_ls_disconnect_acc) - 8);
|
|
CU_ASSERT(from_be32(&acc_hdr->rqst.desc_len) ==
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_rqst) - 8);
|
|
CU_ASSERT(from_be32(&acc_hdr->rqst.desc_tag) ==
|
|
FCNVME_LSDESC_RQST);
|
|
return 0;
|
|
} else {
|
|
CU_FAIL("Unexpected reject response for disconnect");
|
|
}
|
|
} else {
|
|
CU_FAIL("Response not for create connection");
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_conn_bad_assoc_rsp(struct spdk_nvmf_fc_ls_rqst *ls_rqst)
|
|
{
|
|
struct spdk_nvmf_fc_ls_acc_hdr *acc_hdr =
|
|
(struct spdk_nvmf_fc_ls_acc_hdr *) ls_rqst->rspbuf.virt;
|
|
|
|
if (acc_hdr->rqst.w0.ls_cmd == FCNVME_LS_CREATE_CONNECTION) {
|
|
if (acc_hdr->w0.ls_cmd == FCNVME_LS_RJT) {
|
|
struct spdk_nvmf_fc_ls_rjt *rjt =
|
|
(struct spdk_nvmf_fc_ls_rjt *)ls_rqst->rspbuf.virt;
|
|
|
|
CU_ASSERT(from_be32(&rjt->desc_list_len) ==
|
|
sizeof(struct spdk_nvmf_fc_ls_rjt) - 8);
|
|
CU_ASSERT(from_be32(&rjt->rqst.desc_tag) ==
|
|
FCNVME_LSDESC_RQST);
|
|
CU_ASSERT(from_be32(&rjt->rjt.desc_len) ==
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_rjt) - 8);
|
|
CU_ASSERT(from_be32(&rjt->rjt.desc_tag) ==
|
|
FCNVME_LSDESC_RJT);
|
|
CU_ASSERT(rjt->rjt.reason_code ==
|
|
FCNVME_RJT_RC_INV_ASSOC);
|
|
CU_ASSERT(rjt->rjt.reason_explanation ==
|
|
FCNVME_RJT_EXP_NONE);
|
|
/* make sure reserved fields are 0 */
|
|
CU_ASSERT(rjt->rjt.rsvd8 == 0);
|
|
CU_ASSERT(rjt->rjt.rsvd12 == 0);
|
|
return 0;
|
|
} else {
|
|
CU_FAIL("Unexpected accept response for create conn. on bad assoc_id");
|
|
}
|
|
} else {
|
|
CU_FAIL("Response not for create connection on bad assoc_id");
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_disconn_bad_assoc_rsp(struct spdk_nvmf_fc_ls_rqst *ls_rqst)
|
|
{
|
|
struct spdk_nvmf_fc_ls_acc_hdr *acc_hdr =
|
|
(struct spdk_nvmf_fc_ls_acc_hdr *) ls_rqst->rspbuf.virt;
|
|
|
|
if (acc_hdr->rqst.w0.ls_cmd == FCNVME_LS_DISCONNECT) {
|
|
if (acc_hdr->w0.ls_cmd == FCNVME_LS_RJT) {
|
|
struct spdk_nvmf_fc_ls_rjt *rjt =
|
|
(struct spdk_nvmf_fc_ls_rjt *)ls_rqst->rspbuf.virt;
|
|
|
|
CU_ASSERT(from_be32(&rjt->desc_list_len) ==
|
|
sizeof(struct spdk_nvmf_fc_ls_rjt) - 8);
|
|
CU_ASSERT(from_be32(&rjt->rqst.desc_tag) ==
|
|
FCNVME_LSDESC_RQST);
|
|
CU_ASSERT(from_be32(&rjt->rjt.desc_len) ==
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_rjt) - 8);
|
|
CU_ASSERT(from_be32(&rjt->rjt.desc_tag) ==
|
|
FCNVME_LSDESC_RJT);
|
|
CU_ASSERT(rjt->rjt.reason_code ==
|
|
FCNVME_RJT_RC_INV_ASSOC);
|
|
CU_ASSERT(rjt->rjt.reason_explanation ==
|
|
FCNVME_RJT_EXP_NONE);
|
|
return 0;
|
|
} else {
|
|
CU_FAIL("Unexpected accept response for disconnect on bad assoc_id");
|
|
}
|
|
} else {
|
|
CU_FAIL("Response not for dsconnect on bad assoc_id");
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
static struct spdk_nvmf_fc_port g_fc_port = {
|
|
.num_io_queues = 16,
|
|
};
|
|
|
|
static struct spdk_nvmf_fc_nport g_tgt_port;
|
|
|
|
#define FC_LS_UT_MAX_IO_QUEUES 16
|
|
struct spdk_nvmf_fc_hwqp g_fc_hwqp[FC_LS_UT_MAX_IO_QUEUES];
|
|
struct spdk_nvmf_fc_poll_group g_fgroup[FC_LS_UT_MAX_IO_QUEUES];
|
|
struct spdk_nvmf_poll_group g_poll_group[FC_LS_UT_MAX_IO_QUEUES];
|
|
static bool threads_allocated = false;
|
|
|
|
static void
|
|
ls_assign_hwqp_threads(void)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < g_fc_port.num_io_queues; i++) {
|
|
struct spdk_nvmf_fc_hwqp *hwqp = &g_fc_port.io_queues[i];
|
|
if (hwqp->thread == NULL) {
|
|
hwqp->thread = spdk_get_thread();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
ls_prepare_threads(void)
|
|
{
|
|
if (threads_allocated == false) {
|
|
allocate_threads(8);
|
|
set_thread(0);
|
|
}
|
|
threads_allocated = true;
|
|
}
|
|
|
|
static void
|
|
setup_polling_threads(void)
|
|
{
|
|
ls_prepare_threads();
|
|
set_thread(0);
|
|
ls_assign_hwqp_threads();
|
|
}
|
|
|
|
static int
|
|
ls_tests_init(void)
|
|
{
|
|
uint16_t i;
|
|
|
|
bzero(&g_nvmf_tgt, sizeof(g_nvmf_tgt));
|
|
|
|
g_nvmf_transport.opts = g_nvmf_transport_opts;
|
|
|
|
snprintf(g_nvmf_subsystem.subnqn, sizeof(g_nvmf_subsystem.subnqn), "%s", fc_ut_subsystem_nqn);
|
|
g_fc_port.hw_port_status = SPDK_FC_PORT_ONLINE;
|
|
g_fc_port.io_queues = g_fc_hwqp;
|
|
for (i = 0; i < g_fc_port.num_io_queues; i++) {
|
|
struct spdk_nvmf_fc_hwqp *hwqp = &g_fc_port.io_queues[i];
|
|
hwqp->lcore_id = i;
|
|
hwqp->hwqp_id = i;
|
|
hwqp->thread = NULL;
|
|
hwqp->fc_port = &g_fc_port;
|
|
hwqp->num_conns = 0;
|
|
TAILQ_INIT(&hwqp->in_use_reqs);
|
|
|
|
bzero(&g_poll_group[i], sizeof(struct spdk_nvmf_poll_group));
|
|
bzero(&g_fgroup[i], sizeof(struct spdk_nvmf_fc_poll_group));
|
|
TAILQ_INIT(&g_poll_group[i].tgroups);
|
|
TAILQ_INIT(&g_poll_group[i].qpairs);
|
|
g_fgroup[i].group.transport = &g_nvmf_transport;
|
|
g_fgroup[i].group.group = &g_poll_group[i];
|
|
hwqp->fgroup = &g_fgroup[i];
|
|
}
|
|
|
|
nvmf_fc_ls_init(&g_fc_port);
|
|
bzero(&g_tgt_port, sizeof(struct spdk_nvmf_fc_nport));
|
|
g_tgt_port.fc_port = &g_fc_port;
|
|
TAILQ_INIT(&g_tgt_port.rem_port_list);
|
|
TAILQ_INIT(&g_tgt_port.fc_associations);
|
|
|
|
bzero(&g_rem_port, sizeof(struct spdk_nvmf_fc_remote_port_info));
|
|
TAILQ_INSERT_TAIL(&g_tgt_port.rem_port_list, &g_rem_port, link);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ls_tests_fini(void)
|
|
{
|
|
nvmf_fc_ls_fini(&g_fc_port);
|
|
free_threads();
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
create_single_assoc_test(void)
|
|
{
|
|
setup_polling_threads();
|
|
/* main test driver */
|
|
g_test_run_type = TEST_RUN_TYPE_CREATE_ASSOC;
|
|
run_create_assoc_test(fc_ut_subsystem_nqn, fc_ut_host, &g_tgt_port);
|
|
|
|
if (g_last_rslt == 0) {
|
|
/* disconnect the association */
|
|
g_test_run_type = TEST_RUN_TYPE_DISCONNECT;
|
|
run_disconn_test(&g_tgt_port, g_curr_assoc_id);
|
|
g_create_conn_test_cnt = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
create_max_conns_test(void)
|
|
{
|
|
uint16_t qid = 1;
|
|
|
|
setup_polling_threads();
|
|
/* main test driver */
|
|
g_test_run_type = TEST_RUN_TYPE_CREATE_ASSOC;
|
|
run_create_assoc_test(fc_ut_subsystem_nqn, fc_ut_host, &g_tgt_port);
|
|
|
|
if (g_last_rslt == 0) {
|
|
g_test_run_type = TEST_RUN_TYPE_CREATE_CONN;
|
|
/* create connections until we get too many connections error */
|
|
while (g_last_rslt == 0) {
|
|
if (g_create_conn_test_cnt > g_nvmf_transport.opts.max_qpairs_per_ctrlr) {
|
|
CU_FAIL("Did not get CIOC failure for too many connections");
|
|
break;
|
|
}
|
|
run_create_conn_test(fc_ut_host, &g_tgt_port, g_curr_assoc_id, qid++);
|
|
}
|
|
|
|
/* disconnect the association */
|
|
g_last_rslt = 0;
|
|
g_test_run_type = TEST_RUN_TYPE_DISCONNECT;
|
|
run_disconn_test(&g_tgt_port, g_curr_assoc_id);
|
|
g_create_conn_test_cnt = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
invalid_connection_test(void)
|
|
{
|
|
setup_polling_threads();
|
|
/* run test to create connection to invalid association */
|
|
g_test_run_type = TEST_RUN_TYPE_CONN_BAD_ASSOC;
|
|
run_create_conn_test(fc_ut_host, &g_tgt_port, g_curr_assoc_id, 1);
|
|
}
|
|
|
|
static void
|
|
xmt_ls_rsp_failure_test(void)
|
|
{
|
|
setup_polling_threads();
|
|
g_test_run_type = TEST_RUN_TYPE_FAIL_LS_RSP;
|
|
run_create_assoc_test(fc_ut_subsystem_nqn, fc_ut_host, &g_tgt_port);
|
|
if (g_last_rslt == 0) {
|
|
/* check target port for associations */
|
|
CU_ASSERT(g_tgt_port.assoc_count == 0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
disconnect_bad_assoc_test(void)
|
|
{
|
|
setup_polling_threads();
|
|
g_test_run_type = TEST_RUN_TYPE_DISCONNECT_BAD_ASSOC;
|
|
run_disconn_test(&g_tgt_port, 0xffff);
|
|
}
|
|
|
|
/*
|
|
* SPDK functions that are called by LS processing
|
|
*/
|
|
|
|
int
|
|
nvmf_fc_xmt_ls_rsp(struct spdk_nvmf_fc_nport *g_tgt_port,
|
|
struct spdk_nvmf_fc_ls_rqst *ls_rqst)
|
|
{
|
|
switch (g_test_run_type) {
|
|
case TEST_RUN_TYPE_CREATE_ASSOC:
|
|
g_last_rslt = handle_ca_rsp(ls_rqst, false);
|
|
break;
|
|
case TEST_RUN_TYPE_CREATE_CONN:
|
|
g_last_rslt = handle_cc_rsp(ls_rqst);
|
|
break;
|
|
case TEST_RUN_TYPE_DISCONNECT:
|
|
g_last_rslt = handle_disconn_rsp(ls_rqst);
|
|
break;
|
|
case TEST_RUN_TYPE_CONN_BAD_ASSOC:
|
|
g_last_rslt = handle_conn_bad_assoc_rsp(ls_rqst);
|
|
break;
|
|
case TEST_RUN_TYPE_FAIL_LS_RSP:
|
|
g_last_rslt = handle_ca_rsp(ls_rqst, false);
|
|
return 1;
|
|
case TEST_RUN_TYPE_DISCONNECT_BAD_ASSOC:
|
|
g_last_rslt = handle_disconn_bad_assoc_rsp(ls_rqst);
|
|
break;
|
|
case TEST_RUN_TYPE_CREATE_MAX_ASSOC:
|
|
g_last_rslt = handle_ca_rsp(ls_rqst, true);
|
|
break;
|
|
|
|
default:
|
|
CU_FAIL("LS Response for Invalid Test Type");
|
|
g_last_rslt = 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
nvmf_fc_xmt_srsr_req(struct spdk_nvmf_fc_hwqp *hwqp,
|
|
struct spdk_nvmf_fc_srsr_bufs *srsr_bufs,
|
|
spdk_nvmf_fc_caller_cb cb, void *cb_args)
|
|
{
|
|
struct spdk_nvmf_fc_ls_disconnect_rqst *dc_rqst =
|
|
(struct spdk_nvmf_fc_ls_disconnect_rqst *)
|
|
srsr_bufs->rqst;
|
|
|
|
CU_ASSERT(dc_rqst->w0.ls_cmd == FCNVME_LS_DISCONNECT);
|
|
CU_ASSERT(from_be32(&dc_rqst->desc_list_len) ==
|
|
sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst) -
|
|
(2 * sizeof(uint32_t)));
|
|
CU_ASSERT(from_be32(&dc_rqst->assoc_id.desc_tag) ==
|
|
FCNVME_LSDESC_ASSOC_ID);
|
|
CU_ASSERT(from_be32(&dc_rqst->assoc_id.desc_len) ==
|
|
sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id) -
|
|
(2 * sizeof(uint32_t)));
|
|
|
|
g_spdk_nvmf_fc_xmt_srsr_req = true;
|
|
|
|
if (cb) {
|
|
cb(hwqp, 0, cb_args);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_STUB_V(nvmf_fc_request_abort, (struct spdk_nvmf_fc_request *fc_req,
|
|
bool send_abts, spdk_nvmf_fc_caller_cb cb, void *cb_args));
|
|
DEFINE_STUB_V(spdk_bdev_io_abort, (struct spdk_bdev_io *bdev_io, void *ctx));
|
|
DEFINE_STUB_V(nvmf_fc_request_abort_complete, (void *arg1));
|
|
|
|
static void
|
|
usage(const char *program_name)
|
|
{
|
|
printf("%s [options]\n", program_name);
|
|
printf("options:\n");
|
|
spdk_log_usage(stdout, "-t");
|
|
printf(" -i value - Number of IO Queues (default: %u)\n",
|
|
g_fc_port.num_io_queues);
|
|
printf(" -q value - SQ size (default: %u)\n",
|
|
g_nvmf_transport_opts.max_queue_depth);
|
|
printf(" -c value - Connection count (default: %u)\n",
|
|
g_nvmf_transport_opts.max_qpairs_per_ctrlr);
|
|
printf(" -u test# - Unit test# to run\n");
|
|
printf(" 0 : Run all tests (default)\n");
|
|
printf(" 1 : CASS/DISC create single assoc test\n");
|
|
printf(" 2 : Max. conns. test\n");
|
|
printf(" 3 : CIOC to invalid assoc_id connection test\n");
|
|
printf(" 4 : Create/delete max assoc conns test\n");
|
|
printf(" 5 : LS response failure test\n");
|
|
printf(" 6 : Disconnect bad assoc_id test\n");
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
unsigned int num_failures = 0;
|
|
CU_pSuite suite = NULL;
|
|
int test = 0;
|
|
long int val;
|
|
int op;
|
|
|
|
while ((op = getopt(argc, argv, "a:q:c:t:u:d:i:")) != -1) {
|
|
switch (op) {
|
|
case 'q':
|
|
val = spdk_strtol(optarg, 10);
|
|
if (val < 16) {
|
|
fprintf(stderr, "SQ size must be at least 16\n");
|
|
return -EINVAL;
|
|
}
|
|
g_nvmf_transport_opts.max_queue_depth = (uint16_t)val;
|
|
break;
|
|
case 'c':
|
|
val = spdk_strtol(optarg, 10);
|
|
if (val < 2) {
|
|
fprintf(stderr, "Connection count must be at least 2\n");
|
|
return -EINVAL;
|
|
}
|
|
g_nvmf_transport_opts.max_qpairs_per_ctrlr = (uint16_t)val;
|
|
break;
|
|
case 't':
|
|
if (spdk_log_set_flag(optarg) < 0) {
|
|
fprintf(stderr, "Unknown trace flag '%s'\n", optarg);
|
|
usage(argv[0]);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case 'u':
|
|
test = (int)spdk_strtol(optarg, 10);
|
|
break;
|
|
case 'i':
|
|
val = spdk_strtol(optarg, 10);
|
|
if (val < 2) {
|
|
fprintf(stderr, "Number of io queues must be at least 2\n");
|
|
return -EINVAL;
|
|
}
|
|
if (val > FC_LS_UT_MAX_IO_QUEUES) {
|
|
fprintf(stderr, "Number of io queues can't be greater than %d\n",
|
|
FC_LS_UT_MAX_IO_QUEUES);
|
|
return -EINVAL;
|
|
}
|
|
g_fc_port.num_io_queues = (uint32_t)val;
|
|
break;
|
|
|
|
|
|
default:
|
|
usage(argv[0]);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
CU_set_error_action(CUEA_ABORT);
|
|
CU_initialize_registry();
|
|
|
|
suite = CU_add_suite("FC-NVMe LS", ls_tests_init, ls_tests_fini);
|
|
|
|
if (test == 0) {
|
|
|
|
CU_ADD_TEST(suite, create_single_assoc_test);
|
|
|
|
CU_ADD_TEST(suite, create_max_conns_test);
|
|
CU_ADD_TEST(suite, invalid_connection_test);
|
|
CU_ADD_TEST(suite, disconnect_bad_assoc_test);
|
|
|
|
CU_ADD_TEST(suite, xmt_ls_rsp_failure_test);
|
|
|
|
} else {
|
|
|
|
switch (test) {
|
|
case 1:
|
|
CU_ADD_TEST(suite, create_single_assoc_test);
|
|
break;
|
|
case 2:
|
|
CU_ADD_TEST(suite, create_max_conns_test);
|
|
break;
|
|
case 3:
|
|
CU_ADD_TEST(suite, invalid_connection_test);
|
|
break;
|
|
case 5:
|
|
CU_ADD_TEST(suite, xmt_ls_rsp_failure_test);
|
|
break;
|
|
case 6:
|
|
CU_ADD_TEST(suite, disconnect_bad_assoc_test);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "Invalid test number\n");
|
|
usage(argv[0]);
|
|
CU_cleanup_registry();
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
CU_basic_set_mode(CU_BRM_VERBOSE);
|
|
CU_basic_run_tests();
|
|
num_failures = CU_get_number_of_failures();
|
|
CU_cleanup_registry();
|
|
|
|
return num_failures;
|
|
}
|