Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 6 Jun 2019 16:29:08 +0000 (UTC)
From:      Alan Somers <asomers@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r348750 - projects/fuse2/tests/sys/fs/fusefs
Message-ID:  <201906061629.x56GT826077017@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: asomers
Date: Thu Jun  6 16:29:08 2019
New Revision: 348750
URL: https://svnweb.freebsd.org/changeset/base/348750

Log:
  fusefs: add some explicit tests for FUSE_FORGET
  
  Sponsored by:	The FreeBSD Foundation

Added:
  projects/fuse2/tests/sys/fs/fusefs/forget.cc   (contents, props changed)
Modified:
  projects/fuse2/tests/sys/fs/fusefs/Makefile
  projects/fuse2/tests/sys/fs/fusefs/destroy.cc
  projects/fuse2/tests/sys/fs/fusefs/utils.cc
  projects/fuse2/tests/sys/fs/fusefs/utils.hh

Modified: projects/fuse2/tests/sys/fs/fusefs/Makefile
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/Makefile	Thu Jun  6 16:28:34 2019	(r348749)
+++ projects/fuse2/tests/sys/fs/fusefs/Makefile	Thu Jun  6 16:29:08 2019	(r348750)
@@ -16,6 +16,7 @@ GTESTS+=	destroy
 GTESTS+=	dev_fuse_poll
 GTESTS+=	fifo
 GTESTS+=	flush
+GTESTS+=	forget
 GTESTS+=	fsync
 GTESTS+=	fsyncdir
 GTESTS+=	getattr

Modified: projects/fuse2/tests/sys/fs/fusefs/destroy.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/destroy.cc	Thu Jun  6 16:28:34 2019	(r348749)
+++ projects/fuse2/tests/sys/fs/fusefs/destroy.cc	Thu Jun  6 16:29:08 2019	(r348750)
@@ -37,9 +37,7 @@ class Destroy: public FuseTest {};
 
 /*
  * On unmount the kernel should send a FUSE_DESTROY operation.  It should also
- * send FUSE_FORGET operations for all inodes with lookup_count > 0.  It's hard
- * to trigger FUSE_FORGET in any way except by unmounting, so this is the only
- * testing that FUSE_FORGET gets.
+ * send FUSE_FORGET operations for all inodes with lookup_count > 0.
  */
 TEST_F(Destroy, ok)
 {

Added: projects/fuse2/tests/sys/fs/fusefs/forget.cc
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/fuse2/tests/sys/fs/fusefs/forget.cc	Thu Jun  6 16:29:08 2019	(r348750)
@@ -0,0 +1,153 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <fcntl.h>
+#include <semaphore.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+const char reclaim_mib[] = "debug.try_reclaim_vnode";
+
+class Forget: public FuseTest {
+public:
+void SetUp() {
+	if (geteuid() != 0)
+		GTEST_SKIP() << "Only root may use " << reclaim_mib;
+
+	if (-1 == sysctlbyname(reclaim_mib, NULL, 0, NULL, 0) &&
+	    errno == ENOENT)
+		GTEST_SKIP() << reclaim_mib << " is not available";
+
+	FuseTest::SetUp();
+}
+
+};
+
+/*
+ * When a fusefs vnode is reclaimed, it should send a FUSE_FORGET operation.
+ */
+TEST_F(Forget, ok)
+{
+	const char FULLPATH[] = "mountpoint/some_file.txt";
+	const char RELPATH[] = "some_file.txt";
+	uint64_t ino = 42;
+	mode_t mode = S_IFREG | 0755;
+	sem_t sem;
+	int err;
+
+	ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+	.Times(3)
+	.WillRepeatedly(Invoke(
+		ReturnImmediate([=](auto in __unused, auto& out) {
+		SET_OUT_HEADER_LEN(out, entry);
+		out.body.entry.attr.mode = mode;
+		out.body.entry.nodeid = ino;
+		out.body.entry.attr.nlink = 1;
+		out.body.entry.attr_valid = UINT64_MAX;
+	})));
+	expect_forget(ino, 3, &sem);
+
+	/*
+	 * access(2) the file to force a lookup.  Access it twice to double its
+	 * lookup count.
+	 */
+	ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+	ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+
+	err = sysctlbyname(reclaim_mib, NULL, 0, FULLPATH, sizeof(FULLPATH));
+	ASSERT_EQ(0, err) << strerror(errno);
+
+	sem_wait(&sem);
+}
+
+/*
+ * When a directory is reclaimed, the names of its entries vanish from the
+ * namecache
+ */
+TEST_F(Forget, invalidate_names)
+{
+	const char FULLFPATH[] = "mountpoint/some_dir/some_file.txt";
+	const char FULLDPATH[] = "mountpoint/some_dir";
+	const char DNAME[] = "some_dir";
+	const char FNAME[] = "some_file.txt";
+	uint64_t dir_ino = 42;
+	uint64_t file_ino = 43;
+	int err;
+
+	EXPECT_LOOKUP(FUSE_ROOT_ID, DNAME)
+	.WillRepeatedly(Invoke(
+		ReturnImmediate([=](auto in __unused, auto& out) {
+		SET_OUT_HEADER_LEN(out, entry);
+		out.body.entry.attr.mode = S_IFDIR | 0755;
+		out.body.entry.nodeid = dir_ino;
+		out.body.entry.attr.nlink = 2;
+		out.body.entry.attr_valid = UINT64_MAX;
+		out.body.entry.entry_valid = UINT64_MAX;
+	})));
+
+	/* 
+	 * Even though we don't reclaim FNAME and its entry is cacheable, we
+	 * should get two lookups because the reclaim of DNAME will invalidate
+	 * the cached FNAME entry.
+	 */
+	EXPECT_LOOKUP(dir_ino, FNAME)
+	.Times(2)
+	.WillRepeatedly(Invoke(
+		ReturnImmediate([=](auto in __unused, auto& out) {
+		SET_OUT_HEADER_LEN(out, entry);
+		out.body.entry.attr.mode = S_IFREG | 0644;
+		out.body.entry.nodeid = file_ino;
+		out.body.entry.attr.nlink = 1;
+		out.body.entry.attr_valid = UINT64_MAX;
+		out.body.entry.entry_valid = UINT64_MAX;
+	})));
+	expect_forget(dir_ino, 2);
+
+	/* Access the file to cache its name */
+	ASSERT_EQ(0, access(FULLFPATH, F_OK)) << strerror(errno);
+	
+	/* Reclaim the directory, invalidating its children from namecache */
+	err = sysctlbyname(reclaim_mib, NULL, 0, FULLDPATH, sizeof(FULLDPATH));
+	ASSERT_EQ(0, err) << strerror(errno);
+
+	/* Access the file again, causing another lookup */
+	ASSERT_EQ(0, access(FULLFPATH, F_OK)) << strerror(errno);
+}

Modified: projects/fuse2/tests/sys/fs/fusefs/utils.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/utils.cc	Thu Jun  6 16:28:34 2019	(r348749)
+++ projects/fuse2/tests/sys/fs/fusefs/utils.cc	Thu Jun  6 16:29:08 2019	(r348750)
@@ -162,7 +162,7 @@ FuseTest::expect_flush(uint64_t ino, int times, Proces
 }
 
 void
-FuseTest::expect_forget(uint64_t ino, uint64_t nlookup)
+FuseTest::expect_forget(uint64_t ino, uint64_t nlookup, sem_t *sem)
 {
 	EXPECT_CALL(*m_mock, process(
 		ResultOf([=](auto in) {
@@ -171,7 +171,9 @@ FuseTest::expect_forget(uint64_t ino, uint64_t nlookup
 				in.body.forget.nlookup == nlookup);
 		}, Eq(true)),
 		_)
-	).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
+	).WillOnce(Invoke([=](auto in __unused, auto &out __unused) {
+		if (sem != NULL)
+			sem_post(sem);
 		/* FUSE_FORGET has no response! */
 	}));
 }

Modified: projects/fuse2/tests/sys/fs/fusefs/utils.hh
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/utils.hh	Thu Jun  6 16:28:34 2019	(r348749)
+++ projects/fuse2/tests/sys/fs/fusefs/utils.hh	Thu Jun  6 16:29:08 2019	(r348750)
@@ -28,6 +28,9 @@
  * SUCH DAMAGE.
  */
 
+struct _sem;
+typedef struct _sem sem_t;
+
 /* Nanoseconds to sleep, for tests that must */
 #define NAP_NS	(100'000'000)
 
@@ -92,9 +95,10 @@ class FuseTest : public ::testing::Test {
 
 	/*
 	 * Create an expectation that FUSE_FORGET will be called for the given
-	 * inode.  There will be no response
+	 * inode.  There will be no response.  If sem is provided, it will be
+	 * posted after the operation is received by the daemon.
 	 */
-	void expect_forget(uint64_t ino, uint64_t nlookup);
+	void expect_forget(uint64_t ino, uint64_t nlookup, sem_t *sem = NULL);
 
 	/*
 	 * Create an expectation that FUSE_GETATTR will be called for the given



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201906061629.x56GT826077017>