13429092cSAlan Somers /*- 23429092cSAlan Somers * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 33429092cSAlan Somers * 43429092cSAlan Somers * Copyright (c) 2019 The FreeBSD Foundation 53429092cSAlan Somers * 63429092cSAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship 73429092cSAlan Somers * from the FreeBSD Foundation. 83429092cSAlan Somers * 93429092cSAlan Somers * Redistribution and use in source and binary forms, with or without 103429092cSAlan Somers * modification, are permitted provided that the following conditions 113429092cSAlan Somers * are met: 123429092cSAlan Somers * 1. Redistributions of source code must retain the above copyright 133429092cSAlan Somers * notice, this list of conditions and the following disclaimer. 143429092cSAlan Somers * 2. Redistributions in binary form must reproduce the above copyright 153429092cSAlan Somers * notice, this list of conditions and the following disclaimer in the 163429092cSAlan Somers * documentation and/or other materials provided with the distribution. 173429092cSAlan Somers * 183429092cSAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 193429092cSAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 203429092cSAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 213429092cSAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 223429092cSAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 233429092cSAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 243429092cSAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 253429092cSAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 263429092cSAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 273429092cSAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 283429092cSAlan Somers * SUCH DAMAGE. 293429092cSAlan Somers */ 303429092cSAlan Somers 313429092cSAlan Somers /* 323429092cSAlan Somers * This file tests different polling methods for the /dev/fuse device 333429092cSAlan Somers */ 343429092cSAlan Somers 353429092cSAlan Somers extern "C" { 363429092cSAlan Somers #include <fcntl.h> 37*0a7c63e0SAlan Somers #include <semaphore.h> 383429092cSAlan Somers #include <unistd.h> 393429092cSAlan Somers } 403429092cSAlan Somers 413429092cSAlan Somers #include "mockfs.hh" 423429092cSAlan Somers #include "utils.hh" 433429092cSAlan Somers 443429092cSAlan Somers using namespace testing; 453429092cSAlan Somers 463429092cSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 473429092cSAlan Somers const char RELPATH[] = "some_file.txt"; 483429092cSAlan Somers const uint64_t ino = 42; 493429092cSAlan Somers const mode_t access_mode = R_OK; 503429092cSAlan Somers 513429092cSAlan Somers /* 523429092cSAlan Somers * Translate a poll method's string representation to the enum value. 533429092cSAlan Somers * Using strings with ::testing::Values gives better output with 543429092cSAlan Somers * --gtest_list_tests 553429092cSAlan Somers */ 563429092cSAlan Somers enum poll_method poll_method_from_string(const char *s) 573429092cSAlan Somers { 583429092cSAlan Somers if (0 == strcmp("BLOCKING", s)) 593429092cSAlan Somers return BLOCKING; 603429092cSAlan Somers else if (0 == strcmp("KQ", s)) 613429092cSAlan Somers return KQ; 623429092cSAlan Somers else if (0 == strcmp("POLL", s)) 633429092cSAlan Somers return POLL; 643429092cSAlan Somers else 653429092cSAlan Somers return SELECT; 663429092cSAlan Somers } 673429092cSAlan Somers 683429092cSAlan Somers class DevFusePoll: public FuseTest, public WithParamInterface<const char *> { 693429092cSAlan Somers virtual void SetUp() { 703429092cSAlan Somers m_pm = poll_method_from_string(GetParam()); 713429092cSAlan Somers FuseTest::SetUp(); 723429092cSAlan Somers } 733429092cSAlan Somers }; 743429092cSAlan Somers 75*0a7c63e0SAlan Somers class Kqueue: public FuseTest { 76*0a7c63e0SAlan Somers virtual void SetUp() { 77*0a7c63e0SAlan Somers m_pm = KQ; 78*0a7c63e0SAlan Somers FuseTest::SetUp(); 79*0a7c63e0SAlan Somers } 80*0a7c63e0SAlan Somers }; 81*0a7c63e0SAlan Somers 823429092cSAlan Somers TEST_P(DevFusePoll, access) 833429092cSAlan Somers { 843429092cSAlan Somers expect_access(1, X_OK, 0); 853429092cSAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 863429092cSAlan Somers expect_access(ino, access_mode, 0); 873429092cSAlan Somers 883429092cSAlan Somers ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno); 893429092cSAlan Somers } 903429092cSAlan Somers 913429092cSAlan Somers /* Ensure that we wake up pollers during unmount */ 923429092cSAlan Somers TEST_P(DevFusePoll, destroy) 933429092cSAlan Somers { 943429092cSAlan Somers expect_forget(1, 1); 953429092cSAlan Somers expect_destroy(0); 963429092cSAlan Somers 973429092cSAlan Somers m_mock->unmount(); 983429092cSAlan Somers } 993429092cSAlan Somers 1003429092cSAlan Somers INSTANTIATE_TEST_CASE_P(PM, DevFusePoll, 1013429092cSAlan Somers ::testing::Values("BLOCKING", "KQ", "POLL", "SELECT")); 102*0a7c63e0SAlan Somers 103*0a7c63e0SAlan Somers static void* statter(void* arg) { 104*0a7c63e0SAlan Somers const char *name; 105*0a7c63e0SAlan Somers struct stat sb; 106*0a7c63e0SAlan Somers 107*0a7c63e0SAlan Somers name = (const char*)arg; 108*0a7c63e0SAlan Somers stat(name, &sb); 109*0a7c63e0SAlan Somers return 0; 110*0a7c63e0SAlan Somers } 111*0a7c63e0SAlan Somers 112*0a7c63e0SAlan Somers /* 113*0a7c63e0SAlan Somers * A kevent's data field should contain the number of operations available to 114*0a7c63e0SAlan Somers * be immediately rea. 115*0a7c63e0SAlan Somers */ 116*0a7c63e0SAlan Somers TEST_F(Kqueue, data) 117*0a7c63e0SAlan Somers { 118*0a7c63e0SAlan Somers pthread_t th0, th1, th2; 119*0a7c63e0SAlan Somers sem_t sem0, sem1; 120*0a7c63e0SAlan Somers int nready0, nready1, nready2; 121*0a7c63e0SAlan Somers uint64_t foo_ino = 42; 122*0a7c63e0SAlan Somers uint64_t bar_ino = 43; 123*0a7c63e0SAlan Somers uint64_t baz_ino = 44; 124*0a7c63e0SAlan Somers 125*0a7c63e0SAlan Somers ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno); 126*0a7c63e0SAlan Somers ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno); 127*0a7c63e0SAlan Somers 128*0a7c63e0SAlan Somers EXPECT_LOOKUP(1, "foo") 129*0a7c63e0SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { 130*0a7c63e0SAlan Somers SET_OUT_HEADER_LEN(out, entry); 131*0a7c63e0SAlan Somers out->body.entry.entry_valid = UINT64_MAX; 132*0a7c63e0SAlan Somers out->body.entry.attr.mode = S_IFREG | 0644; 133*0a7c63e0SAlan Somers out->body.entry.nodeid = foo_ino; 134*0a7c63e0SAlan Somers }))); 135*0a7c63e0SAlan Somers EXPECT_LOOKUP(1, "bar") 136*0a7c63e0SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { 137*0a7c63e0SAlan Somers SET_OUT_HEADER_LEN(out, entry); 138*0a7c63e0SAlan Somers out->body.entry.entry_valid = UINT64_MAX; 139*0a7c63e0SAlan Somers out->body.entry.attr.mode = S_IFREG | 0644; 140*0a7c63e0SAlan Somers out->body.entry.nodeid = bar_ino; 141*0a7c63e0SAlan Somers }))); 142*0a7c63e0SAlan Somers EXPECT_LOOKUP(1, "baz") 143*0a7c63e0SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { 144*0a7c63e0SAlan Somers SET_OUT_HEADER_LEN(out, entry); 145*0a7c63e0SAlan Somers out->body.entry.entry_valid = UINT64_MAX; 146*0a7c63e0SAlan Somers out->body.entry.attr.mode = S_IFREG | 0644; 147*0a7c63e0SAlan Somers out->body.entry.nodeid = baz_ino; 148*0a7c63e0SAlan Somers }))); 149*0a7c63e0SAlan Somers 150*0a7c63e0SAlan Somers EXPECT_CALL(*m_mock, process( 151*0a7c63e0SAlan Somers ResultOf([=](auto in) { 152*0a7c63e0SAlan Somers return (in->header.opcode == FUSE_GETATTR && 153*0a7c63e0SAlan Somers in->header.nodeid == foo_ino); 154*0a7c63e0SAlan Somers }, Eq(true)), 155*0a7c63e0SAlan Somers _) 156*0a7c63e0SAlan Somers ) 157*0a7c63e0SAlan Somers .WillOnce(Invoke(ReturnImmediate([&](auto in, auto out) { 158*0a7c63e0SAlan Somers nready0 = m_mock->m_nready; 159*0a7c63e0SAlan Somers 160*0a7c63e0SAlan Somers sem_post(&sem0); 161*0a7c63e0SAlan Somers // Block the daemon so we can accumulate a few more ops 162*0a7c63e0SAlan Somers sem_wait(&sem1); 163*0a7c63e0SAlan Somers 164*0a7c63e0SAlan Somers out->header.unique = in->header.unique; 165*0a7c63e0SAlan Somers out->header.error = -EIO; 166*0a7c63e0SAlan Somers out->header.len = sizeof(out->header); 167*0a7c63e0SAlan Somers }))); 168*0a7c63e0SAlan Somers 169*0a7c63e0SAlan Somers EXPECT_CALL(*m_mock, process( 170*0a7c63e0SAlan Somers ResultOf([=](auto in) { 171*0a7c63e0SAlan Somers return (in->header.opcode == FUSE_GETATTR && 172*0a7c63e0SAlan Somers in->header.nodeid == bar_ino); 173*0a7c63e0SAlan Somers }, Eq(true)), 174*0a7c63e0SAlan Somers _) 175*0a7c63e0SAlan Somers ) 176*0a7c63e0SAlan Somers .WillOnce(Invoke(ReturnImmediate([&](auto in, auto out) { 177*0a7c63e0SAlan Somers nready1 = m_mock->m_nready; 178*0a7c63e0SAlan Somers out->header.unique = in->header.unique; 179*0a7c63e0SAlan Somers out->header.error = -EIO; 180*0a7c63e0SAlan Somers out->header.len = sizeof(out->header); 181*0a7c63e0SAlan Somers }))); 182*0a7c63e0SAlan Somers EXPECT_CALL(*m_mock, process( 183*0a7c63e0SAlan Somers ResultOf([=](auto in) { 184*0a7c63e0SAlan Somers return (in->header.opcode == FUSE_GETATTR && 185*0a7c63e0SAlan Somers in->header.nodeid == baz_ino); 186*0a7c63e0SAlan Somers }, Eq(true)), 187*0a7c63e0SAlan Somers _) 188*0a7c63e0SAlan Somers ) 189*0a7c63e0SAlan Somers .WillOnce(Invoke(ReturnImmediate([&](auto in, auto out) { 190*0a7c63e0SAlan Somers nready2 = m_mock->m_nready; 191*0a7c63e0SAlan Somers out->header.unique = in->header.unique; 192*0a7c63e0SAlan Somers out->header.error = -EIO; 193*0a7c63e0SAlan Somers out->header.len = sizeof(out->header); 194*0a7c63e0SAlan Somers }))); 195*0a7c63e0SAlan Somers 196*0a7c63e0SAlan Somers /* 197*0a7c63e0SAlan Somers * Create cached lookup entries for these files. It seems that only 198*0a7c63e0SAlan Somers * one thread at a time can be in VOP_LOOKUP for a given directory 199*0a7c63e0SAlan Somers */ 200*0a7c63e0SAlan Somers access("mountpoint/foo", F_OK); 201*0a7c63e0SAlan Somers access("mountpoint/bar", F_OK); 202*0a7c63e0SAlan Somers access("mountpoint/baz", F_OK); 203*0a7c63e0SAlan Somers ASSERT_EQ(0, pthread_create(&th0, NULL, statter, 204*0a7c63e0SAlan Somers (void*)"mountpoint/foo")) << strerror(errno); 205*0a7c63e0SAlan Somers EXPECT_EQ(0, sem_wait(&sem0)) << strerror(errno); 206*0a7c63e0SAlan Somers ASSERT_EQ(0, pthread_create(&th1, NULL, statter, 207*0a7c63e0SAlan Somers (void*)"mountpoint/bar")) << strerror(errno); 208*0a7c63e0SAlan Somers ASSERT_EQ(0, pthread_create(&th2, NULL, statter, 209*0a7c63e0SAlan Somers (void*)"mountpoint/baz")) << strerror(errno); 210*0a7c63e0SAlan Somers 211*0a7c63e0SAlan Somers nap(); // Allow th1 and th2 to send their ops to the daemon 212*0a7c63e0SAlan Somers EXPECT_EQ(0, sem_post(&sem1)) << strerror(errno); 213*0a7c63e0SAlan Somers 214*0a7c63e0SAlan Somers pthread_join(th0, NULL); 215*0a7c63e0SAlan Somers pthread_join(th1, NULL); 216*0a7c63e0SAlan Somers pthread_join(th2, NULL); 217*0a7c63e0SAlan Somers 218*0a7c63e0SAlan Somers EXPECT_EQ(1, nready0); 219*0a7c63e0SAlan Somers EXPECT_EQ(2, nready1); 220*0a7c63e0SAlan Somers EXPECT_EQ(1, nready2); 221*0a7c63e0SAlan Somers 222*0a7c63e0SAlan Somers sem_destroy(&sem0); 223*0a7c63e0SAlan Somers sem_destroy(&sem1); 224*0a7c63e0SAlan Somers } 225