All of lore.kernel.org
 help / color / mirror / Atom feed
From: Luis Henriques <luis@igalia.com>
To: Sasha Levin <sashal@kernel.org>
Cc: Miklos Szeredi <miklos@szeredi.hu>,
	 Amir Goldstein <amir73il@gmail.com>,
	 Shuah Khan <shuah@kernel.org>,
	fuse-devel@lists.linux.dev,  linux-fsdevel@vger.kernel.org,
	linux-kselftest@vger.kernel.org,  linux-kernel@vger.kernel.org,
	 kernel test robot <lkp@intel.com>
Subject: Re: [PATCH] selftests/fuse: add the missing symlink caching test
Date: Wed, 28 Oct 2026 09:34:44 +0000	[thread overview]
Message-ID: <87zevyi2x7.fsf@igalia.com> (raw)
In-Reply-To: <20261026194008.274349-1-sashal@kernel.org>

On Mon, Oct 26 2026, Sasha Levin wrote:

> Commit 3e14f079d7a0 ("selftests/fuse: add fuse symlink caching test")
> registered fuse_symlink_cache_test in the fuse selftest Makefile and
> .gitignore, but the test source was not part of the applied commit, so
> building the fuse selftests fails:
>
>   make[1]: *** No rule to make target 'fuse_symlink_cache_test.c', needed by '.../fuse_symlink_cache_test'.
>
> Add fuse_symlink_cache_test.c as posted in Luis Henriques's v5
> submission. The test serves a file and a symlink to it from a libfuse
> low-level filesystem, resolves the symlink 100 times, and counts the
> readlink requests that reach the server: one when FUSE_CAP_CACHE_SYMLINKS
> is set during FUSE_INIT, and all 100 when it is not.
>
> Fixes: 3e14f079d7a0 ("selftests/fuse: add fuse symlink caching test")
> Reported-by: kernel test robot <lkp@intel.com>
> Closes: https://lore.kernel.org/r/202610170822.oEJ1Autq-lkp@intel.com/
> Link: https://lore.kernel.org/r/20261001164916.15311-8-luis@igalia.com
> Originally-by: Luis Henriques <luis@igalia.com>
> Signed-off-by: Sasha Levin <sashal@kernel.org>
> ---
> As with e64a46aefc01 ("selftests/fuse: add the missing FUSE_HAS_SYNCFS
> privilege test"), the new file in the posted patch is not in the applied
> commit; the other files did land.

Oh, wow!  I wonder how that happen.  Anyway, thanks for noticing that and
for fixing it, Sasha.  If it makes any sense (I wrote the test! :-) ) feel
free to add my

Reviewed-by: Luis Henriques <luis@igalia.com>

Cheers,
-- 
Luís


>  .../fuse/fuse_symlink_cache_test.c            | 162 ++++++++++++++++++
>  1 file changed, 162 insertions(+)
>  create mode 100644 tools/testing/selftests/filesystems/fuse/fuse_symlink_cache_test.c
>
> diff --git a/tools/testing/selftests/filesystems/fuse/fuse_symlink_cache_test.c b/tools/testing/selftests/filesystems/fuse/fuse_symlink_cache_test.c
> new file mode 100644
> index 0000000000000..7b53734995736
> --- /dev/null
> +++ b/tools/testing/selftests/filesystems/fuse/fuse_symlink_cache_test.c
> @@ -0,0 +1,162 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Simple filesystem to test FUSE symlink cache
> + *
> + * This is a simple FUSE filesystem that contains two objects: a file named
> + * 'file' and a symlink to that file named 'link'.  If symlink caching is
> + * disabled (i.e. FUSE_CAP_CACHE_SYMLINKS is reset during FUSE_INIT), whenever
> + * the ->readlink() is executed to resolve 'link' a counter will be incremented.
> + *
> + * If symlink caching is enabled (i.e. FUSE_CAP_CACHE_SYMLINKS is set during
> + * FUSE_INIT), resolving a symlink will only call into user-space the first
> + * time.
> + */
> +
> +#define FUSE_USE_VERSION 31
> +
> +#include <stdio.h>
> +#include <fuse_lowlevel.h>
> +
> +#include "kselftest_harness.h"
> +
> +#include "fuse_common.h"
> +
> +#define FILENAME "file"
> +#define FILE_INO 42
> +
> +#define LINKNAME "link"
> +#define LINK_INO 43
> +
> +#define TIMEOUT	86400.0f
> +
> +struct test_state {
> +	pthread_mutex_t lock;
> +	int readlink_counter;
> +	bool cache;
> +} test_state = {
> +	.lock = PTHREAD_MUTEX_INITIALIZER,
> +};
> +
> +static void fs_init(void *userdata, struct fuse_conn_info *conn)
> +{
> +	pthread_mutex_lock(&test_state.lock);
> +	if (test_state.cache)
> +		fuse_set_feature_flag(conn, FUSE_CAP_CACHE_SYMLINKS);
> +	else
> +		fuse_unset_feature_flag(conn, FUSE_CAP_CACHE_SYMLINKS);
> +	pthread_mutex_unlock(&test_state.lock);
> +}
> +
> +static void fs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
> +{
> +	struct fuse_entry_param e = {};
> +
> +	if (parent != FUSE_ROOT_ID ||
> +	    (strcmp(name, FILENAME) != 0 && strcmp(name, LINKNAME) != 0)) {
> +		fuse_reply_err(req, ENOENT);
> +		return;
> +	}
> +
> +	if (!strcmp(name, FILENAME)) {
> +		e.ino = FILE_INO;
> +		e.attr.st_mode = S_IFREG | 0444;
> +		e.attr.st_nlink = 2;
> +	} else if (!strcmp(name, LINKNAME)) {
> +		e.ino = LINK_INO;
> +		e.attr.st_mode = S_IFLNK | 0444;
> +		e.attr.st_nlink = 1;
> +		e.attr.st_size = strlen(FILENAME);
> +	}
> +	e.attr_timeout = TIMEOUT;
> +	e.entry_timeout = TIMEOUT;
> +
> +	fuse_reply_entry(req, &e);
> +}
> +
> +static void fs_readlink(fuse_req_t req, fuse_ino_t ino)
> +{
> +	char buf[PATH_MAX];
> +	size_t sz = strlen(FILENAME);
> +
> +	if (ino != LINK_INO) {
> +		fuse_reply_err(req, ENOENT);
> +		return;
> +	}
> +
> +	memcpy(buf, FILENAME, sz);
> +	buf[sz] = '\0';
> +	pthread_mutex_lock(&test_state.lock);
> +	test_state.readlink_counter++;
> +	pthread_mutex_unlock(&test_state.lock);
> +
> +	fuse_reply_readlink(req, buf);
> +}
> +
> +static const struct fuse_lowlevel_ops fs_ops = {
> +	.init           = fs_init,
> +	.lookup		= fs_lookup,
> +	.readlink	= fs_readlink,
> +};
> +
> +FIXTURE(symlink_cache)
> +{
> +	struct fuse_common_ctx ctx;
> +};
> +FIXTURE_VARIANT(symlink_cache)
> +{
> +	const bool cache;
> +};
> +FIXTURE_VARIANT_ADD(symlink_cache, symlinks_nocache)
> +{
> +	/* Variant with symlink cache disabled */
> +	.cache = false,
> +};
> +FIXTURE_VARIANT_ADD(symlink_cache, symlinks_cache)
> +{
> +	/* Variant with symlink cache enabled */
> +	.cache = true,
> +};
> +
> +FIXTURE_SETUP(symlink_cache)
> +{
> +	char err[MAX_ERR_MSG];
> +
> +	pthread_mutex_lock(&test_state.lock);
> +	test_state.readlink_counter = 0;
> +	test_state.cache = variant->cache;
> +	pthread_mutex_unlock(&test_state.lock);
> +
> +	if (fs_setup(&fs_ops, &self->ctx, err))
> +		SKIP(return, err);
> +}
> +
> +FIXTURE_TEARDOWN(symlink_cache)
> +{
> +	fs_teardown(&self->ctx);
> +}
> +
> +TEST_F(symlink_cache, test_symlink_cache)
> +{
> +	char pathname[PATH_MAX];
> +	char buf[PATH_MAX];
> +	ssize_t sz;
> +	int counter;
> +	int i;
> +
> +	sprintf(pathname, "%s/%s", self->ctx.mountpoint, LINKNAME);
> +	for (i = 0; i < 100; i++) {
> +		sz = readlink(pathname, buf, PATH_MAX);
> +		ASSERT_NE(sz, -1);
> +	}
> +	pthread_mutex_lock(&test_state.lock);
> +	counter = test_state.readlink_counter;
> +	pthread_mutex_unlock(&test_state.lock);
> +
> +	if (variant->cache) {
> +		ASSERT_EQ(counter, 1);
> +	} else {
> +		ASSERT_EQ(counter, 100);
> +	}
> +}
> +
> +TEST_HARNESS_MAIN
>
> base-commit: 9b1da7b4a4e197072e630c3b85d4100a399752ff
> -- 
> 2.53.0
>


      reply	other threads:[~2026-10-28  9:33 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-26 19:40 [PATCH] selftests/fuse: add the missing symlink caching test Sasha Levin
2026-10-28  9:34 ` Luis Henriques [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87zevyi2x7.fsf@igalia.com \
    --to=luis@igalia.com \
    --cc=amir73il@gmail.com \
    --cc=fuse-devel@lists.linux.dev \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=lkp@intel.com \
    --cc=miklos@szeredi.hu \
    --cc=sashal@kernel.org \
    --cc=shuah@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.