#include <err.h>
#include <string.h>
#include "libnvme_test_common.h"
static bool
info_roundtrip_pci(nvme_ctrl_info_t *info, nvme_ctrl_info_t *rest_info)
{
bool ret = true;
uint32_t id32, rest_id32;
uint16_t id16, rest_id16;
uint8_t id8, rest_id8;
if (!nvme_ctrl_info_pci_vid(info, &id16)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI vendor "
"from original snapshot");
ret = false;
} else if (!nvme_ctrl_info_pci_vid(rest_info, &rest_id16)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI vendor "
"from restored snapshot");
ret = false;
} else if (id16 != rest_id16) {
warnx("TEST FAILED: PCI vendor mismatch: was %u now %u",
id16, rest_id16);
ret = false;
} else {
(void) printf("TEST PASSED: PCI vendor successfully "
"restored\n");
}
if (!nvme_ctrl_info_pci_did(info, &id16)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI device "
"from original snapshot");
ret = false;
} else if (!nvme_ctrl_info_pci_did(rest_info, &rest_id16)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI device "
"from restored snapshot");
ret = false;
} else if (id16 != rest_id16) {
warnx("TEST FAILED: PCI device mismatch: was %u now %u",
id16, rest_id16);
ret = false;
} else {
(void) printf("TEST PASSED: PCI device successfully "
"restored\n");
}
if (!nvme_ctrl_info_pci_subvid(info, &id16)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI subsystem "
"vendor from original snapshot");
ret = false;
} else if (!nvme_ctrl_info_pci_subvid(rest_info, &rest_id16)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI subsystem "
"vendor from restored snapshot");
ret = false;
} else if (id16 != rest_id16) {
warnx("TEST FAILED: PCI subsystem vendor mismatch: was %u "
"now %u", id16, rest_id16);
ret = false;
} else {
(void) printf("TEST PASSED: PCI subsystem vendor successfully "
"restored\n");
}
if (!nvme_ctrl_info_pci_subsys(info, &id16)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI subsystem "
"id from original snapshot");
ret = false;
} else if (!nvme_ctrl_info_pci_subsys(rest_info, &rest_id16)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI subsystem "
"id from restored snapshot");
ret = false;
} else if (id16 != rest_id16) {
warnx("TEST FAILED: PCI subsystem id mismatch: was %u "
"now %u", id16, rest_id16);
ret = false;
} else {
(void) printf("TEST PASSED: PCI subsystem id successfully "
"restored\n");
}
if (!nvme_ctrl_info_pci_rev(info, &id8)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI revision "
"from original snapshot");
ret = false;
} else if (!nvme_ctrl_info_pci_rev(rest_info, &rest_id8)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI revision "
"from restored snapshot");
ret = false;
} else if (id8 != rest_id8) {
warnx("TEST FAILED: PCI revision mismatch: was %u now %u",
id8, rest_id8);
ret = false;
} else {
(void) printf("TEST PASSED: PCI revision successfully "
"restored\n");
}
if (!nvme_ctrl_info_pci_mps_min(info, &id32)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI MPS min "
"from original snapshot");
ret = false;
} else if (!nvme_ctrl_info_pci_mps_min(rest_info, &rest_id32)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI MPS min "
"from restored snapshot");
ret = false;
} else if (id32 != rest_id32) {
warnx("TEST FAILED: PCI MPS min mismatch: was %u now %u",
id32, rest_id32);
ret = false;
} else {
(void) printf("TEST PASSED: PCI MPS min successfully "
"restored\n");
}
if (!nvme_ctrl_info_pci_mps_max(info, &id32)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI MPS max "
"from original snapshot");
ret = false;
} else if (!nvme_ctrl_info_pci_mps_max(rest_info, &rest_id32)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI MPS max "
"from restored snapshot");
ret = false;
} else if (id32 != rest_id32) {
warnx("TEST FAILED: PCI MPS max mismatch: was %u now %u",
id32, rest_id32);
ret = false;
} else {
(void) printf("TEST PASSED: PCI MPS max successfully "
"restored\n");
}
if (!nvme_ctrl_info_pci_nintrs(info, &id32)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI intr "
"count from original snapshot");
ret = false;
} else if (!nvme_ctrl_info_pci_nintrs(rest_info, &rest_id32)) {
libnvme_test_ctrl_info_warn(info, "failed to get PCI intr "
"count from restored snapshot");
ret = false;
} else if (id32 != rest_id32) {
warnx("TEST FAILED: PCI intr count mismatch: was %u now %u",
id32, rest_id32);
ret = false;
} else {
(void) printf("TEST PASSED: PCI intr count successfully "
"restored\n");
}
return (ret);
}
static bool
info_roundtrip_ns(nvme_ctrl_info_t *info, nvme_ctrl_info_t *rest_info)
{
bool ret = true;
nvme_uint128_t u128, rest_u128;
const nvme_identify_nsid_t *idns, *rest_idns;
if (!nvme_ctrl_info_cap(info, &u128)) {
libnvme_test_ctrl_info_warn(info, "failed to get NVM capacity "
"from original snapshot");
ret = false;
} else if (!nvme_ctrl_info_cap(rest_info, &rest_u128)) {
libnvme_test_ctrl_info_warn(info, "failed to get NVM capacity "
"from restored snapshot");
ret = false;
} else if (memcmp(&u128, &rest_u128, sizeof (nvme_uint128_t)) != 0) {
warnx("TEST FAILED: NVM capacity mismatch");
ret = false;
} else {
(void) printf("TEST PASSED: NVM capacity successfully "
"restored\n");
}
if (!nvme_ctrl_info_unalloc_cap(info, &u128)) {
libnvme_test_ctrl_info_warn(info, "failed to get NVM "
"unallocated capacity from original snapshot");
ret = false;
} else if (!nvme_ctrl_info_unalloc_cap(rest_info, &rest_u128)) {
libnvme_test_ctrl_info_warn(info, "failed to get NVM "
"unallocated capacity from restored snapshot");
ret = false;
} else if (memcmp(&u128, &rest_u128, sizeof (nvme_uint128_t)) != 0) {
warnx("TEST FAILED: NVM unallocated capacity mismatch");
ret = false;
} else {
(void) printf("TEST PASSED: NVM unallocated capacity "
"successfully restored\n");
}
if (!nvme_ctrl_info_common_ns(info, &idns)) {
libnvme_test_ctrl_info_warn(info, "failed to get common ns "
"from original snapshot");
ret = false;
} else if (!nvme_ctrl_info_common_ns(rest_info, &rest_idns)) {
libnvme_test_ctrl_info_warn(info, "failed to get common ns "
"from restored snapshot");
ret = false;
} else if (memcmp(idns, rest_idns,
sizeof (nvme_identify_nsid_t)) != 0) {
warnx("TEST FAILED: Common Identify Namespace mismatch");
ret = false;
} else {
(void) printf("TEST PASSED: common identify namespace "
"successfully restored\n");
}
return (ret);
}
static bool
info_roundtrip_lba(nvme_ctrl_info_t *info, nvme_ctrl_info_t *rest_info)
{
bool ret = true;
const uint32_t nlbas = nvme_ctrl_info_nformats(info);
for (uint32_t i = 0; i < nlbas; i++) {
const nvme_nvm_lba_fmt_t *fmt, *rest_fmt;
if (!nvme_ctrl_info_format(info, i, &fmt)) {
if (nvme_ctrl_info_err(info) == NVME_INFO_ERR_BAD_FMT) {
continue;
}
libnvme_test_ctrl_info_warn(info, "failed to get "
"LBA format %u from original snapshot", i);
ret = false;
continue;
}
if (!nvme_ctrl_info_format(rest_info, i, &rest_fmt)) {
libnvme_test_ctrl_info_warn(info, "failed to get "
"LBA format %u from restored snapshot", i);
ret = false;
continue;
}
(void) printf("TEST PASSED: successfully got LBA format %u\n",
i);
if (nvme_nvm_lba_fmt_id(fmt) != i) {
warnx("TEST FAILED: format %u from original snapshot "
"has wrong format id: %u\n", i,
nvme_nvm_lba_fmt_id(fmt));
ret = false;
}
if (nvme_nvm_lba_fmt_id(rest_fmt) != i) {
warnx("TEST FAILED: format %u from restored snapshot "
"has wrong format id: %u\n", i,
nvme_nvm_lba_fmt_id(rest_fmt));
ret = false;
}
if (nvme_nvm_lba_fmt_meta_size(fmt) !=
nvme_nvm_lba_fmt_meta_size(rest_fmt)) {
warnx("TEST FAILED: LBA %u metadata size mismatch: "
"was %u, now %u", i,
nvme_nvm_lba_fmt_meta_size(fmt),
nvme_nvm_lba_fmt_meta_size(rest_fmt));
ret = false;
} else {
(void) printf("TEST PASSED: LBA %u metadata "
"successfully restored\n", i);
}
if (nvme_nvm_lba_fmt_data_size(fmt) !=
nvme_nvm_lba_fmt_data_size(rest_fmt)) {
warnx("TEST FAILED: LBA %u data size mismatch: "
"was %" PRIu64 ", now %" PRIu64, i,
nvme_nvm_lba_fmt_data_size(fmt),
nvme_nvm_lba_fmt_data_size(rest_fmt));
ret = false;
} else {
(void) printf("TEST PASSED: LBA %u data size "
"successfully restored\n", i);
}
if (nvme_nvm_lba_fmt_rel_perf(fmt) !=
nvme_nvm_lba_fmt_rel_perf(rest_fmt)) {
warnx("TEST FAILED: LBA %u relative perf mismatch: "
"was %u, now %u", i,
nvme_nvm_lba_fmt_rel_perf(fmt),
nvme_nvm_lba_fmt_rel_perf(rest_fmt));
ret = false;
} else {
(void) printf("TEST PASSED: LBA %u relative perf "
"successfully restored\n", i);
}
}
return (ret);
}
int
main(void)
{
int ret = EXIT_SUCCESS;
nvme_t *nvme;
nvme_ctrl_t *ctrl;
nvme_ctrl_info_t *info, *rest_info;
nvlist_t *nvl;
const nvme_identify_ctrl_t *ctrlid, *rest_ctrlid;
const nvme_version_t *vers, *rest_vers;
libnvme_test_init(&nvme, &ctrl);
if (!nvme_ctrl_info_snap(ctrl, &info)) {
libnvme_test_ctrl_fatal(ctrl, "failed to take a snapshot");
}
if (!nvme_ctrl_info_persist(info, &nvl)) {
libnvme_test_ctrl_info_fatal(info, "failed to persist the "
"controller snapshot");
}
if (!nvme_ctrl_info_restore(nvme, nvl, &rest_info)) {
libnvme_test_hdl_fatal(nvme, "failed to restore controller "
"snapshot");
}
if (nvme_ctrl_info_vendor(info) != nvme_ctrl_info_vendor(rest_info)) {
warnx("TEST FAILED: vendor mismatch: orig 0x%x, restored: 0x%x",
nvme_ctrl_info_vendor(info),
nvme_ctrl_info_vendor(rest_info));
ret = EXIT_FAILURE;
} else {
(void) printf("TEST PASSED: successfully matched vendor id\n");
}
ctrlid = nvme_ctrl_info_identify(info);
rest_ctrlid = nvme_ctrl_info_identify(rest_info);
if (memcmp(ctrlid, rest_ctrlid, sizeof (nvme_identify_ctrl_t)) != 0) {
warnx("TEST FAILED: Identify info mismatched after restore");
ret = EXIT_FAILURE;
} else {
(void) printf("TEST PASSED: identify controller successfully "
"restored\n");
}
vers = nvme_ctrl_info_version(info);
rest_vers = nvme_ctrl_info_version(rest_info);
if (vers->v_major != rest_vers->v_major) {
warnx("TEST FAILED: mismatched major version: was %u, found %u",
vers->v_major, rest_vers->v_major);
ret = EXIT_FAILURE;
} else {
(void) printf("TEST PASSED: major version successfully "
"restored\n");
}
if (vers->v_minor != rest_vers->v_minor) {
warnx("TEST FAILED: mismatched minor version: was %u, found %u",
vers->v_minor, rest_vers->v_minor);
ret = EXIT_FAILURE;
} else {
(void) printf("TEST PASSED: minor version successfully "
"restored\n");
}
if (strcmp(nvme_ctrl_info_model(info),
nvme_ctrl_info_model(rest_info)) != 0) {
warnx("TEST FAILED: model string mismatch");
ret = EXIT_FAILURE;
} else {
(void) printf("TEST PASSED: model successfully restored\n");
}
if (strcmp(nvme_ctrl_info_serial(info),
nvme_ctrl_info_serial(rest_info)) != 0) {
warnx("TEST FAILED: serial string mismatch");
ret = EXIT_FAILURE;
} else {
(void) printf("TEST PASSED: serial successfully restored\n");
}
if (strcmp(nvme_ctrl_info_fwrev(info),
nvme_ctrl_info_fwrev(rest_info)) != 0) {
warnx("TEST FAILED: fwrev string mismatch");
ret = EXIT_FAILURE;
} else {
(void) printf("TEST PASSED: fwrev successfully restored\n");
}
if (nvme_ctrl_info_nns(info) != nvme_ctrl_info_nns(rest_info)) {
warnx("TEST FAILED: number of namespaces mismatch: was %u, "
"now %u", nvme_ctrl_info_nns(info),
nvme_ctrl_info_nns(rest_info));
ret = EXIT_FAILURE;
} else {
(void) printf("TEST PASSED: number of namespaces successfully "
"restored\n");
}
if (nvme_ctrl_info_type(info) != nvme_ctrl_info_type(rest_info)) {
warnx("TEST FAILED: controller type mismatch: was %u, "
"now %u", nvme_ctrl_info_type(info),
nvme_ctrl_info_type(rest_info));
ret = EXIT_FAILURE;
} else {
(void) printf("TEST PASSED: controller type successfully "
"restored\n");
}
if (nvme_ctrl_info_transport(info) !=
nvme_ctrl_info_transport(rest_info)) {
warnx("TEST FAILED: controller transport mismatch: was %u, "
"now %u", nvme_ctrl_info_transport(info),
nvme_ctrl_info_transport(rest_info));
ret = EXIT_FAILURE;
} else {
(void) printf("TEST PASSED: controller transport successfully "
"restored\n");
}
if (nvme_ctrl_info_transport(info) == NVME_CTRL_TRANSPORT_PCI &&
!info_roundtrip_pci(info, rest_info)) {
ret = EXIT_FAILURE;
}
if (ctrlid->id_oacs.oa_nsmgmt != 0 && !info_roundtrip_ns(info,
rest_info)) {
ret = EXIT_FAILURE;
}
if (nvme_ctrl_info_nformats(info) !=
nvme_ctrl_info_nformats(rest_info)) {
warnx("TEST FAILED: number of LBA formats mismatch: was %u, "
"now %u", nvme_ctrl_info_nformats(info),
nvme_ctrl_info_nformats(rest_info));
ret = EXIT_FAILURE;
} else {
(void) printf("TEST PASSED: number of LBA formats successfully "
"restored\n");
}
if (nvme_ctrl_info_nformats(info) > 0 && !info_roundtrip_lba(info,
rest_info)) {
ret = EXIT_FAILURE;
}
nvme_ctrl_info_free(rest_info);
nvme_ctrl_info_free(info);
nvme_ctrl_fini(ctrl);
nvme_fini(nvme);
if (ret == EXIT_SUCCESS) {
(void) printf("All tests exited successfully\n");
}
return (ret);
}