summaryrefslogtreecommitdiff
path: root/tests/kernel
diff options
context:
space:
mode:
authorthorpej <thorpej@NetBSD.org>2021-10-20 03:08:16 +0000
committerthorpej <thorpej@NetBSD.org>2021-10-20 03:08:16 +0000
commit8abbca48b237c9edc3ffb387e9074ddcff8b645d (patch)
tree41861906d64ee2ea60ac288b69c2b14012886108 /tests/kernel
parent6ddcd84a3618d73c6acd9eb1ba9c92a563467245 (diff)
Overhaul of the EVFILT_VNODE kevent(2) filter:
- Centralize vnode kevent handling in the VOP_*() wrappers, rather than forcing each individual file system to deal with it (except VOP_RENAME(), because VOP_RENAME() is a mess and we currently have 2 different ways of handling it; at least it's reasonably well-centralized in the "new" way). - Add support for NOTE_OPEN, NOTE_CLOSE, NOTE_CLOSE_WRITE, and NOTE_READ, compatible with the same events in FreeBSD. - Track which kevent notifications clients are interested in receiving to avoid doing work for events no one cares about (avoiding, e.g. taking locks and traversing the klist to send a NOTE_WRITE when someone is merely watching for a file to be deleted, for example). In support of the above: - Add support in vnode_if.sh for specifying PRE- and POST-op handlers, to be invoked before and after vop_pre() and vop_post(), respectively. Basic idea from FreeBSD, but implemented differently. - Add support in vnode_if.sh for specifying CONTEXT fields in the vop_*_args structures. These context fields are used to convey information between the file system VOP function and the VOP wrapper, but do not occupy an argument slot in the VOP_*() call itself. These context fields are initialized and subsequently interpreted by PRE- and POST-op handlers. - Version VOP_REMOVE(), uses the a context field for the file system to report back the resulting link count of the target vnode. Return this in tmpfs, udf, nfs, chfs, ext2fs, lfs, and ufs. NetBSD 9.99.92.
Diffstat (limited to 'tests/kernel')
-rw-r--r--tests/kernel/kqueue/t_vnode.c173
1 files changed, 173 insertions, 0 deletions
diff --git a/tests/kernel/kqueue/t_vnode.c b/tests/kernel/kqueue/t_vnode.c
index e87c2b2c2d6..b3a95cc8e93 100644
--- a/tests/kernel/kqueue/t_vnode.c
+++ b/tests/kernel/kqueue/t_vnode.c
@@ -509,25 +509,198 @@ ATF_TC_CLEANUP(dir_note_write_mv_file_within, tc)
cleanup();
}
+static const char testfile[] = "testfile";
+
+ATF_TC_WITH_CLEANUP(open_write_read_close);
+ATF_TC_HEAD(open_write_read_close, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "This test case exercises "
+ "that kevent(2) returns NOTE_OPEN, NOTE_READ, NOTE_WRITE, "
+ "NOTE_CLOSE, and NOTE_CLOSE_WRITE.");
+}
+ATF_TC_BODY(open_write_read_close, tc)
+{
+ struct kevent event[1];
+ char buf[sizeof(testfile)];
+ int fd;
+
+ ATF_REQUIRE((kq = kqueue()) != -1);
+
+ /*
+ * Create the test file and register an event on it. We need
+ * to keep the fd open to keep receiving events, so we'll just
+ * leak it and re-use the fd variable.
+ */
+ ATF_REQUIRE((fd = open(testfile,
+ O_RDWR | O_CREAT | O_TRUNC, 0600)) != -1);
+ EV_SET(&event[0], fd, EVFILT_VNODE, EV_ADD | EV_CLEAR,
+ NOTE_OPEN | NOTE_READ | NOTE_WRITE |
+ NOTE_CLOSE | NOTE_CLOSE_WRITE, 0, NULL);
+ ATF_REQUIRE(kevent(kq, event, 1, NULL, 0, NULL) == 0);
+
+ /*
+ * Open the file for writing, check for NOTE_OPEN.
+ * Write to the file, check for NOTE_WRITE | NOTE_EXTEND.
+ * Re-write the file, check for NOTE_WRITE and !NOTE_EXTEND.
+ * Write one additional byte, check for NOTE_WRITE | NOTE_EXTEND.
+ * Close the file, check for NOTE_CLOSE_WRITE.
+ */
+ ATF_REQUIRE((fd = open(testfile, O_RDWR)) != -1);
+ ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1);
+ ATF_REQUIRE(event[0].fflags & NOTE_OPEN);
+
+ ATF_REQUIRE((pwrite(fd, testfile,
+ sizeof(testfile), 0)) == sizeof(testfile));
+ ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1);
+ ATF_REQUIRE(event[0].fflags & NOTE_WRITE);
+ ATF_REQUIRE(event[0].fflags & NOTE_EXTEND);
+
+ ATF_REQUIRE((pwrite(fd, testfile,
+ sizeof(testfile), 0)) == sizeof(testfile));
+ ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1);
+ ATF_REQUIRE(event[0].fflags & NOTE_WRITE);
+ ATF_REQUIRE((event[0].fflags & NOTE_EXTEND) == 0);
+
+ ATF_REQUIRE((pwrite(fd, testfile,
+ 1, sizeof(testfile))) == 1);
+ ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1);
+ ATF_REQUIRE(event[0].fflags & NOTE_WRITE);
+ ATF_REQUIRE(event[0].fflags & NOTE_EXTEND);
+
+ (void)close(fd);
+ ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1);
+ ATF_REQUIRE(event[0].fflags & NOTE_CLOSE_WRITE);
+ ATF_REQUIRE((event[0].fflags & NOTE_CLOSE) == 0);
+
+ /*
+ * Open the file for reading, check for NOTE_OPEN.
+ * Read from the file, check for NOTE_READ.
+ * Close the file., check for NOTE_CLOSE.
+ */
+ ATF_REQUIRE((fd = open(testfile, O_RDONLY)) != -1);
+ ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1);
+ ATF_REQUIRE(event[0].fflags & NOTE_OPEN);
+
+ ATF_REQUIRE((read(fd, buf, sizeof(buf))) == sizeof(buf));
+ ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1);
+ ATF_REQUIRE(event[0].fflags & NOTE_READ);
+
+ (void)close(fd);
+ ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1);
+ ATF_REQUIRE(event[0].fflags & NOTE_CLOSE);
+ ATF_REQUIRE((event[0].fflags & NOTE_CLOSE_WRITE) == 0);
+}
+ATF_TC_CLEANUP(open_write_read_close, tc)
+{
+ (void)unlink(testfile);
+}
+
+ATF_TC_WITH_CLEANUP(interest);
+ATF_TC_HEAD(interest, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "This test case exercises "
+ "the kernel code that computes vnode kevent interest");
+}
+ATF_TC_BODY(interest, tc)
+{
+ struct kevent event[3];
+ int open_ev_fd, write_ev_fd, close_ev_fd;
+ int fd;
+
+ /*
+ * This test cases exercises some implementation details
+ * regarding how "kevent interest" is computed for a vnode.
+ *
+ * We are going to add events, one at a time, in a specific
+ * order, and then remove one of them, with the knowledge that
+ * a specific code path in vfs_vnops.c:vn_knote_detach() will
+ * be taken. There are several KASSERT()s in this code path
+ * that will be validated.
+ *
+ * In order to ensure distinct knotes are attached to the vnodes,
+ * we must use a different file descriptor to register interest
+ * in each kind of event.
+ */
+
+ ATF_REQUIRE((kq = kqueue()) != -1);
+
+ /*
+ * Create the test file and register an event on it. We need
+ * to keep the fd open to keep receiving events, so we'll just
+ * leak it and re-use the fd variable.
+ */
+ ATF_REQUIRE((open_ev_fd = open(testfile,
+ O_RDWR | O_CREAT | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE((write_ev_fd = dup(open_ev_fd)) != -1);
+ ATF_REQUIRE((close_ev_fd = dup(open_ev_fd)) != -1);
+
+ EV_SET(&event[0], open_ev_fd, EVFILT_VNODE, EV_ADD | EV_CLEAR,
+ NOTE_OPEN, 0, NULL);
+ EV_SET(&event[1], write_ev_fd, EVFILT_VNODE, EV_ADD | EV_CLEAR,
+ NOTE_WRITE, 0, NULL);
+ EV_SET(&event[2], close_ev_fd, EVFILT_VNODE, EV_ADD | EV_CLEAR,
+ NOTE_CLOSE | NOTE_CLOSE_WRITE, 0, NULL);
+ ATF_REQUIRE(kevent(kq, event, 3, NULL, 0, NULL) == 0);
+
+ /*
+ * The testfile vnode now has 3 knotes attached, in "LIFO"
+ * order:
+ *
+ * NOTE_CLOSE -> NOTE_WRITE -> NOTE_OPEN
+ *
+ * We will now remove the NOTE_WRITE knote.
+ */
+ (void)close(write_ev_fd);
+
+ ATF_REQUIRE((fd = open(testfile, O_RDWR)) != -1);
+ ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1);
+ ATF_REQUIRE(event[0].fflags & NOTE_OPEN);
+
+ ATF_REQUIRE((pwrite(fd, testfile,
+ sizeof(testfile), 0)) == sizeof(testfile));
+ ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 0);
+
+ (void)close(fd);
+ ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1);
+ ATF_REQUIRE(event[0].fflags & NOTE_CLOSE_WRITE);
+ ATF_REQUIRE((event[0].fflags & NOTE_CLOSE) == 0);
+
+}
+ATF_TC_CLEANUP(interest, tc)
+{
+ (void)unlink(testfile);
+}
+
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, dir_no_note_link_create_file_in);
ATF_TP_ADD_TC(tp, dir_no_note_link_delete_file_in);
+
ATF_TP_ADD_TC(tp, dir_no_note_link_mv_dir_within);
ATF_TP_ADD_TC(tp, dir_no_note_link_mv_file_within);
+
ATF_TP_ADD_TC(tp, dir_note_link_create_dir_in);
ATF_TP_ADD_TC(tp, dir_note_link_delete_dir_in);
+
ATF_TP_ADD_TC(tp, dir_note_link_mv_dir_in);
ATF_TP_ADD_TC(tp, dir_note_link_mv_dir_out);
+
ATF_TP_ADD_TC(tp, dir_note_write_create_dir_in);
ATF_TP_ADD_TC(tp, dir_note_write_create_file_in);
+
ATF_TP_ADD_TC(tp, dir_note_write_delete_dir_in);
ATF_TP_ADD_TC(tp, dir_note_write_delete_file_in);
+
ATF_TP_ADD_TC(tp, dir_note_write_mv_dir_in);
ATF_TP_ADD_TC(tp, dir_note_write_mv_dir_out);
ATF_TP_ADD_TC(tp, dir_note_write_mv_dir_within);
ATF_TP_ADD_TC(tp, dir_note_write_mv_file_in);
ATF_TP_ADD_TC(tp, dir_note_write_mv_file_out);
ATF_TP_ADD_TC(tp, dir_note_write_mv_file_within);
+
+ ATF_TP_ADD_TC(tp, open_write_read_close);
+ ATF_TP_ADD_TC(tp, interest);
+
return atf_no_error();
}