Date: Fri, 28 Jun 2019 18:48:02 +0000 (UTC) From: Alan Somers <asomers@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r349513 - in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs Message-ID: <201906281848.x5SIm2Mo043791@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: asomers Date: Fri Jun 28 18:48:02 2019 New Revision: 349513 URL: https://svnweb.freebsd.org/changeset/base/349513 Log: fusefs: don't leak memory of unsent operations on unmount Sponsored by: The FreeBSD Foundation Modified: projects/fuse2/sys/fs/fuse/fuse_device.c projects/fuse2/tests/sys/fs/fusefs/destroy.cc Modified: projects/fuse2/sys/fs/fuse/fuse_device.c ============================================================================== --- projects/fuse2/sys/fs/fuse/fuse_device.c Fri Jun 28 18:03:13 2019 (r349512) +++ projects/fuse2/sys/fs/fuse/fuse_device.c Fri Jun 28 18:48:02 2019 (r349513) @@ -157,6 +157,13 @@ fdata_dtor(void *arg) fuse_ticket_drop(tick); } fuse_lck_mtx_unlock(fdata->aw_mtx); + + /* Cleanup unsent operations */ + fuse_lck_mtx_lock(fdata->ms_mtx); + while ((tick = fuse_ms_pop(fdata))) { + fuse_ticket_drop(tick); + } + fuse_lck_mtx_unlock(fdata->ms_mtx); FUSE_UNLOCK(); fdata_trydestroy(fdata); Modified: projects/fuse2/tests/sys/fs/fusefs/destroy.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/destroy.cc Fri Jun 28 18:03:13 2019 (r349512) +++ projects/fuse2/tests/sys/fs/fusefs/destroy.cc Fri Jun 28 18:48:02 2019 (r349513) @@ -28,12 +28,105 @@ * SUCH DAMAGE. */ +extern "C" { +#include <fcntl.h> +#include <pthread.h> +#include <semaphore.h> +} + #include "mockfs.hh" #include "utils.hh" using namespace testing; +/* Tests for orderly unmounts */ class Destroy: public FuseTest {}; + +/* Tests for unexpected deaths of the server */ +class Death: public FuseTest{}; + +static void* open_th(void* arg) { + int fd; + const char *path = (const char*)arg; + + fd = open(path, O_RDONLY); + EXPECT_EQ(-1, fd); + EXPECT_EQ(ENOTCONN, errno); + return 0; +} + +/* + * The server dies with unsent operations still on the message queue. + * Check for any memory leaks like this: + * 1) kldunload fusefs, if necessary + * 2) kldload fusefs + * 3) ./destroy --gtest_filter=Destroy.unsent_operations + * 4) kldunload fusefs + * 5) check /var/log/messages for anything like this: +Freed UMA keg (fuse_ticket) was not empty (31 items). Lost 2 pages of memory. +Warning: memory type fuse_msgbuf leaked memory on destroy (68 allocations, 428800 bytes leaked). + */ +TEST_F(Death, unsent_operations) +{ + const char FULLPATH0[] = "mountpoint/some_file.txt"; + const char FULLPATH1[] = "mountpoint/other_file.txt"; + const char RELPATH0[] = "some_file.txt"; + const char RELPATH1[] = "other_file.txt"; + pthread_t th0, th1; + ino_t ino0 = 42, ino1 = 43; + sem_t sem; + mode_t mode = S_IFREG | 0644; + + sem_init(&sem, 0, 0); + + EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH0) + .WillRepeatedly(Invoke( + ReturnImmediate([=](auto in __unused, auto& out) { + SET_OUT_HEADER_LEN(out, entry); + out.body.entry.attr.mode = mode; + out.body.entry.nodeid = ino0; + out.body.entry.attr.nlink = 1; + }))); + EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH1) + .WillRepeatedly(Invoke( + ReturnImmediate([=](auto in __unused, auto& out) { + SET_OUT_HEADER_LEN(out, entry); + out.body.entry.attr.mode = mode; + out.body.entry.nodeid = ino1; + out.body.entry.attr.nlink = 1; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([&](auto in) { + return (in.header.opcode == FUSE_OPEN); + }, Eq(true)), + _) + ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) { + sem_post(&sem); + pause(); + })); + + /* + * One thread's operation will be sent to the daemon and block, and the + * other's will be stuck in the message queue. + */ + ASSERT_EQ(0, pthread_create(&th0, NULL, open_th, (void*)FULLPATH0)) + << strerror(errno); + ASSERT_EQ(0, pthread_create(&th1, NULL, open_th, (void*)FULLPATH1)) + << strerror(errno); + + /* Wait for the first thread to block */ + sem_wait(&sem); + /* Give the second thread time to block */ + nap(); + + m_mock->kill_daemon(); + + pthread_join(th0, NULL); + pthread_join(th1, NULL); + + sem_destroy(&sem); +} /* * On unmount the kernel should send a FUSE_DESTROY operation. It should also
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201906281848.x5SIm2Mo043791>