/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2024 Oxide Computer Company */ /* * This validates the simplest form of locking functionality: * * o On a controller fd we can take controller read and write locks. * o On a controller fd we can take namespace read and write locks. * o On a namespace fd we can take namespace read and write locks with nsid = 0 * to get our nsid. * o On a namespace fd we can specify our nsid still. * o A namespace fd cannot take a controller lock with either nsid. */ #include #include #include #include #include #include #include #include "nvme_ioctl_util.h" /* * Loop for multiple times on each lock just to make sure this isn't a one off. */ #define BASIC_LOCK_NITERS 3 typedef struct { const char *blt_desc; nvme_lock_ent_t blt_ent; nvme_lock_level_t blt_level; uint32_t blt_nsid; } basic_lock_test_t; static const basic_lock_test_t basic_lock_tests[] = { { "ctrl fd ctrl write lock", NVME_LOCK_E_CTRL, NVME_LOCK_L_WRITE, 0 }, { "ctrl fd ctrl read lock", NVME_LOCK_E_CTRL, NVME_LOCK_L_READ, 0 }, { "ctrl fd ns write lock", NVME_LOCK_E_NS, NVME_LOCK_L_WRITE, 1 }, { "ctrl fd ns read lock", NVME_LOCK_E_NS, NVME_LOCK_L_READ, 1 } }; static const basic_lock_test_t basic_ns_lock_tests[] = { { "ns fd ns write lock (nsid=0)", NVME_LOCK_E_NS, NVME_LOCK_L_WRITE, 0 }, { "ns fd ns read lock (nsid=0)", NVME_LOCK_E_NS, NVME_LOCK_L_READ, 0 }, { "ns fd ns write lock (nsid=1)", NVME_LOCK_E_NS, NVME_LOCK_L_WRITE, 1 }, { "ns fd ns read lock (nsid=1)", NVME_LOCK_E_NS, NVME_LOCK_L_READ, 1 } }; static const basic_lock_test_t basic_ns_ctrl_lock_tests[] = { { "ns fd ctrl write lock (nsid=0)", NVME_LOCK_E_CTRL, NVME_LOCK_L_WRITE, 0 }, { "ns fd ctrl read lock (nsid=0)", NVME_LOCK_E_CTRL, NVME_LOCK_L_READ, 0 }, { "ns fd ctrl write lock (nsid=1)", NVME_LOCK_E_CTRL, NVME_LOCK_L_WRITE, 1 }, { "ns fd ctrl read lock (nsid=1)", NVME_LOCK_E_CTRL, NVME_LOCK_L_READ, 1 } }; static bool basic_lock_test(const basic_lock_test_t *test, int fd) { nvme_ioctl_lock_t lock; nvme_ioctl_unlock_t unlock; (void) memset(&lock, 0, sizeof (lock)); lock.nil_common.nioc_nsid = test->blt_nsid; lock.nil_ent = test->blt_ent; lock.nil_level = test->blt_level; lock.nil_flags = NVME_LOCK_F_DONT_BLOCK; (void) memset(&unlock, 0, sizeof (unlock)); unlock.niu_common.nioc_nsid = test->blt_nsid; unlock.niu_ent = test->blt_ent; for (uint32_t i = 0; i < BASIC_LOCK_NITERS; i++) { if (ioctl(fd, NVME_IOC_LOCK, &lock) != 0) { warn("TEST FAILED: %s %u: failed to issue lock ioctl", test->blt_desc, i); return (false); } else if (lock.nil_common.nioc_drv_err != NVME_IOCTL_E_OK) { warnx("TEST FAILED: %s %u: lock ioctl failed with " "driver error 0x%x", test->blt_desc, i, lock.nil_common.nioc_drv_err); return (false); } else { (void) printf("TEST PASSED: %s %u: lock acquired\n", test->blt_desc, i); } if (ioctl(fd, NVME_IOC_UNLOCK, &unlock) != 0) { return (false); } else if (lock.nil_common.nioc_drv_err != NVME_IOCTL_E_OK) { return (false); } else { (void) printf("TEST PASSED: %s %u: lock released\n", test->blt_desc, i); } } return (true); } /* * Verify that attempting to grab these locks fails with * NVME_IOCTL_E_NS_CANNOT_LOCK_CTRL. We don't bother with multiple repetitions * here. */ static bool basic_lock_test_no_ns_ctrl(const basic_lock_test_t *test, int fd) { nvme_ioctl_lock_t lock; bool ret = true; (void) memset(&lock, 0, sizeof (lock)); lock.nil_common.nioc_nsid = test->blt_nsid; lock.nil_ent = test->blt_ent; lock.nil_level = test->blt_level; lock.nil_flags = NVME_LOCK_F_DONT_BLOCK; if (ioctl(fd, NVME_IOC_LOCK, &lock) != 0) { warn("TEST FAILED: %s: failed to issue lock ioctl", test->blt_desc); return (false); } else if (lock.nil_common.nioc_drv_err == NVME_IOCTL_E_OK) { errx(EXIT_FAILURE, "TEST FAILED: %s: lock erroneously " "acquired: cannot continue test", test->blt_desc); } else if (lock.nil_common.nioc_drv_err != NVME_IOCTL_E_NS_CANNOT_LOCK_CTRL) { warnx("TEST FAILED: %s: lock ioctl failed with " "driver error 0x%x, expected 0x%x " "(NVME_IOCTL_E_NS_CANNOT_LOCK_CTRL)", test->blt_desc, lock.nil_common.nioc_drv_err, NVME_IOCTL_E_NS_CANNOT_LOCK_CTRL); return (false); } else { (void) printf("TEST PASSED: %s: lock denied\n", test->blt_desc); } return (ret); } int main(void) { int fd = nvme_ioctl_test_get_fd(0); int ret = EXIT_SUCCESS; for (size_t i = 0; i < ARRAY_SIZE(basic_lock_tests); i++) { if (!basic_lock_test(&basic_lock_tests[i], fd)) { ret = EXIT_FAILURE; } } VERIFY0(close(fd)); fd = nvme_ioctl_test_get_fd(1); for (size_t i = 0; i < ARRAY_SIZE(basic_ns_lock_tests); i++) { if (!basic_lock_test(&basic_ns_lock_tests[i], fd)) { ret = EXIT_FAILURE; } } for (size_t i = 0; i < ARRAY_SIZE(basic_ns_ctrl_lock_tests); i++) { if (!basic_lock_test_no_ns_ctrl(&basic_ns_ctrl_lock_tests[i], fd)) { ret = EXIT_FAILURE; } } return (ret); }