Path: blob/main/tests/sys/fs/fusefs/dev_fuse_poll.cc
39537 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2019 The FreeBSD Foundation4*5* This software was developed by BFF Storage Systems, LLC under sponsorship6* from the FreeBSD Foundation.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*/2930/*31* This file tests different polling methods for the /dev/fuse device32*/3334extern "C" {35#include <fcntl.h>36#include <semaphore.h>37#include <unistd.h>38}3940#include "mockfs.hh"41#include "utils.hh"4243using namespace testing;4445const char FULLPATH[] = "mountpoint/some_file.txt";46const char RELPATH[] = "some_file.txt";47const uint64_t ino = 42;48const mode_t access_mode = R_OK;4950/*51* Translate a poll method's string representation to the enum value.52* Using strings with ::testing::Values gives better output with53* --gtest_list_tests54*/55enum poll_method poll_method_from_string(const char *s)56{57if (0 == strcmp("BLOCKING", s))58return BLOCKING;59else if (0 == strcmp("KQ", s))60return KQ;61else if (0 == strcmp("POLL", s))62return POLL;63else64return SELECT;65}6667class DevFusePoll: public FuseTest, public WithParamInterface<const char *> {68virtual void SetUp() {69m_pm = poll_method_from_string(GetParam());70FuseTest::SetUp();71}72};7374class Kqueue: public FuseTest {75virtual void SetUp() {76m_pm = KQ;77FuseTest::SetUp();78}79};8081TEST_P(DevFusePoll, access)82{83expect_access(FUSE_ROOT_ID, X_OK, 0);84expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);85expect_access(ino, access_mode, 0);8687ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);88}8990/* Ensure that we wake up pollers during unmount */91TEST_P(DevFusePoll, destroy)92{93expect_destroy(0);9495m_mock->unmount();96}9798INSTANTIATE_TEST_SUITE_P(PM, DevFusePoll,99::testing::Values("BLOCKING", "KQ", "POLL", "SELECT"));100101static void* statter(void* arg) {102const char *name;103struct stat sb;104105name = (const char*)arg;106return ((void*)(intptr_t)stat(name, &sb));107}108109/*110* A kevent's data field should contain the number of operations available to111* be immediately read.112*/113TEST_F(Kqueue, data)114{115pthread_t th0, th1, th2;116sem_t sem0, sem1;117int nready0, nready1, nready2;118uint64_t foo_ino = 42;119uint64_t bar_ino = 43;120uint64_t baz_ino = 44;121Sequence seq;122void *th_ret;123124ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);125ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);126127EXPECT_LOOKUP(FUSE_ROOT_ID, "foo")128.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {129SET_OUT_HEADER_LEN(out, entry);130out.body.entry.entry_valid = UINT64_MAX;131out.body.entry.attr.mode = S_IFREG | 0644;132out.body.entry.nodeid = foo_ino;133})));134EXPECT_LOOKUP(FUSE_ROOT_ID, "bar")135.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {136SET_OUT_HEADER_LEN(out, entry);137out.body.entry.entry_valid = UINT64_MAX;138out.body.entry.attr.mode = S_IFREG | 0644;139out.body.entry.nodeid = bar_ino;140})));141EXPECT_LOOKUP(FUSE_ROOT_ID, "baz")142.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {143SET_OUT_HEADER_LEN(out, entry);144out.body.entry.entry_valid = UINT64_MAX;145out.body.entry.attr.mode = S_IFREG | 0644;146out.body.entry.nodeid = baz_ino;147})));148149EXPECT_CALL(*m_mock, process(150ResultOf([=](auto in) {151return (in.header.opcode == FUSE_GETATTR &&152in.header.nodeid == foo_ino);153}, Eq(true)),154_)155)156.WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {157nready0 = m_mock->m_nready;158159sem_post(&sem0);160// Block the daemon so we can accumulate a few more ops161sem_wait(&sem1);162163out.header.unique = in.header.unique;164out.header.error = -EIO;165out.header.len = sizeof(out.header);166})));167168EXPECT_CALL(*m_mock, process(169ResultOf([=](auto in) {170return (in.header.opcode == FUSE_GETATTR &&171(in.header.nodeid == bar_ino ||172in.header.nodeid == baz_ino));173}, Eq(true)),174_)175).InSequence(seq)176.WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {177nready1 = m_mock->m_nready;178out.header.unique = in.header.unique;179out.header.error = -EIO;180out.header.len = sizeof(out.header);181})));182EXPECT_CALL(*m_mock, process(183ResultOf([=](auto in) {184return (in.header.opcode == FUSE_GETATTR &&185(in.header.nodeid == bar_ino ||186in.header.nodeid == baz_ino));187}, Eq(true)),188_)189).InSequence(seq)190.WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {191nready2 = m_mock->m_nready;192out.header.unique = in.header.unique;193out.header.error = -EIO;194out.header.len = sizeof(out.header);195})));196197/*198* Create cached lookup entries for these files. It seems that only199* one thread at a time can be in VOP_LOOKUP for a given directory200*/201access("mountpoint/foo", F_OK);202access("mountpoint/bar", F_OK);203access("mountpoint/baz", F_OK);204ASSERT_EQ(0, pthread_create(&th0, NULL, statter,205__DECONST(void*, "mountpoint/foo"))) << strerror(errno);206EXPECT_EQ(0, sem_wait(&sem0)) << strerror(errno);207ASSERT_EQ(0, pthread_create(&th1, NULL, statter,208__DECONST(void*, "mountpoint/bar"))) << strerror(errno);209ASSERT_EQ(0, pthread_create(&th2, NULL, statter,210__DECONST(void*, "mountpoint/baz"))) << strerror(errno);211212nap(); // Allow th1 and th2 to send their ops to the daemon213EXPECT_EQ(0, sem_post(&sem1)) << strerror(errno);214215pthread_join(th0, &th_ret);216ASSERT_EQ(-1, (intptr_t)th_ret);217pthread_join(th1, &th_ret);218ASSERT_EQ(-1, (intptr_t)th_ret);219pthread_join(th2, &th_ret);220ASSERT_EQ(-1, (intptr_t)th_ret);221222EXPECT_EQ(1, nready0);223EXPECT_EQ(2, nready1);224EXPECT_EQ(1, nready2);225226sem_destroy(&sem0);227sem_destroy(&sem1);228}229230231