#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 enum {
CBF_FD_CTRL,
CBF_FD_NS
} ctrl_block_fd_t;
typedef struct {
const char *cbt_desc;
ctrl_block_fd_t cbt_fd0;
ctrl_block_fd_t cbt_fd1;
const nvme_ioctl_lock_t *cbt_lock0;
const nvme_ioctl_lock_t *cbt_lock1;
nvme_ioctl_errno_t cbt_ret1;
} ctrl_block_test_t;
static const ctrl_block_test_t ctrl_block_tests[] = { {
.cbt_desc = "controller write blocks controller read",
.cbt_fd0 = CBF_FD_CTRL,
.cbt_fd1 = CBF_FD_CTRL,
.cbt_lock0 = &nvme_test_ctrl_wrlock,
.cbt_lock1 = &nvme_test_ctrl_rdlock,
.cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
}, {
.cbt_desc = "controller write blocks controller write",
.cbt_fd0 = CBF_FD_CTRL,
.cbt_fd1 = CBF_FD_CTRL,
.cbt_lock0 = &nvme_test_ctrl_wrlock,
.cbt_lock1 = &nvme_test_ctrl_wrlock,
.cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
}, {
.cbt_desc = "controller write blocks namespace read",
.cbt_fd0 = CBF_FD_CTRL,
.cbt_fd1 = CBF_FD_NS,
.cbt_lock0 = &nvme_test_ctrl_wrlock,
.cbt_lock1 = &nvme_test_ns_rdlock,
.cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
}, {
.cbt_desc = "controller write blocks namespace write",
.cbt_fd0 = CBF_FD_CTRL,
.cbt_fd1 = CBF_FD_NS,
.cbt_lock0 = &nvme_test_ctrl_wrlock,
.cbt_lock1 = &nvme_test_ns_wrlock,
.cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
}, {
.cbt_desc = "controller read blocks controller write",
.cbt_fd0 = CBF_FD_CTRL,
.cbt_fd1 = CBF_FD_CTRL,
.cbt_lock0 = &nvme_test_ctrl_rdlock,
.cbt_lock1 = &nvme_test_ctrl_wrlock,
.cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
}, {
.cbt_desc = "controller read does not block namespace write",
.cbt_fd0 = CBF_FD_CTRL,
.cbt_fd1 = CBF_FD_NS,
.cbt_lock0 = &nvme_test_ctrl_rdlock,
.cbt_lock1 = &nvme_test_ns_wrlock,
.cbt_ret1 = NVME_IOCTL_E_OK
}, {
.cbt_desc = "namespace write blocks namespace read",
.cbt_fd0 = CBF_FD_NS,
.cbt_fd1 = CBF_FD_NS,
.cbt_lock0 = &nvme_test_ns_wrlock,
.cbt_lock1 = &nvme_test_ns_rdlock,
.cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
}, {
.cbt_desc = "namespace write blocks namespace read",
.cbt_fd0 = CBF_FD_NS,
.cbt_fd1 = CBF_FD_NS,
.cbt_lock0 = &nvme_test_ns_wrlock,
.cbt_lock1 = &nvme_test_ns_rdlock,
.cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
}, {
.cbt_desc = "namespace write blocks namespace write",
.cbt_fd0 = CBF_FD_NS,
.cbt_fd1 = CBF_FD_NS,
.cbt_lock0 = &nvme_test_ns_wrlock,
.cbt_lock1 = &nvme_test_ns_wrlock,
.cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
}, {
.cbt_desc = "namespace write blocks controller write",
.cbt_fd0 = CBF_FD_NS,
.cbt_fd1 = CBF_FD_CTRL,
.cbt_lock0 = &nvme_test_ns_wrlock,
.cbt_lock1 = &nvme_test_ctrl_wrlock,
.cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
}, {
.cbt_desc = "namespace write does not block controller read",
.cbt_fd0 = CBF_FD_NS,
.cbt_fd1 = CBF_FD_CTRL,
.cbt_lock0 = &nvme_test_ns_wrlock,
.cbt_lock1 = &nvme_test_ctrl_rdlock,
.cbt_ret1 = NVME_IOCTL_E_OK
}, {
.cbt_desc = "namespace read blocks namespace write",
.cbt_fd0 = CBF_FD_NS,
.cbt_fd1 = CBF_FD_NS,
.cbt_lock0 = &nvme_test_ns_rdlock,
.cbt_lock1 = &nvme_test_ns_wrlock,
.cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
}, {
.cbt_desc = "namespace read blocks controller write",
.cbt_fd0 = CBF_FD_NS,
.cbt_fd1 = CBF_FD_CTRL,
.cbt_lock0 = &nvme_test_ns_rdlock,
.cbt_lock1 = &nvme_test_ctrl_wrlock,
.cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
} };
static bool
ctrl_block_test_one(int fd0, int fd1, const ctrl_block_test_t *test)
{
nvme_ioctl_lock_t lock0 = *test->cbt_lock0;
nvme_ioctl_lock_t lock1 = *test->cbt_lock1;
if (ioctl(fd0, NVME_IOC_LOCK, &lock0) != 0) {
warn("TEST FAILED: %s: failed to issue lock ioctl for fd0",
test->cbt_desc);
return (false);
} else if (lock0.nil_common.nioc_drv_err != NVME_IOCTL_E_OK) {
warnx("TEST FAILED: %s: fd0 lock ioctl failed with 0x%x, "
"expected success", test->cbt_desc,
lock0.nil_common.nioc_drv_err);
return (false);
}
if (ioctl(fd1, NVME_IOC_LOCK, &lock1) != 0) {
warn("TEST FAILED: %s: failed to issue lock ioctl for fd1",
test->cbt_desc);
return (false);
} else if (lock1.nil_common.nioc_drv_err != test->cbt_ret1) {
warnx("TEST FAILED: %s: fd1 lock ioctl returned with 0x%x, "
"expected 0x%x", test->cbt_desc,
lock1.nil_common.nioc_drv_err, test->cbt_ret1);
return (false);
}
(void) printf("TEST PASSED: %s\n", test->cbt_desc);
return (true);
}
int
main(void)
{
int ret = EXIT_SUCCESS;
for (size_t i = 0; i < ARRAY_SIZE(ctrl_block_tests); i++) {
int fd0, fd1;
if (ctrl_block_tests[i].cbt_fd0 == CBF_FD_CTRL) {
fd0 = nvme_ioctl_test_get_fd(0);
} else {
fd0 = nvme_ioctl_test_get_fd(1);
}
if (ctrl_block_tests[i].cbt_fd1 == CBF_FD_CTRL) {
fd1 = nvme_ioctl_test_get_fd(0);
} else {
fd1 = nvme_ioctl_test_get_fd(1);
}
if (!ctrl_block_test_one(fd0, fd1, &ctrl_block_tests[i])) {
ret = EXIT_FAILURE;
}
VERIFY0(close(fd0));
VERIFY0(close(fd1));
}
return (ret);
}