diff options
author | Andy Fiddaman <omnios@citrus-it.co.uk> | 2022-02-02 16:23:30 +0000 |
---|---|---|
committer | Andy Fiddaman <omnios@citrus-it.co.uk> | 2022-02-07 20:51:44 +0000 |
commit | 251becc882939aaf03088561add2c257a7a92424 (patch) | |
tree | e83d28aec3116e89b7cf379ae918ac71ea9c976d | |
parent | 6dc983494b0ffef2565cc4d91371ee345425ffab (diff) | |
download | illumos-joyent-251becc882939aaf03088561add2c257a7a92424.tar.gz |
14374 Improve bhyve mevent EVF_VNODE monitoring
Reviewed by: Patrick Mooney <pmooney@pfmooney.com>
Approved by: Dan McDonald <danmcd@joyent.com>
-rw-r--r-- | usr/src/cmd/bhyve/mevent.c | 327 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/test/tests/mevent/Makefile | 9 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/test/tests/mevent/mevent.c | 6 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/test/tests/mevent/testlib.h | 2 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/test/tests/mevent/vnode_file.c | 141 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/test/tests/mevent/vnode_zvol.c | 259 | ||||
-rw-r--r-- | usr/src/pkg/manifests/system-bhyve-tests.p5m | 4 | ||||
-rw-r--r-- | usr/src/test/bhyve-tests/runfiles/default.run | 7 |
8 files changed, 637 insertions, 118 deletions
diff --git a/usr/src/cmd/bhyve/mevent.c b/usr/src/cmd/bhyve/mevent.c index 98a1d8ca7f..576ba3390e 100644 --- a/usr/src/cmd/bhyve/mevent.c +++ b/usr/src/cmd/bhyve/mevent.c @@ -30,7 +30,7 @@ /* * Copyright 2018 Joyent, Inc. - * Copyright 2021 OmniOS Community Edition (OmniOSce) Association. + * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. */ /* @@ -82,11 +82,15 @@ __FBSDID("$FreeBSD$"); #define EV_ADD EV_ENABLE #define EV_DISABLE 0x02 #define EV_DELETE 0x04 + +static int mevent_file_poll_interval_ms = 5000; #endif static pthread_t mevent_tid; static pthread_once_t mevent_once = PTHREAD_ONCE_INIT; +#ifdef __FreeBSD__ static int mevent_timid = 43; +#endif static int mevent_pipefd[2]; static int mfd; static pthread_mutex_t mevent_lmutex = PTHREAD_MUTEX_INITIALIZER; @@ -112,6 +116,12 @@ struct mevent { boolean_t me_auto_requeue; struct file_obj me_fobj; char *me_fname; + struct { + int mp_fd; + off_t mp_size; + void (*mp_func)(int, enum ev_type, void *); + void *mp_param; + } me_poll; #endif LIST_ENTRY(mevent) me_list; }; @@ -177,7 +187,7 @@ mevent_init(void) cap_rights_init(&rights, CAP_KQUEUE); if (caph_rights_limit(mfd, &rights) == -1) errx(EX_OSERR, "Unable to apply rights for sandbox"); - #endif +#endif LIST_INIT(&change_head); LIST_INIT(&global_head); @@ -382,149 +392,233 @@ mevent_fdpath(int fd) } static void -mevent_update_one(struct mevent *mevp) +mevent_poll_file_attrib(int fd, enum ev_type type, void *param) { - int portfd = mevp->me_notify.portnfy_port; + struct mevent *mevp = param; + struct stat st; - switch (mevp->me_type) { - case EVF_READ: - case EVF_WRITE: - mevp->me_auto_requeue = B_FALSE; + if (fstat(mevp->me_poll.mp_fd, &st) != 0) { + (void) fprintf(stderr, "%s: fstat(%d) \"%s\" failed: %s\n", + __func__, fd, mevp->me_fname, strerror(errno)); + return; + } + + if (mevp->me_poll.mp_size != st.st_size || + mevp->me_fobj.fo_ctime.tv_sec != st.st_ctim.tv_sec || + mevp->me_fobj.fo_ctime.tv_nsec != st.st_ctim.tv_nsec) { + mevp->me_poll.mp_size = st.st_size; + mevp->me_fobj.fo_atime = st.st_atim; + mevp->me_fobj.fo_mtime = st.st_mtim; + mevp->me_fobj.fo_ctime = st.st_ctim; + + (*mevp->me_poll.mp_func)(mevp->me_poll.mp_fd, EVF_VNODE, + mevp->me_poll.mp_param); + } +} - switch (mevp->me_state) { - case EV_ENABLE: - { - int events; +static void +mevent_update_one_readwrite(struct mevent *mevp) +{ + int portfd = mevp->me_notify.portnfy_port; - events = (mevp->me_type == EVF_READ) ? POLLIN : POLLOUT; + mevp->me_auto_requeue = B_FALSE; - if (port_associate(portfd, PORT_SOURCE_FD, mevp->me_fd, - events, mevp) != 0) { - (void) fprintf(stderr, - "port_associate fd %d %p failed: %s\n", - mevp->me_fd, mevp, strerror(errno)); - } - return; + switch (mevp->me_state) { + case EV_ENABLE: + { + const int events = (mevp->me_type == EVF_READ) ? + POLLIN : POLLOUT; + + if (port_associate(portfd, PORT_SOURCE_FD, mevp->me_fd, + events, mevp) != 0) { + (void) fprintf(stderr, + "port_associate fd %d %p failed: %s\n", + mevp->me_fd, mevp, strerror(errno)); } - case EV_DISABLE: - case EV_DELETE: - /* - * A disable that comes in while an event is being - * handled will result in an ENOENT. - */ - if (port_dissociate(portfd, PORT_SOURCE_FD, - mevp->me_fd) != 0 && errno != ENOENT) { - (void) fprintf(stderr, "port_dissociate " - "portfd %d fd %d mevp %p failed: %s\n", - portfd, mevp->me_fd, mevp, strerror(errno)); - } - return; - default: - goto abort; + return; + } + case EV_DISABLE: + case EV_DELETE: + /* + * A disable that comes in while an event is being + * handled will result in an ENOENT. + */ + if (port_dissociate(portfd, PORT_SOURCE_FD, + mevp->me_fd) != 0 && errno != ENOENT) { + (void) fprintf(stderr, "port_dissociate " + "portfd %d fd %d mevp %p failed: %s\n", + portfd, mevp->me_fd, mevp, strerror(errno)); } + return; + default: + (void) fprintf(stderr, "%s: unhandled state %d\n", __func__, + mevp->me_state); + abort(); + } +} - case EVF_TIMER: - mevp->me_auto_requeue = B_TRUE; +static void +mevent_update_one_timer(struct mevent *mevp) +{ + mevp->me_auto_requeue = B_TRUE; - switch (mevp->me_state) { - case EV_ENABLE: - { - struct itimerspec it = { 0 }; + switch (mevp->me_state) { + case EV_ENABLE: + { + struct itimerspec it = { 0 }; - mevp->me_sigev.sigev_notify = SIGEV_PORT; - mevp->me_sigev.sigev_value.sival_ptr = &mevp->me_notify; + mevp->me_sigev.sigev_notify = SIGEV_PORT; + mevp->me_sigev.sigev_value.sival_ptr = &mevp->me_notify; - if (timer_create(CLOCK_REALTIME, &mevp->me_sigev, - &mevp->me_timid) != 0) { - (void) fprintf(stderr, - "timer_create failed: %s", strerror(errno)); - return; - } + if (timer_create(CLOCK_REALTIME, &mevp->me_sigev, + &mevp->me_timid) != 0) { + (void) fprintf(stderr, "timer_create failed: %s", + strerror(errno)); + return; + } - /* The first timeout */ - it.it_value.tv_sec = mevp->me_msecs / MILLISEC; - it.it_value.tv_nsec = - MSEC2NSEC(mevp->me_msecs % MILLISEC); - /* Repeat at the same interval */ - it.it_interval = it.it_value; + /* The first timeout */ + it.it_value.tv_sec = mevp->me_msecs / MILLISEC; + it.it_value.tv_nsec = + MSEC2NSEC(mevp->me_msecs % MILLISEC); + /* Repeat at the same interval */ + it.it_interval = it.it_value; - if (timer_settime(mevp->me_timid, 0, &it, NULL) != 0) { - (void) fprintf(stderr, "timer_settime failed: " - "%s", strerror(errno)); - } - return; + if (timer_settime(mevp->me_timid, 0, &it, NULL) != 0) { + (void) fprintf(stderr, "timer_settime failed: %s", + strerror(errno)); } - case EV_DISABLE: - case EV_DELETE: - if (timer_delete(mevp->me_timid) != 0) { - (void) fprintf(stderr, "timer_delete failed: " - "%s", strerror(errno)); - } - return; - default: - goto abort; + return; + } + case EV_DISABLE: + case EV_DELETE: + if (timer_delete(mevp->me_timid) != 0) { + (void) fprintf(stderr, "timer_delete failed: %s", + strerror(errno)); } + mevp->me_timid = -1; + return; + default: + (void) fprintf(stderr, "%s: unhandled state %d\n", __func__, + mevp->me_state); + abort(); + } +} - case EVF_VNODE: - mevp->me_auto_requeue = B_FALSE; +static void +mevent_update_one_vnode(struct mevent *mevp) +{ + int portfd = mevp->me_notify.portnfy_port; - switch (mevp->me_state) { - case EV_ENABLE: - { - int events = 0; + mevp->me_auto_requeue = B_FALSE; - if ((mevp->me_fflags & EVFF_ATTRIB) != 0) - events |= FILE_ATTRIB; + switch (mevp->me_state) { + case EV_ENABLE: + { + int events = 0; - assert(events != 0); + if ((mevp->me_fflags & EVFF_ATTRIB) != 0) + events |= FILE_ATTRIB; - if (mevp->me_fname == NULL) { - mevp->me_fname = mevent_fdpath(mevp->me_fd); - if (mevp->me_fname == NULL) - return; - } + assert(events != 0); + + if (mevp->me_fname == NULL) { + mevp->me_fname = mevent_fdpath(mevp->me_fd); + if (mevp->me_fname == NULL) + return; + } + + bzero(&mevp->me_fobj, sizeof (mevp->me_fobj)); + mevp->me_fobj.fo_name = mevp->me_fname; - bzero(&mevp->me_fobj, sizeof (mevp->me_fobj)); - mevp->me_fobj.fo_name = mevp->me_fname; + if (port_associate(portfd, PORT_SOURCE_FILE, + (uintptr_t)&mevp->me_fobj, events, mevp) != 0) { + /* + * If this file does not support event ports + * (e.g. ZVOLs do not yet have support) + * then convert this to a timer event and poll for + * file attribute changes. + */ + struct stat st; - if (port_associate(portfd, PORT_SOURCE_FILE, - (uintptr_t)&mevp->me_fobj, events, mevp) != 0) { + if (errno != ENOTSUP) { (void) fprintf(stderr, - "port_associate fd %d (%s) %p failed: %s\n", + "port_associate fd %d (%s) %p failed: %s" + ", polling instead\n", mevp->me_fd, mevp->me_fname, mevp, strerror(errno)); } - return; - } - case EV_DISABLE: - case EV_DELETE: - /* - * A disable that comes in while an event is being - * handled will result in an ENOENT. - */ - if (port_dissociate(portfd, PORT_SOURCE_FILE, - (uintptr_t)&mevp->me_fobj) != 0 && - errno != ENOENT) { - (void) fprintf(stderr, "port_dissociate " - "portfd %d fd %d mevp %p failed: %s\n", - portfd, mevp->me_fd, mevp, strerror(errno)); + + if (fstat(mevp->me_fd, &st) != 0) { + (void) fprintf(stderr, + "fstat(%d) \"%s\" failed: %s\n", + mevp->me_fd, mevp->me_fname, + strerror(errno)); + return; } - free(mevp->me_fname); - mevp->me_fname = NULL; - return; - default: - goto abort; - } + mevp->me_fobj.fo_atime = st.st_atim; + mevp->me_fobj.fo_mtime = st.st_mtim; + mevp->me_fobj.fo_ctime = st.st_ctim; + + mevp->me_poll.mp_fd = mevp->me_fd; + mevp->me_poll.mp_size = st.st_size; + + mevp->me_poll.mp_func = mevp->me_func; + mevp->me_poll.mp_param = mevp->me_param; + mevp->me_func = mevent_poll_file_attrib; + mevp->me_param = mevp; + + mevp->me_type = EVF_TIMER; + mevp->me_timid = -1; + mevp->me_msecs = mevent_file_poll_interval_ms; + mevent_update_one_timer(mevp); + } + return; + } + case EV_DISABLE: + case EV_DELETE: + /* + * A disable that comes in while an event is being + * handled will result in an ENOENT. + */ + if (port_dissociate(portfd, PORT_SOURCE_FILE, + (uintptr_t)&mevp->me_fobj) != 0 && + errno != ENOENT) { + (void) fprintf(stderr, "port_dissociate " + "portfd %d fd %d mevp %p failed: %s\n", + portfd, mevp->me_fd, mevp, strerror(errno)); + } + free(mevp->me_fname); + mevp->me_fname = NULL; + return; default: - /* EVF_SIGNAL not yet implemented. */ - goto abort; + (void) fprintf(stderr, "%s: unhandled state %d\n", __func__, + mevp->me_state); + abort(); } +} -abort: - (void) fprintf(stderr, "%s: unhandled type %d state %d\n", __func__, - mevp->me_type, mevp->me_state); - abort(); +static void +mevent_update_one(struct mevent *mevp) +{ + switch (mevp->me_type) { + case EVF_READ: + case EVF_WRITE: + mevent_update_one_readwrite(mevp); + break; + case EVF_TIMER: + mevent_update_one_timer(mevp); + break; + case EVF_VNODE: + mevent_update_one_vnode(mevp); + break; + case EVF_SIGNAL: /* EVF_SIGNAL not yet implemented. */ + default: + (void) fprintf(stderr, "%s: unhandled event type %d\n", + __func__, mevp->me_type); + abort(); + } } static void @@ -637,13 +731,16 @@ mevent_add_state(int tfd, enum ev_type type, if (type == EVF_TIMER) { mevp->me_msecs = tfd; +#ifdef __FreeBSD__ mevp->me_timid = mevent_timid++; +#else + mevp->me_timid = -1; +#endif } else mevp->me_fd = tfd; mevp->me_type = type; mevp->me_func = func; mevp->me_param = param; - mevp->me_state = state; mevp->me_fflags = fflags; diff --git a/usr/src/cmd/bhyve/test/tests/mevent/Makefile b/usr/src/cmd/bhyve/test/tests/mevent/Makefile index 363deb02cc..9d93e17f5a 100644 --- a/usr/src/cmd/bhyve/test/tests/mevent/Makefile +++ b/usr/src/cmd/bhyve/test/tests/mevent/Makefile @@ -12,6 +12,7 @@ # # Copyright 2018 Joyent, Inc. # Copyright 2022 Oxide Computer Company +# Copyright 2022 OmniOS Community Edition (OmniOSce) Association. # TESTSUBDIR = mevent @@ -19,7 +20,9 @@ PROG = \ lists_delete \ read_disable \ read_pause \ - read_requeue + read_requeue \ + vnode_file \ + vnode_zvol SUPOBJS = mevent.o testlib.o @@ -34,8 +37,12 @@ install: $(TESTDIR) $(CMDS) $(CMDS): $(PROG) +vnode_zvol := LDLIBS += -lzfs -lnvpair + include ../../Makefile.targ %: %.o $(SUPOBJS) $(LINK.c) -o $@ $< $(SUPOBJS) $(LDLIBS) $(POST_PROCESS) + +mevent.o: ../../../mevent.c diff --git a/usr/src/cmd/bhyve/test/tests/mevent/mevent.c b/usr/src/cmd/bhyve/test/tests/mevent/mevent.c index 971cf4aa77..51c94a4c09 100644 --- a/usr/src/cmd/bhyve/test/tests/mevent/mevent.c +++ b/usr/src/cmd/bhyve/test/tests/mevent/mevent.c @@ -55,3 +55,9 @@ test_mevent_count_lists(int *ret_global, int *ret_change, int *ret_del_pending) *ret_change = change; *ret_del_pending = del_pending; } + +void +set_mevent_file_poll_interval_ms(int ms) +{ + mevent_file_poll_interval_ms = ms; +} diff --git a/usr/src/cmd/bhyve/test/tests/mevent/testlib.h b/usr/src/cmd/bhyve/test/tests/mevent/testlib.h index 7e5ca2e9c9..1639f29f87 100644 --- a/usr/src/cmd/bhyve/test/tests/mevent/testlib.h +++ b/usr/src/cmd/bhyve/test/tests/mevent/testlib.h @@ -71,6 +71,7 @@ #define ASSERT_INT_EQ(msg, got, exp) ASSERT_CMP(msg, got, ==, exp, "%d") #define ASSERT_INT_NEQ(msg, got, exp) ASSERT_CMP(msg, got, !=, exp, "%d") #define ASSERT_INT64_EQ(msg, got, exp) ASSERT_CMP(msg, got, ==, exp, "%ld") +#define ASSERT_INT64_NEQ(msg, got, exp) ASSERT_CMP(msg, got, !=, exp, "%ld") #define ASSERT_PTR_EQ(msg, got, exp) ASSERT_CMP(msg, got, ==, exp, "%p") #define ASSERT_PTR_NEQ(msg, got, exp) ASSERT_CMP(msg, got, !=, exp, "%p") @@ -89,5 +90,6 @@ extern boolean_t testlib_verbose; extern void start_test(const char *, uint32_t); extern void start_event_thread(void); extern void test_mevent_count_lists(int *, int *, int *); +extern void set_mevent_file_poll_interval_ms(int); #endif /* _TESTLIB_H_ */ diff --git a/usr/src/cmd/bhyve/test/tests/mevent/vnode_file.c b/usr/src/cmd/bhyve/test/tests/mevent/vnode_file.c new file mode 100644 index 0000000000..850ac1be27 --- /dev/null +++ b/usr/src/cmd/bhyve/test/tests/mevent/vnode_file.c @@ -0,0 +1,141 @@ +/* + * 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 2018 Joyent, Inc. + * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. + */ + +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "testlib.h" +#include "mevent.h" + +static char *cookie = "Chocolate chip with fudge stripes"; + +static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t cv = PTHREAD_COND_INITIALIZER; + +static void +callback(int fd, enum ev_type ev, void *arg) +{ + static off_t size = 0; + struct stat st; + + ASSERT_INT_EQ(("bad event"), ev, EVF_VNODE); + ASSERT_PTR_EQ(("bad cookie"), arg, cookie); + + if (fstat(fd, &st) != 0) + FAIL_ERRNO("fstat failed"); + + ASSERT_INT64_NEQ(("File size has not changed"), size, st.st_size); + size = st.st_size; + + pthread_mutex_lock(&mtx); + pthread_cond_signal(&cv); + VERBOSE(("wakeup")); + pthread_mutex_unlock(&mtx); +} + +static void +test_fd(int fd, char *tag) +{ + struct mevent *evp; + int err; + + evp = mevent_add_flags(fd, EVF_VNODE, EVFF_ATTRIB, callback, cookie); + ASSERT_PTR_NEQ(("%s: mevent_add", tag), evp, NULL); + + for (uint_t i = 0; cookie[i] != '\0'; i++) { + ssize_t written; + + pthread_mutex_lock(&mtx); + + if (i > 0) { + /* + * Check that no events are emitted for writes which do + * not alter the size. + */ + if (lseek(fd, -1, SEEK_CUR) == -1) + FAIL_ERRNO("lseek"); + if (write(fd, "X", 1) == -1) + FAIL_ERRNO("write"); + } + + written = write(fd, cookie + i, 1); + if (written < 0) + FAIL_ERRNO("bad write"); + ASSERT_INT64_EQ(("write byte %d of cookie", i), written, 1); + + /* Wait for the size change to be processed */ + pthread_cond_wait(&cv, &mtx); + pthread_mutex_unlock(&mtx); + /* + * This is a bit unsatisfactory but we need to allow time + * for mevent to re-associate the port or the next write could + * be missed. + */ + usleep(500); + } + + err = mevent_disable(evp); + ASSERT_INT_EQ(("%s: mevent_disable: %s", tag, strerror(err)), err, 0); + + (void) printf("PASS %s - %s\n", testlib_prog, tag); +} + +int +main(int argc, const char **argv) +{ + start_test(argv[0], 5); + start_event_thread(); + int fd; + + /* Test with a temporary file in /tmp */ + char *template = strdup("/tmp/mevent.vnode.XXXXXX"); + ASSERT_PTR_NEQ(("strdup"), template, NULL); + fd = mkstemp(template); + if (fd == -1) + FAIL_ERRNO("Couldn't create temporary file with mkstemp"); + + VERBOSE(("Opened temporary file at '%s'", template)); + + test_fd(fd, "temporary file"); + + /* Test with a file which is unlinked from the filesystem */ + FILE *fp = tmpfile(); + ASSERT_PTR_NEQ(("tmpfile"), fp, NULL); + + fd = fileno(fp); + if (fd == -1) + FAIL_ERRNO("Couldn't get file descriptor for temporary file"); + + test_fd(fd, "anon file"); + + /* + * Defer to here to avoid generating a new event before the disable has + * been processed and the port deassociated. + */ + unlink(template); + free(template); + + PASS(); +} diff --git a/usr/src/cmd/bhyve/test/tests/mevent/vnode_zvol.c b/usr/src/cmd/bhyve/test/tests/mevent/vnode_zvol.c new file mode 100644 index 0000000000..8e99d7ba0d --- /dev/null +++ b/usr/src/cmd/bhyve/test/tests/mevent/vnode_zvol.c @@ -0,0 +1,259 @@ +/* + * 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 2018 Joyent, Inc. + * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. + */ + +#include <errno.h> +#include <fcntl.h> +#include <libzfs.h> +#include <pthread.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> +#include <zone.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "testlib.h" +#include "mevent.h" + +#define MB (1024 * 1024) + +static char *cookie = "Chocolate chip with fudge stripes"; + +static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t cv = PTHREAD_COND_INITIALIZER; + +static void +callback(int fd, enum ev_type ev, void *arg) +{ + static off_t size = 0; + struct stat st; + + ASSERT_INT_EQ(("bad event"), ev, EVF_VNODE); + ASSERT_PTR_EQ(("bad cookie"), arg, cookie); + + if (fstat(fd, &st) != 0) + FAIL_ERRNO("fstat failed"); + + ASSERT_INT64_NEQ(("Size has not changed"), size, st.st_size); + size = st.st_size; + + pthread_mutex_lock(&mtx); + pthread_cond_signal(&cv); + VERBOSE(("wakeup")); + pthread_mutex_unlock(&mtx); +} + +static void +destroy_zpool(libzfs_handle_t *zfshdl, zpool_handle_t *poolhdl, + zfs_handle_t *volhdl) +{ + if (volhdl != NULL) { + if (zfs_destroy(volhdl, B_FALSE) != 0) { + FAIL(("Failed to destroy ZVOL - %s", + libzfs_error_description(zfshdl))); + } + } + + if (poolhdl != NULL) { + if (zpool_destroy(poolhdl, testlib_prog) != 0) { + FAIL(("Failed to destroy ZPOOL - %s", + libzfs_error_description(zfshdl))); + } + } +} + +static void +create_zpool(libzfs_handle_t *zfshdl, const char *pool, const char *file) +{ + nvlist_t *nvroot, *props; + nvlist_t *vdevs[1]; + + nvroot = fnvlist_alloc(); + props = fnvlist_alloc(); + vdevs[0] = fnvlist_alloc(); + + fnvlist_add_string(vdevs[0], ZPOOL_CONFIG_PATH, file); + fnvlist_add_string(vdevs[0], ZPOOL_CONFIG_TYPE, VDEV_TYPE_FILE); + fnvlist_add_uint64(vdevs[0], ZPOOL_CONFIG_IS_LOG, 0); + + fnvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT); + fnvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, vdevs, 1); + + fnvlist_add_string(props, + zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), ZFS_MOUNTPOINT_NONE); + + if (zpool_create(zfshdl, pool, nvroot, NULL, props) != 0) { + FAIL(("Failed to create ZPOOL %s using %s - %s", + pool, file, libzfs_error_description(zfshdl))); + } + + VERBOSE(("Created ZFS pool %s", pool)); +} + +static bool +create_zvol(libzfs_handle_t *zfshdl, const char *vol) +{ + nvlist_t *volprops; + int err; + + volprops = fnvlist_alloc(); + fnvlist_add_uint64(volprops, + zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1 * MB); + + err = zfs_create(zfshdl, vol, ZFS_TYPE_VOLUME, volprops); + if (err != 0) { + (void) printf("Failed to create ZVOL %s - %s", + vol, libzfs_error_description(zfshdl)); + return (false); + } + + VERBOSE(("Created ZVOL %s", vol)); + return (true); +} + +int +main(int argc, const char **argv) +{ + libzfs_handle_t *zfshdl; + char *template, *pool, *vol, *backend; + struct mevent *evp; + zpool_handle_t *poolhdl = NULL; + zfs_handle_t *volhdl = NULL; + int err, fd; + + start_test(argv[0], 10); + set_mevent_file_poll_interval_ms(1000); + + if (getzoneid() != GLOBAL_ZONEID) + FAIL(("Can only be run in the global zone")); + + if ((zfshdl = libzfs_init()) == NULL) + FAIL_ERRNO("Could not open ZFS library"); + + template = strdup("/tmp/mevent.vnode.zvol.XXXXXX"); + ASSERT_PTR_NEQ(("strdup"), template, NULL); + fd = mkstemp(template); + if (fd == -1) + FAIL_ERRNO("Couldn't create temporary file with mkstemp"); + VERBOSE(("Opened temporary file at '%s'", template)); + + err = asprintf(&pool, "mevent_test_%d", getpid()); + ASSERT_INT_NEQ(("asprintf pool"), err, -1); + + err = asprintf(&vol, "%s/test_zvol_%d", pool, getpid()); + ASSERT_INT_NEQ(("asprintf vol"), err, -1); + + err = asprintf(&backend, "/dev/zvol/rdsk/%s", vol); + ASSERT_INT_NEQ(("asprintf backend"), err, -1); + + err = ftruncate(fd, 64 * MB); + if (err != 0) + FAIL_ERRNO("ftruncate"); + (void) close(fd); + fd = -1; + + /* + * Create the pool as late as possible to reduce the risk of leaving + * a test pool hanging around. + */ + create_zpool(zfshdl, pool, template); + + if ((poolhdl = zpool_open(zfshdl, pool)) == NULL) { + (void) printf("Could not open ZPOOL - %s\n", + libzfs_error_description(zfshdl)); + err = EXIT_FAIL; + goto out; + } + + if (!create_zvol(zfshdl, vol)) { + err = EXIT_FAIL; + goto out; + } + + if ((volhdl = zfs_open(zfshdl, vol, ZFS_TYPE_VOLUME)) == NULL) { + (void) printf("Could not open ZFS volume - %s\n", + libzfs_error_description(zfshdl)); + err = EXIT_FAIL; + goto out; + } + + if ((fd = open(backend, O_RDWR)) == -1) { + (void) printf("Failed to open '%s': %s\n", + backend, strerror(errno)); + err = EXIT_FAIL; + goto out; + } + VERBOSE(("Opened backend %s", backend)); + + start_event_thread(); + + evp = mevent_add_flags(fd, EVF_VNODE, EVFF_ATTRIB, callback, cookie); + if (evp == NULL) { + (void) printf("mevent_add returned NULL\n"); + err = EXIT_FAIL; + goto out; + } + + for (uint_t i = 2; i < 4; i++) { + ssize_t written; + char buf[64]; + + /* + * Check that a write to the volume does not trigger an event. + */ + if (lseek(fd, 0, SEEK_SET) == -1) + FAIL_ERRNO("lseek"); + written = write(fd, cookie, strlen(cookie)); + if (written < 0) + FAIL_ERRNO("bad write"); + ASSERT_INT64_EQ(("write cookie", i), written, strlen(cookie)); + + (void) snprintf(buf, sizeof (buf), "%llu", i * MB); + VERBOSE(("Setting volsize to %s", buf)); + + if (zfs_prop_set(volhdl, + zfs_prop_to_name(ZFS_PROP_VOLSIZE), buf) != 0) { + (void) printf("Failed to increase ZFS volume size\n"); + pthread_mutex_unlock(&mtx); + err = EXIT_FAIL; + goto out; + } + + /* Wait for the size change to be processed */ + pthread_mutex_lock(&mtx); + pthread_cond_wait(&cv, &mtx); + pthread_mutex_unlock(&mtx); + } + + (void) mevent_disable(evp); + + err = EXIT_PASS; + +out: + + (void) close(fd); + destroy_zpool(zfshdl, poolhdl, volhdl); + (void) libzfs_fini(zfshdl); + (void) unlink(template); + + if (err == EXIT_PASS) + PASS(); + + exit(err); +} diff --git a/usr/src/pkg/manifests/system-bhyve-tests.p5m b/usr/src/pkg/manifests/system-bhyve-tests.p5m index 74881cf19c..5b4a7351c4 100644 --- a/usr/src/pkg/manifests/system-bhyve-tests.p5m +++ b/usr/src/pkg/manifests/system-bhyve-tests.p5m @@ -14,7 +14,7 @@ # # -# Copyright 2018 OmniOS Community Edition (OmniOSce) Association. +# Copyright 2022 OmniOS Community Edition (OmniOSce) Association. # Copyright 2022 Oxide Computer Company # @@ -35,6 +35,8 @@ file path=opt/bhyve-tests/tests/mevent/lists_delete mode=0555 file path=opt/bhyve-tests/tests/mevent/read_disable mode=0555 file path=opt/bhyve-tests/tests/mevent/read_pause mode=0555 file path=opt/bhyve-tests/tests/mevent/read_requeue mode=0555 +file path=opt/bhyve-tests/tests/mevent/vnode_file mode=0555 +file path=opt/bhyve-tests/tests/mevent/vnode_zvol mode=0555 dir path=opt/bhyve-tests/tests/vmm file path=opt/bhyve-tests/tests/vmm/mem_partial mode=0555 file path=opt/bhyve-tests/tests/vmm/mem_seg_map mode=0555 diff --git a/usr/src/test/bhyve-tests/runfiles/default.run b/usr/src/test/bhyve-tests/runfiles/default.run index 51afd4581a..babfa0f7e9 100644 --- a/usr/src/test/bhyve-tests/runfiles/default.run +++ b/usr/src/test/bhyve-tests/runfiles/default.run @@ -24,4 +24,9 @@ tests = ['mem_partial', 'mem_seg_map'] # Tests of userspace mevent system, built from cmd/bhyve [/opt/bhyve-tests/tests/mevent] -tests = ['lists_delete', 'read_disable', 'read_pause', 'read_requeue'] +tests = ['lists_delete', 'read_disable', 'read_pause', 'read_requeue', + 'vnode_file'] + +[/opt/bhyve-tests/tests/mevent/vnode_zvol] +user = root + |