#include <err.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/sysmacros.h>
#include <sys/debug.h>
#include <thread.h>
#include <synch.h>
#include <strings.h>
#include <signal.h>
#include "nvme_ioctl_util.h"
static volatile int lock_sig_ret = EXIT_SUCCESS;
static volatile uint32_t lock_sig_nsignals = 0;
static volatile thread_t lock_sig_thrid;
typedef struct {
const char *lss_desc;
const nvme_ioctl_lock_t *lss_lock;
} lock_sig_test_t;
static const lock_sig_test_t lock_sig_tests[] = {
{ "controller write lock", &nvme_test_ctrl_wrlock },
{ "controller read lock", &nvme_test_ctrl_wrlock },
{ "namespace write lock", &nvme_test_ns_wrlock },
{ "namespace read lock", &nvme_test_ns_wrlock }
};
static void
lock_signal_hdlr(int sig)
{
VERIFY3U(sig, ==, SIGINFO);
VERIFY3U(thr_self(), ==, lock_sig_thrid);
lock_sig_nsignals++;
}
static void *
lock_signal_thr(void *arg)
{
int fd = nvme_ioctl_test_get_fd(0);
const lock_sig_test_t *test = arg;
nvme_ioctl_lock_t lock = *test->lss_lock;
sigset_t set;
int ret;
VERIFY0(sigemptyset(&set));
VERIFY0(sigaddset(&set, SIGINFO));
lock_sig_thrid = thr_self();
if ((ret = thr_sigsetmask(SIG_UNBLOCK, &set, NULL)) != 0) {
errc(EXIT_FAILURE, ret, "failed to unblock SIGINFO");
}
lock.nil_flags &= ~NVME_LOCK_F_DONT_BLOCK;
if (ioctl(fd, NVME_IOC_LOCK, &lock) != 0) {
err(EXIT_FAILURE, "TEST FAILED: unable to continue test "
"execution due to lock ioctl failure");
}
if (lock.nil_common.nioc_drv_err != NVME_IOCTL_E_LOCK_WAIT_SIGNAL) {
warnx("TEST FAILED: %s: lock thread didn't error with "
"NVME_IOCTL_E_LOCK_WAIT_SIGNAL (%u), but found instead %u",
test->lss_desc, NVME_IOCTL_E_LOCK_WAIT_SIGNAL,
lock.nil_common.nioc_drv_err);
lock_sig_ret = EXIT_FAILURE;
} else {
(void) printf("TEST PASSED: %s: thread successfully "
"interrupted\n", test->lss_desc);
}
thr_exit(NULL);
}
static void
lock_signal_one(const lock_sig_test_t *test)
{
int fd = nvme_ioctl_test_get_fd(0);
int ret;
thread_t thr;
nvme_ioctl_test_lock(fd, &nvme_test_ctrl_wrlock);
ret = thr_create(NULL, 0, lock_signal_thr, (void *)test, 0, &thr);
if (ret != 0) {
errc(EXIT_FAILURE, ret, "TEST FAILED: %s: cannot continue "
"because we failed to create the thread to signal",
test->lss_desc);
}
while (!nvme_ioctl_test_thr_blocked(thr)) {
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = MSEC2NSEC(10);
(void) nanosleep(&sleep, NULL);
}
ret = thr_kill(thr, SIGINFO);
if (ret != 0) {
errc(EXIT_FAILURE, ret, "TEST FAILED: %s: cannot continue "
"because we failed to send SIGINFO to tid %u",
test->lss_desc, thr);
}
ret = thr_join(thr, NULL, NULL);
if (ret != 0) {
errc(EXIT_FAILURE, ret, "TEST FAILED: %s: cannot continue "
"because we failed to join thread %u", test->lss_desc, thr);
}
VERIFY0(close(fd));
fd = nvme_ioctl_test_get_fd(0);
nvme_ioctl_test_lock(fd, test->lss_lock);
(void) printf("TEST PASSED: %s: successfully grabbed follow up lock\n",
test->lss_desc);
VERIFY0(close(fd));
}
int
main(void)
{
int ret;
sigset_t set;
struct sigaction act;
VERIFY0(sigfillset(&set));
if ((ret = thr_sigsetmask(SIG_BLOCK, &set, NULL)) != 0) {
errc(EXIT_FAILURE, ret, "failed to block signals");
}
act.sa_handler = lock_signal_hdlr;
VERIFY0(sigemptyset(&act.sa_mask));
act.sa_flags = 0;
VERIFY0(sigaction(SIGINFO, &act, NULL));
for (size_t i = 0; i < ARRAY_SIZE(lock_sig_tests); i++) {
lock_signal_one(&lock_sig_tests[i]);
}
if (lock_sig_nsignals != ARRAY_SIZE(lock_sig_tests)) {
lock_sig_ret = EXIT_FAILURE;
warnx("TEST FAILED: Didn't get %zu SIGINFO handlers, instead "
"got %u", ARRAY_SIZE(lock_sig_tests), lock_sig_nsignals);
} else {
(void) printf("TEST PASSED: Successfully ran SIGINFO "
"handlers\n");
}
return (lock_sig_ret);
}