#include <stdlib.h>
#include <stdbool.h>
#include <err.h>
#include <umem.h>
#include "libnvme_test_common.h"
static bool
ns_info_test_inactive(nvme_ns_info_t *info, uint32_t nsid)
{
uint8_t guid[16];
bool ret = true;
uint64_t val;
const nvme_nvm_lba_fmt_t *fmt;
const char *bd;
if (nvme_ns_info_nguid(info, guid)) {
warnx("TEST FAILED: ns %u returned a namespace guid in error",
nsid);
ret = false;
} else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) {
warnx("TEST FAILED: ns %u nvme_ns_info_nguid() returned "
"wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE "
"(0x%x)", nsid, nvme_ns_info_errtostr(info,
nvme_ns_info_err(info)), nvme_ns_info_err(info),
NVME_INFO_ERR_NS_INACTIVE);
ret = false;
} else {
(void) printf("TEST PASSED: ns %u: nvme_ns_info_nguid() "
"returned NVME_INFO_ERR_NS_INACTIVE\n", nsid);
}
if (nvme_ns_info_eui64(info, guid)) {
warnx("TEST FAILED: ns %u returned a namespace eui64 in error",
nsid);
ret = false;
} else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) {
warnx("TEST FAILED: ns %u nvme_ns_info_eui64() returned "
"wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE "
"(0x%x)", nsid, nvme_ns_info_errtostr(info,
nvme_ns_info_err(info)), nvme_ns_info_err(info),
NVME_INFO_ERR_NS_INACTIVE);
ret = false;
} else {
(void) printf("TEST PASSED: ns %u: nvme_ns_info_eui64() "
"returned NVME_INFO_ERR_NS_INACTIVE\n", nsid);
}
if (nvme_ns_info_size(info, &val)) {
warnx("TEST FAILED: ns %u returned a namespace size in error",
nsid);
ret = false;
} else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) {
warnx("TEST FAILED: ns %u nvme_ns_info_size() returned "
"wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE "
"(0x%x)", nsid, nvme_ns_info_errtostr(info,
nvme_ns_info_err(info)), nvme_ns_info_err(info),
NVME_INFO_ERR_NS_INACTIVE);
ret = false;
} else {
(void) printf("TEST PASSED: ns %u: nvme_ns_info_size() "
"returned NVME_INFO_ERR_NS_INACTIVE\n", nsid);
}
if (nvme_ns_info_cap(info, &val)) {
warnx("TEST FAILED: ns %u returned a namespace cap in error",
nsid);
ret = false;
} else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) {
warnx("TEST FAILED: ns %u nvme_ns_info_cap() returned "
"wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE "
"(0x%x)", nsid, nvme_ns_info_errtostr(info,
nvme_ns_info_err(info)), nvme_ns_info_err(info),
NVME_INFO_ERR_NS_INACTIVE);
ret = false;
} else {
(void) printf("TEST PASSED: ns %u: nvme_ns_info_cap() "
"returned NVME_INFO_ERR_NS_INACTIVE\n", nsid);
}
if (nvme_ns_info_use(info, &val)) {
warnx("TEST FAILED: ns %u returned a namespace use in error",
nsid);
ret = false;
} else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) {
warnx("TEST FAILED: ns %u nvme_ns_info_use() returned "
"wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE "
"(0x%x)", nsid, nvme_ns_info_errtostr(info,
nvme_ns_info_err(info)), nvme_ns_info_err(info),
NVME_INFO_ERR_NS_INACTIVE);
ret = false;
} else {
(void) printf("TEST PASSED: ns %u: nvme_ns_info_use() "
"returned NVME_INFO_ERR_NS_INACTIVE\n", nsid);
}
if (nvme_ns_info_curformat(info, &fmt)) {
warnx("TEST FAILED: ns %u returned a current format in error",
nsid);
ret = false;
} else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) {
warnx("TEST FAILED: ns %u nvme_ns_info_curformat() returned "
"wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE "
"(0x%x)", nsid, nvme_ns_info_errtostr(info,
nvme_ns_info_err(info)), nvme_ns_info_err(info),
NVME_INFO_ERR_NS_INACTIVE);
ret = false;
} else {
(void) printf("TEST PASSED: ns %u: nvme_ns_info_curformat() "
"returned NVME_INFO_ERR_NS_INACTIVE\n", nsid);
}
if (nvme_ns_info_format(info, 0, &fmt)) {
warnx("TEST FAILED: ns %u returned format 0 in error",
nsid);
ret = false;
} else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) {
warnx("TEST FAILED: ns %u nvme_ns_info_format() returned "
"wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE "
"(0x%x)", nsid, nvme_ns_info_errtostr(info,
nvme_ns_info_err(info)), nvme_ns_info_err(info),
NVME_INFO_ERR_NS_INACTIVE);
ret = false;
} else {
(void) printf("TEST PASSED: ns %u: nvme_ns_info_curformat() "
"returned NVME_INFO_ERR_NS_INACTIVE\n", nsid);
}
if (nvme_ns_info_bd_addr(info, &bd)) {
warnx("TEST FAILED: ns %u returned a blkdev address in error",
nsid);
ret = false;
} else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_NO_BLKDEV) {
warnx("TEST FAILED: ns %u nvme_ns_info_bd_addr() returned "
"wrong error %s (0x%x), not NVME_INFO_ERR_NS_NO_BLKDEV "
"(0x%x)", nsid, nvme_ns_info_errtostr(info,
nvme_ns_info_err(info)), nvme_ns_info_err(info),
NVME_INFO_ERR_NS_NO_BLKDEV);
ret = false;
} else {
(void) printf("TEST PASSED: ns %u: nvme_ns_info_curformat() "
"returned NVME_INFO_ERR_NS_NO_BLKDEV\n", nsid);
}
return (ret);
}
static bool
ns_info_test_size(nvme_ns_info_t *info,
bool (*func)(nvme_ns_info_t *, uint64_t *), uint64_t exp_size,
const char *name, uint32_t nsid)
{
uint64_t val;
if (!func(info, &val)) {
libnvme_test_ns_info_warn(info, "ns %u nvme_ns_info_%s() "
"unexpected failed", nsid, name);
return (false);
} else if (val != exp_size) {
warnx("TEST FAILED: ns %u: nvme_ns_info_%s() value was 0x%"
PRIx64 ", but expected 0x%" PRIx64, nsid, name, val,
exp_size);
return (false);
} else {
(void) printf("TEST PASSED: ns %u: nvme_ns_info_%s() returned "
"correct value\n", nsid, name);
return (true);
}
}
static bool
ns_info_test(nvme_ctrl_t *ctrl, const nvme_version_t *vers, uint32_t nsid)
{
bool ret = true;
nvme_ns_t *ns = NULL;
nvme_ns_info_t *info = NULL;
nvme_ns_disc_level_t level;
const nvme_identify_nsid_t *idns;
const nvme_nvm_lba_fmt_t *fmt;
uint32_t nfmt;
if ((nsid % 2) == 0) {
if (!nvme_ns_init(ctrl, nsid, &ns)) {
libnvme_test_ctrl_warn(ctrl, "failed to init ns %u",
nsid);
ret = false;
goto done;
}
if (!nvme_ns_info_snap(ns, &info)) {
libnvme_test_ctrl_warn(ctrl, "failed to take snapshot "
"of ns %u", nsid);
ret = false;
goto done;
}
} else {
if (!nvme_ctrl_ns_info_snap(ctrl, nsid, &info)) {
libnvme_test_ctrl_warn(ctrl, "failed to take snapshot "
"of ns %u", nsid);
ret = false;
goto done;
}
}
(void) printf("TEST PASSED: ns %u: successfully got info snapshot\n",
nsid);
if (nvme_ns_info_nsid(info) != nsid) {
warnx("TEST FAILED: nsid %u info snapshot returned wrong "
"nsid: %u", nsid, nvme_ns_info_nsid(info));
ret = false;
} else {
(void) printf("TEST PASSED: ns %u: info snapshot had correct "
"nsid\n", nsid);
}
level = nvme_ns_info_level(info);
if (level < NVME_NS_DISC_F_ACTIVE) {
if (!ns_info_test_inactive(info, nsid)) {
ret = false;
}
goto done;
}
idns = nvme_ns_info_identify(info);
if (!ns_info_test_size(info, nvme_ns_info_size, idns->id_nsize, "size",
nsid)) {
ret = false;
}
if (!ns_info_test_size(info, nvme_ns_info_cap, idns->id_ncap, "cap",
nsid)) {
ret = false;
}
if (!ns_info_test_size(info, nvme_ns_info_use, idns->id_nuse, "use",
nsid)) {
ret = false;
}
if (!nvme_ns_info_curformat(info, &fmt)) {
libnvme_test_ns_info_warn(info, "ns %u failed to get current "
"format", nsid);
ret = false;
} else if (nvme_nvm_lba_fmt_id(fmt) != idns->id_flbas.lba_format) {
warnx("TEST FAILED: current LBA format 0x%x does not match "
"identify namespace 0x%x", nvme_nvm_lba_fmt_id(fmt),
idns->id_flbas.lba_format);
ret = false;
} else {
(void) printf("TEST PASSED: ns %u: nvme_ns_info_curformat() "
"returned correct format\n", nsid);
}
if (!nvme_ns_info_nformats(info, &nfmt)) {
libnvme_test_ns_info_warn(info, "ns %u failed to get number "
"of formats", nsid);
ret = false;
} else if (nfmt != idns->id_nlbaf + 1) {
warnx("TEST FAILED: number of LBA formats 0x%x does not match "
"identify namespace 0x%x", nvme_nvm_lba_fmt_id(fmt),
idns->id_nlbaf + 1);
ret = false;
} else {
(void) printf("TEST PASSED: ns %u: nvme_ns_info_nformats() "
"returned correct number of formats\n", nsid);
}
if (nvme_ns_info_format(info, 0x7777, &fmt)) {
warnx("TEST FAILED: ns %u erroneously returned info for format "
"0x7777", nsid);
ret = false;
} else if (nvme_ns_info_err(info) != NVME_INFO_ERR_BAD_FMT) {
warnx("TEST FAILED: ns %u nvme_ns_info_format() returned "
"wrong error %s (0x%x), not NVME_INFO_ERR_BAD_FMT "
"(0x%x)", nsid, nvme_ns_info_errtostr(info,
nvme_ns_info_err(info)), nvme_ns_info_err(info),
NVME_INFO_ERR_BAD_FMT);
ret = false;
} else {
(void) printf("TEST PASSED: ns %u: invalid format id 0x7777 "
"correctly rejected\n", nsid);
}
done:
nvme_ns_info_free(info);
nvme_ns_fini(ns);
return (ret);
}
static bool
ns_info_bad_snap(nvme_ctrl_t *ctrl, uint32_t nsid, nvme_ns_info_t **infop,
nvme_err_t exp_err, const char *desc)
{
if (nvme_ctrl_ns_info_snap(ctrl, nsid, infop)) {
warnx("TEST FAILED: nvme_ctrl_ns_info_snap() erroneously "
"passed despite %s", desc);
return (false);
} else if (nvme_ctrl_err(ctrl) != exp_err) {
warnx("TEST FAILED: nvme_ctrl_ns_info_snap() returned "
"wrong error %s (0x%x), not %s (0x%x)",
nvme_ctrl_errtostr(ctrl, nvme_ctrl_err(ctrl)),
nvme_ctrl_err(ctrl), nvme_ctrl_errtostr(ctrl,
exp_err), exp_err);
return (false);
} else {
(void) printf("TEST PASSED: nvme_ctrl_ns_info_snap() failed "
"correctly for %s\n", desc);
return (true);
}
}
int
main(void)
{
int ret = EXIT_SUCCESS;
nvme_t *nvme;
nvme_ctrl_t *ctrl;
nvme_ctrl_info_t *info;
nvme_ns_info_t *ns_info;
uint32_t nns;
const nvme_version_t *vers;
libnvme_test_init(&nvme, &ctrl);
if (!nvme_ctrl_info_snap(ctrl, &info)) {
libnvme_test_ctrl_fatal(ctrl, "failed to take information "
"snapshot");
}
nns = nvme_ctrl_info_nns(info);
if (nns == 0) {
errx(EXIT_FAILURE, "TEST FAILED: somehow discovered 0 "
"namespaces");
}
vers = nvme_ctrl_info_version(info);
for (uint32_t i = 1; i <= nns; i++) {
if (!ns_info_test(ctrl, vers, i)) {
ret = EXIT_FAILURE;
}
}
if (!ns_info_bad_snap(ctrl, NVME_NSID_BCAST, &ns_info,
NVME_ERR_NS_RANGE, "invalid nsid")) {
ret = EXIT_FAILURE;
}
if (!ns_info_bad_snap(ctrl, 1, NULL, NVME_ERR_BAD_PTR,
"invalid output pointer")) {
ret = EXIT_FAILURE;
}
umem_setmtbf(1);
if (!ns_info_bad_snap(ctrl, 1, &ns_info, NVME_ERR_NO_MEM,
"no memory")) {
ret = EXIT_FAILURE;
}
umem_setmtbf(0);
if (ret == EXIT_SUCCESS) {
(void) printf("All tests passed successfully\n");
}
nvme_ctrl_info_free(info);
nvme_ctrl_fini(ctrl);
nvme_fini(nvme);
return (ret);
}