Page MenuHomeFreeBSD

D61556.1794768269.diff
No OneTemporary

Authored By
Unknown
Size
14 KB
Referenced Files
None
Subscribers
None

D61556.1794768269.diff

diff --git a/tests/sys/cam/ctl/nvmft/README b/tests/sys/cam/ctl/nvmft/README
--- a/tests/sys/cam/ctl/nvmft/README
+++ b/tests/sys/cam/ctl/nvmft/README
@@ -15,6 +15,7 @@
test_suites.FreeBSD.nvmft.address = "127.0.0.1"
test_suites.FreeBSD.nvmft.port = "4420"
test_suites.FreeBSD.nvmft.nqn = "nqn.1994-09.org.freebsd:test"
+test_suites.FreeBSD.nvmft.nsid = "1"
In addition, the following optional configuration variables may also
be set (Note that the variable names all require a prefix of
diff --git a/tests/sys/cam/ctl/nvmft/nvmft.c b/tests/sys/cam/ctl/nvmft/nvmft.c
--- a/tests/sys/cam/ctl/nvmft/nvmft.c
+++ b/tests/sys/cam/ctl/nvmft/nvmft.c
@@ -1053,6 +1053,357 @@
identify_bad_size(tc, 4097);
}
+static void
+nvmf_init_read_sqe(void *sqe, uint32_t nsid, uint64_t lba, u_int nlb)
+{
+ struct nvme_command *cmd = sqe;
+
+ nvmf_init_sqe(cmd, NVME_OPC_READ);
+ cmd->nsid = htole32(nsid);
+ cmd->cdw10 = htole32(lba);
+ cmd->cdw11 = htole32(lba >> 32);
+ cmd->cdw12 = htole16(nlb - 1);
+}
+
+static struct nvmf_capsule *
+create_read_command(struct nvmf_qpair *io, uint32_t nsid, uint64_t lba,
+ u_int nlb, void *buf, size_t len)
+{
+ struct nvme_command cmd;
+ struct nvmf_capsule *cc;
+ int error;
+
+ nvmf_init_read_sqe(&cmd, nsid, lba, nlb);
+ cc = nvmf_allocate_command(io, &cmd);
+ if (cc == NULL)
+ atf_tc_fail("Failed to allocate command capsule: %s",
+ strerror(errno));
+
+ error = nvmf_capsule_append_data(cc, buf, len, false);
+ if (error != 0)
+ atf_tc_fail("Failed to append data to READ capsule: %s",
+ strerror(error));
+
+ return (cc);
+}
+
+ATF_TC(read_block);
+ATF_TC_HEAD(read_block, tc)
+{
+ require_nvmft(tc);
+}
+ATF_TC_BODY(read_block, tc)
+{
+ const struct nvme_completion *cpl;
+ struct nvmf_qpair_params qparams;
+ struct nvmf_association *na;
+ struct nvmf_qpair *admin, *io;
+ struct nvmf_capsule *cc, *rc;
+ char hostnqn[NVMF_NQN_MAX_LEN];
+ uint8_t hostid[16];
+ struct addrinfo *ai, *list;
+ char *buf;
+ size_t len;
+ u_int block_size, nsid;
+ int error;
+ uint16_t status;
+
+ init_hostid(hostid, hostnqn);
+
+ na = create_association(tc);
+
+ memset(&qparams, 0, sizeof(qparams));
+ qparams.admin = true;
+ qparams.tcp.fd = open_socket(tc, &ai, &list);
+
+ admin = connect_admin_queue(tc, na, &qparams, hostid, hostnqn, 0);
+ if (admin == NULL)
+ atf_tc_fail("Failed to create admin queue: %s",
+ nvmf_association_error(na));
+
+ require_nvm(admin);
+
+ memset(&qparams, 0, sizeof(qparams));
+ qparams.admin = false;
+ qparams.tcp.fd = open_socket_ai(ai);
+ freeaddrinfo(list);
+
+ io = connect_io_queue(tc, admin, na, &qparams, hostid, hostnqn);
+ nvmf_free_association(na);
+
+ nsid = validate_namespace(tc, admin, &block_size);
+
+ len = block_size;
+ buf = malloc(len);
+ cc = create_read_command(io, nsid, 0, 1, buf, len);
+
+ error = nvmf_host_transmit_command(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to transmit READ capsule: %s",
+ strerror(errno));
+
+ error = nvmf_host_wait_for_response(cc, &rc);
+ nvmf_free_capsule(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to receive READ response: %s",
+ strerror(error));
+
+ cpl = nvmf_capsule_cqe(rc);
+ status = le16toh(cpl->status);
+ ATF_REQUIRE(status == 0);
+ nvmf_free_capsule(rc);
+
+ shutdown_controller(admin);
+ nvmf_free_qpair(io);
+ nvmf_free_qpair(admin);
+}
+
+static void
+nvmf_init_compare_sqe(void *sqe, uint32_t nsid, uint64_t lba, u_int nlb)
+{
+ struct nvme_command *cmd = sqe;
+
+ nvmf_init_sqe(cmd, NVME_OPC_COMPARE);
+ cmd->nsid = htole32(nsid);
+ cmd->cdw10 = htole32(lba);
+ cmd->cdw11 = htole32(lba >> 32);
+ cmd->cdw12 = htole16(nlb - 1);
+}
+
+static struct nvmf_capsule *
+create_compare_command(struct nvmf_qpair *io, uint32_t nsid, uint64_t lba,
+ u_int nlb, void *buf, size_t len)
+{
+ struct nvme_command cmd;
+ struct nvmf_capsule *cc;
+ int error;
+
+ nvmf_init_compare_sqe(&cmd, nsid, lba, nlb);
+ cc = nvmf_allocate_command(io, &cmd);
+ if (cc == NULL)
+ atf_tc_fail("Failed to allocate command capsule: %s",
+ strerror(errno));
+
+ error = nvmf_capsule_append_data(cc, buf, len, true);
+ if (error != 0)
+ atf_tc_fail("Failed to append data to COMPARE capsule: %s",
+ strerror(error));
+
+ return (cc);
+}
+
+ATF_TC(compare_block);
+ATF_TC_HEAD(compare_block, tc)
+{
+ require_nvmft(tc);
+}
+ATF_TC_BODY(compare_block, tc)
+{
+ const struct nvme_completion *cpl;
+ struct nvmf_qpair_params qparams;
+ struct nvmf_association *na;
+ struct nvmf_qpair *admin, *io;
+ struct nvmf_capsule *cc, *rc;
+ char hostnqn[NVMF_NQN_MAX_LEN];
+ uint8_t hostid[16];
+ struct addrinfo *ai, *list;
+ char *buf;
+ size_t len;
+ u_int block_size, nsid;
+ int error;
+ uint16_t status;
+
+ init_hostid(hostid, hostnqn);
+
+ na = create_association(tc);
+
+ memset(&qparams, 0, sizeof(qparams));
+ qparams.admin = true;
+ qparams.tcp.fd = open_socket(tc, &ai, &list);
+
+ admin = connect_admin_queue(tc, na, &qparams, hostid, hostnqn, 0);
+ if (admin == NULL)
+ atf_tc_fail("Failed to create admin queue: %s",
+ nvmf_association_error(na));
+
+ require_compare(admin);
+
+ memset(&qparams, 0, sizeof(qparams));
+ qparams.admin = false;
+ qparams.tcp.fd = open_socket_ai(ai);
+ freeaddrinfo(list);
+
+ io = connect_io_queue(tc, admin, na, &qparams, hostid, hostnqn);
+ nvmf_free_association(na);
+
+ nsid = validate_namespace(tc, admin, &block_size);
+
+ /* First, read the first block. */
+ len = block_size;
+ buf = malloc(len);
+ cc = create_read_command(io, nsid, 0, 1, buf, len);
+
+ error = nvmf_host_transmit_command(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to transmit READ capsule: %s",
+ strerror(errno));
+
+ error = nvmf_host_wait_for_response(cc, &rc);
+ nvmf_free_capsule(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to receive READ response: %s",
+ strerror(error));
+
+ cpl = nvmf_capsule_cqe(rc);
+ status = le16toh(cpl->status);
+ ATF_REQUIRE(status == 0);
+ nvmf_free_capsule(rc);
+
+ /* Second, compare that block with the value just read. */
+ cc = create_compare_command(io, nsid, 0, 1, buf, len);
+
+ error = nvmf_host_transmit_command(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to transmit COMPARE capsule: %s",
+ strerror(errno));
+
+ error = nvmf_host_wait_for_response(cc, &rc);
+ nvmf_free_capsule(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to receive COMPARE response: %s",
+ strerror(error));
+
+ cpl = nvmf_capsule_cqe(rc);
+ status = le16toh(cpl->status);
+ ATF_REQUIRE(status == 0);
+ nvmf_free_capsule(rc);
+
+ shutdown_controller(admin);
+ nvmf_free_qpair(io);
+ nvmf_free_qpair(admin);
+}
+
+ATF_TC(compare_mismatch);
+ATF_TC_HEAD(compare_mismatch, tc)
+{
+ require_nvmft(tc);
+}
+ATF_TC_BODY(compare_mismatch, tc)
+{
+ const struct nvme_completion *cpl;
+ struct nvmf_qpair_params qparams;
+ struct nvmf_association *na;
+ struct nvmf_qpair *admin, *io;
+ struct nvmf_capsule *cc, *rc;
+ char hostnqn[NVMF_NQN_MAX_LEN];
+ uint8_t hostid[16];
+ struct addrinfo *ai, *list;
+ char *buf;
+ size_t len;
+ u_int block_size, nsid;
+ int error;
+ uint16_t status;
+
+ init_hostid(hostid, hostnqn);
+
+ na = create_association(tc);
+
+ memset(&qparams, 0, sizeof(qparams));
+ qparams.admin = true;
+ qparams.tcp.fd = open_socket(tc, &ai, &list);
+
+ admin = connect_admin_queue(tc, na, &qparams, hostid, hostnqn, 0);
+ if (admin == NULL)
+ atf_tc_fail("Failed to create admin queue: %s",
+ nvmf_association_error(na));
+
+ require_compare(admin);
+
+ memset(&qparams, 0, sizeof(qparams));
+ qparams.admin = false;
+ qparams.tcp.fd = open_socket_ai(ai);
+ freeaddrinfo(list);
+
+ io = connect_io_queue(tc, admin, na, &qparams, hostid, hostnqn);
+ nvmf_free_association(na);
+
+ nsid = validate_namespace(tc, admin, &block_size);
+
+ /* First, read the first block. */
+ len = block_size;
+ buf = malloc(len);
+ cc = create_read_command(io, nsid, 0, 1, buf, len);
+
+ error = nvmf_host_transmit_command(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to transmit READ capsule: %s",
+ strerror(errno));
+
+ error = nvmf_host_wait_for_response(cc, &rc);
+ nvmf_free_capsule(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to receive READ response: %s",
+ strerror(error));
+
+ cpl = nvmf_capsule_cqe(rc);
+ status = le16toh(cpl->status);
+ ATF_REQUIRE(status == 0);
+ nvmf_free_capsule(rc);
+
+ /* Second, flip some bits in the first byte of the block. */
+ buf[0] ^= 0xaa;
+
+ /* Third, compare that block with the altered values. */
+ cc = create_compare_command(io, nsid, 0, 1, buf, len);
+
+ error = nvmf_host_transmit_command(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to transmit COMPARE capsule: %s",
+ strerror(errno));
+
+ error = nvmf_host_wait_for_response(cc, &rc);
+ nvmf_free_capsule(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to receive COMPARE response: %s",
+ strerror(error));
+
+ cpl = nvmf_capsule_cqe(rc);
+ status = le16toh(cpl->status);
+ ATF_REQUIRE(status != 0);
+ ATF_REQUIRE(NVME_STATUS_GET_SCT(status) == NVME_SCT_MEDIA_ERROR);
+ ATF_REQUIRE(NVME_STATUS_GET_SC(status) == NVME_SC_COMPARE_FAILURE);
+ nvmf_free_capsule(rc);
+
+ /* Next, restore the first byte and flip some bits in the last byte. */
+ buf[0] ^= 0xaa;
+ buf[len - 1] ^= 0x55;
+
+ /* Last, one more compare. */
+ cc = create_compare_command(io, nsid, 0, 1, buf, len);
+
+ error = nvmf_host_transmit_command(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to transmit COMPARE capsule: %s",
+ strerror(errno));
+
+ error = nvmf_host_wait_for_response(cc, &rc);
+ nvmf_free_capsule(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to receive COMPARE response: %s",
+ strerror(error));
+
+ cpl = nvmf_capsule_cqe(rc);
+ status = le16toh(cpl->status);
+ ATF_REQUIRE(status != 0);
+ ATF_REQUIRE(NVME_STATUS_GET_SCT(status) == NVME_SCT_MEDIA_ERROR);
+ ATF_REQUIRE(NVME_STATUS_GET_SC(status) == NVME_SC_COMPARE_FAILURE);
+ nvmf_free_capsule(rc);
+
+ shutdown_controller(admin);
+ nvmf_free_qpair(io);
+ nvmf_free_qpair(admin);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, invalid_qid);
@@ -1078,6 +1429,9 @@
ATF_TP_ADD_TC(tp, fetch_reserved_log_page);
ATF_TP_ADD_TC(tp, identify_short);
ATF_TP_ADD_TC(tp, identify_long);
+ ATF_TP_ADD_TC(tp, read_block);
+ ATF_TP_ADD_TC(tp, compare_block);
+ ATF_TP_ADD_TC(tp, compare_mismatch);
return (atf_no_error());
}
diff --git a/tests/sys/cam/ctl/nvmft/nvmft_subr.h b/tests/sys/cam/ctl/nvmft/nvmft_subr.h
--- a/tests/sys/cam/ctl/nvmft/nvmft_subr.h
+++ b/tests/sys/cam/ctl/nvmft/nvmft_subr.h
@@ -11,13 +11,21 @@
struct nvmf_qpair *connect_admin_queue(const atf_tc_t *tc,
struct nvmf_association *na, const struct nvmf_qpair_params *params,
const uint8_t hostid[16], const char *hostnqn, uint32_t kato);
+struct nvmf_qpair *connect_io_queue(const atf_tc_t *tc,
+ struct nvmf_qpair *admin, struct nvmf_association *na,
+ const struct nvmf_qpair_params *params, const uint8_t hostid[16],
+ const char *hostnqn);
struct nvmf_association *create_association(const atf_tc_t *tc);
void init_hostid(uint8_t hostid[16], char hostnqn[NVMF_NQN_MAX_LEN]);
int open_socket(const atf_tc_t *tc, struct addrinfo **aip,
struct addrinfo **listp);
int open_socket_ai(struct addrinfo *ai);
uint16_t parse_cntlid(const atf_tc_t *tc);
+void require_compare(struct nvmf_qpair *qp);
+void require_nvm(struct nvmf_qpair *qp);
void require_nvmft(atf_tc_t *tc);
void shutdown_controller(struct nvmf_qpair *qp);
+u_int validate_namespace(const atf_tc_t *tc, struct nvmf_qpair *qp,
+ u_int *block_size);
#endif /* !__NVMFT_SUBR_H__ */
diff --git a/tests/sys/cam/ctl/nvmft/nvmft_subr.c b/tests/sys/cam/ctl/nvmft/nvmft_subr.c
--- a/tests/sys/cam/ctl/nvmft/nvmft_subr.c
+++ b/tests/sys/cam/ctl/nvmft/nvmft_subr.c
@@ -239,3 +239,104 @@
atf_tc_fail("Failed to set CC to trigger shutdown: %s",
strerror(error));
}
+
+void
+require_nvm(struct nvmf_qpair *qp)
+{
+ uint64_t cap;
+ int error;
+
+ error = nvmf_read_property(qp, NVMF_PROP_CAP, 8, &cap);
+ if (error != 0)
+ atf_tc_fail("Failed to fetch CAP: %s", strerror(error));
+
+ if (NVME_CAP_HI_CSS_NVM(cap >> 32) == 0)
+ atf_tc_skip("Controller does not support the NVM command set");
+}
+
+void
+require_compare(struct nvmf_qpair *qp)
+{
+ struct nvme_controller_data cdata;
+ int error;
+
+ require_nvm(qp);
+
+ error = nvmf_host_identify_controller(qp, &cdata);
+ if (error != 0)
+ atf_tc_fail("Failed to fetch controller data: %s",
+ strerror(error));
+
+ if (NVMEV(NVME_CTRLR_DATA_ONCS_COMPARE, cdata.oncs) == 0)
+ atf_tc_skip("Controller does not support the COMPARE command");
+}
+
+struct nvmf_qpair *
+connect_io_queue(const atf_tc_t *tc, struct nvmf_qpair *admin,
+ struct nvmf_association *na, const struct nvmf_qpair_params *params,
+ const uint8_t hostid[16], const char *hostnqn)
+{
+ struct nvme_controller_data cdata;
+ struct nvmf_qpair *io;
+ int error, queues;
+
+ error = nvmf_host_identify_controller(admin, &cdata);
+ if (error != 0)
+ atf_tc_fail("Failed to fetch controller data: %s",
+ strerror(error));
+ nvmf_update_assocation(na, &cdata);
+
+ error = nvmf_host_request_queues(admin, 1, &queues);
+ if (error != 0)
+ atf_tc_fail("Failed to request I/O queues: %s",
+ strerror(error));
+
+ io = nvmf_connect(na, params, 1, NVME_MIN_IO_ENTRIES, hostid,
+ nvmf_cntlid(admin), atf_tc_get_config_var(tc, "nvmft.nqn"),
+ hostnqn, 0);
+ if (io == NULL)
+ atf_tc_fail("Failed to create I/O queue: %s",
+ nvmf_association_error(na));
+ return (io);
+}
+
+u_int
+validate_namespace(const atf_tc_t *tc, struct nvmf_qpair *qp, u_int *block_size)
+{
+ struct nvme_namespace_data nsdata;
+ long val;
+ uint32_t nsid;
+ int error;
+ uint8_t lbads, lbaf;
+
+ if (!atf_tc_has_config_var(tc, "nvmft.nsid"))
+ atf_tc_skip("requires nvmft.nsid config variable");
+
+ val = atf_tc_get_config_var_as_long(tc, "nvmft.nsid");
+ nsid = val;
+ if (val < 1 || nsid >= NVME_GLOBAL_NAMESPACE_TAG)
+ atf_tc_fail("Invalid nsid: %ld", val);
+
+ error = nvmf_host_identify_namespace(qp, nsid, &nsdata);
+ if (error != 0)
+ atf_tc_fail("IDENTIFY failed for nsid %u", nsid);
+
+ nvme_namespace_data_swapbytes(&nsdata);
+
+ if (NVMEV(NVME_NS_DATA_DPS_PIT, nsdata.dps) != 0)
+ atf_tc_fail("Namespace %u uses data protection", nsid);
+
+ lbaf = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsdata.flbas);
+ if (lbaf > nsdata.nlbaf)
+ atf_tc_fail("Invalid LBA format index for namespace %u", nsid);
+
+ if (NVMEV(NVME_NS_DATA_LBAF_MS, nsdata.lbaf[lbaf]) != 0)
+ atf_tc_fail("Namespace %u uses metadata", nsid);
+
+ lbads = NVMEV(NVME_NS_DATA_LBAF_LBADS, nsdata.lbaf[lbaf]);
+ if (lbads == 0)
+ atf_tc_fail("Namespace %u block size is zero", nsid);
+
+ *block_size = 1 << lbads;
+ return (nsid);
+}

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 15, 6:44 PM (22 h, 36 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41746882
Default Alt Text
D61556.1794768269.diff (14 KB)

Event Timeline