#include <err.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/sysmacros.h>
#include <sys/debug.h>
#include "nvme_ioctl_util.h"
typedef struct {
const char *rlt_desc;
bool rlt_ctrl_only;
const nvme_ioctl_lock_t *rlt_lock;
} rec_lock_test_t;
static const rec_lock_test_t rec_lock_tests[] = { {
.rlt_desc = "recursive controller write lock",
.rlt_ctrl_only = true,
.rlt_lock = &nvme_test_ctrl_wrlock
}, {
.rlt_desc = "recursive controller read lock",
.rlt_ctrl_only = true,
.rlt_lock = &nvme_test_ctrl_rdlock
}, {
.rlt_desc = "recursive namespace write lock",
.rlt_lock = &nvme_test_ns_wrlock
}, {
.rlt_desc = "recursive namespace read lock",
.rlt_lock = &nvme_test_ns_rdlock
} };
typedef struct {
const char *nlt_desc;
const nvme_ioctl_lock_t *nlt_lock;
nvme_ioctl_errno_t nlt_err;
} ns_lock_test_t;
static const ns_lock_test_t ns_lock_tests[] = { {
.nlt_desc = "take controller read lock w/ ns lock",
.nlt_lock = &nvme_test_ctrl_rdlock,
.nlt_err = NVME_IOCTL_E_LOCK_NO_CTRL_WITH_NS
}, {
.nlt_desc = "take controller read lock w/ ns lock",
.nlt_lock = &nvme_test_ctrl_wrlock,
.nlt_err = NVME_IOCTL_E_LOCK_NO_CTRL_WITH_NS
} };
static const ns_lock_test_t ns_ctrl_tests[] = { {
.nlt_desc = "attempt ns read lock with controller write lock",
.nlt_lock = &nvme_test_ns_rdlock,
.nlt_err = NVME_IOCTL_LOCK_NO_NS_WITH_CTRL_WRLOCK
}, {
.nlt_desc = "attempt ns write lock with controller write lock",
.nlt_lock = &nvme_test_ns_wrlock,
.nlt_err = NVME_IOCTL_LOCK_NO_NS_WITH_CTRL_WRLOCK
} };
static bool
rec_lock_test(int fd, const rec_lock_test_t *test, bool nsfd)
{
nvme_ioctl_lock_t lock = *test->rlt_lock;
const char *type = nsfd ? "(ns)" : "(ctrl)";
if (ioctl(fd, NVME_IOC_LOCK, &lock) != 0) {
warn("TEST FAILED: %s %s: failed to issue initial lock ioctl",
test->rlt_desc, type);
return (false);
} else if (lock.nil_common.nioc_drv_err != NVME_IOCTL_E_OK) {
warnx("TEST FAILED: %s %s: initial lock ioctl failed with "
"0x%x, expected success", test->rlt_desc, type,
lock.nil_common.nioc_drv_err);
return (false);
}
lock = *test->rlt_lock;
if (ioctl(fd, NVME_IOC_LOCK, &lock) != 0) {
warn("TEST FAILED: %s %s: failed to issue recursive lock ioctl",
test->rlt_desc, type);
return (false);
} else if (lock.nil_common.nioc_drv_err !=
NVME_IOCTL_E_LOCK_ALREADY_HELD) {
warnx("TEST FAILED: %s %s: recursive lock ioctl failed with "
"0x%x, expected 0x%x (NVME_IOCTL_E_LOCK_ALREADY_HELD)",
test->rlt_desc, type, lock.nil_common.nioc_drv_err,
NVME_IOCTL_E_LOCK_ALREADY_HELD);
return (false);
}
return (true);
}
static bool
ns_lock_test(int fd, const ns_lock_test_t *test, bool rdlock)
{
nvme_ioctl_lock_t lock = *test->nlt_lock;
const char *type = rdlock ? "(ns read lock)" : "(ns write lock)";
if (ioctl(fd, NVME_IOC_LOCK, &lock) != 0) {
warn("TEST FAILED: %s %s: failed to issue lock ioctl",
test->nlt_desc, type);
return (false);
} else if (lock.nil_common.nioc_drv_err != test->nlt_err) {
warnx("TEST FAILED: %s %s: recursive lock ioctl failed with "
"0x%x, expected 0x%x",
test->nlt_desc, type, lock.nil_common.nioc_drv_err,
test->nlt_err);
return (false);
} else {
(void) printf("TEST PASSED: %s\n", test->nlt_desc);
}
return (true);
}
int
main(void)
{
int ret = EXIT_SUCCESS;
for (size_t i = 0; i < ARRAY_SIZE(rec_lock_tests); i++) {
int fd = nvme_ioctl_test_get_fd(0);
if (!rec_lock_test(fd, &rec_lock_tests[i], false)) {
ret = EXIT_FAILURE;
}
VERIFY0(close(fd));
if (rec_lock_tests[i].rlt_ctrl_only)
continue;
fd = nvme_ioctl_test_get_fd(1);
if (!rec_lock_test(fd, &rec_lock_tests[i], true)) {
ret = EXIT_FAILURE;
}
VERIFY0(close(fd));
}
int fd = nvme_ioctl_test_get_fd(0);
nvme_ioctl_test_lock(fd, &nvme_test_ns_rdlock);
for (size_t i = 0; i < ARRAY_SIZE(ns_lock_tests); i++) {
if (!ns_lock_test(fd, &ns_lock_tests[i], true)) {
ret = EXIT_FAILURE;
}
}
VERIFY0(close(fd));
fd = nvme_ioctl_test_get_fd(0);
nvme_ioctl_test_lock(fd, &nvme_test_ns_wrlock);
for (size_t i = 0; i < ARRAY_SIZE(ns_lock_tests); i++) {
if (!ns_lock_test(fd, &ns_lock_tests[i], false)) {
ret = EXIT_FAILURE;
}
}
VERIFY0(close(fd));
fd = nvme_ioctl_test_get_fd(0);
nvme_ioctl_test_lock(fd, &nvme_test_ctrl_wrlock);
for (size_t i = 0; i < ARRAY_SIZE(ns_ctrl_tests); i++) {
if (!ns_lock_test(fd, &ns_ctrl_tests[i], true)) {
ret = EXIT_FAILURE;
}
}
VERIFY0(close(fd));
return (ret);
}