public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [GIT PULL] fuse update for 6.19
@ 2025-12-04  8:25 Miklos Szeredi
  2025-12-05 23:47 ` Linus Torvalds
  2025-12-06  0:17 ` pr-tracker-bot
  0 siblings, 2 replies; 12+ messages in thread
From: Miklos Szeredi @ 2025-12-04  8:25 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-fsdevel, linux-kernel

Hi Linus,

Please pull from:

git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git
tags/fuse-update-6.19

- Add mechanism for cleaning out unused, stale dentries; controlled
via a module option (Luis Henriques)

- Fix various bugs

- Cleanups

The stale dentry cleanup has a patch touching dcache.c: this extracts
a helper from d_prune_aliases() that puts the unused dentry on a
dispose list.  Export this and shrink_dentry_list() to modules.

Thanks,
Miklos

---
Bernd Schubert (3):
      fuse: Fix whitespace for fuse_uring_args_to_ring() comment
      fuse: Invalidate the page cache after FOPEN_DIRECT_IO write
      fuse: Always flush the page cache before FOPEN_DIRECT_IO write

Cheng Ding (1):
      fuse: missing copy_finish in fuse-over-io-uring argument copies

Dan Carpenter (1):
      fuse: Uninitialized variable in fuse_epoch_work()

Darrick J. Wong (1):
      fuse: signal that a fuse inode should exhibit local fs behaviors

Joanne Koong (2):
      fuse: fix readahead reclaim deadlock
      fuse: fix io-uring list corruption for terminated non-committed requests

Luis Henriques (4):
      dcache: export shrink_dentry_list() and add new helper
d_dispose_if_unused()
      fuse: new work queue to periodically invalidate expired dentries
      fuse: new work queue to invalidate dentries from old epochs
      fuse: refactor fuse_conn_put() to remove negative logic.

Miklos Szeredi (1):
      fuse: add WARN_ON and comment for RCU revalidate

Miquel Sabaté Solà (2):
      fuse: use strscpy instead of strcpy
      fuse: rename 'namelen' to 'namesize'

---
 fs/dcache.c            |  18 ++--
 fs/fuse/dev.c          |   9 +-
 fs/fuse/dev_uring.c    |  12 ++-
 fs/fuse/dir.c          | 248 +++++++++++++++++++++++++++++++++++++++++++------
 fs/fuse/file.c         |  37 ++++++--
 fs/fuse/fuse_dev_i.h   |   1 +
 fs/fuse/fuse_i.h       |  28 +++++-
 fs/fuse/inode.c        |  44 +++++----
 fs/overlayfs/super.c   |  12 ++-
 include/linux/dcache.h |   2 +
 10 files changed, 340 insertions(+), 71 deletions(-)

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [GIT PULL] fuse update for 6.19
  2025-12-04  8:25 [GIT PULL] fuse update for 6.19 Miklos Szeredi
@ 2025-12-05 23:47 ` Linus Torvalds
  2025-12-06  1:42   ` Al Viro
  2025-12-06  0:17 ` pr-tracker-bot
  1 sibling, 1 reply; 12+ messages in thread
From: Linus Torvalds @ 2025-12-05 23:47 UTC (permalink / raw)
  To: Miklos Szeredi; +Cc: linux-fsdevel, linux-kernel

On Thu, 4 Dec 2025 at 00:25, Miklos Szeredi <miklos@szeredi.hu> wrote:
>
> The stale dentry cleanup has a patch touching dcache.c: this extracts
> a helper from d_prune_aliases() that puts the unused dentry on a
> dispose list.  Export this and shrink_dentry_list() to modules.

Is that

        spin_lock(&dentry->d_lock);
        if (!dentry->d_lockref.count)
                to_shrink_list(dentry, dispose);
        spin_unlock(&dentry->d_lock);

thing possibly hot, and count might be commonly non-zero?

Because it's possible that we could just make it a lockref operation
where we atomically don't take the lock if the count is non-zero so
that we don't unnecessarily move cachelines around...

IOW, some kind of "lockref_lock_if_zero()" pattern?

I have no idea what the fuse dentry lifetime patterns might be, maybe
this is a complete non-issue...

         Linus

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [GIT PULL] fuse update for 6.19
  2025-12-04  8:25 [GIT PULL] fuse update for 6.19 Miklos Szeredi
  2025-12-05 23:47 ` Linus Torvalds
@ 2025-12-06  0:17 ` pr-tracker-bot
  1 sibling, 0 replies; 12+ messages in thread
From: pr-tracker-bot @ 2025-12-06  0:17 UTC (permalink / raw)
  To: Miklos Szeredi; +Cc: Linus Torvalds, linux-fsdevel, linux-kernel

The pull request you sent on Thu, 4 Dec 2025 09:25:29 +0100:

> git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git tags/fuse-update-6.19

has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/4b6b4321280ea1ea1e101fd39d8664195d18ecb0

Thank you!

-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [GIT PULL] fuse update for 6.19
  2025-12-05 23:47 ` Linus Torvalds
@ 2025-12-06  1:42   ` Al Viro
  2025-12-06  1:52     ` Linus Torvalds
  0 siblings, 1 reply; 12+ messages in thread
From: Al Viro @ 2025-12-06  1:42 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Miklos Szeredi, linux-fsdevel, linux-kernel

On Fri, Dec 05, 2025 at 03:47:50PM -0800, Linus Torvalds wrote:
> On Thu, 4 Dec 2025 at 00:25, Miklos Szeredi <miklos@szeredi.hu> wrote:
> >
> > The stale dentry cleanup has a patch touching dcache.c: this extracts
> > a helper from d_prune_aliases() that puts the unused dentry on a
> > dispose list.  Export this and shrink_dentry_list() to modules.
> 
> Is that
> 
>         spin_lock(&dentry->d_lock);
>         if (!dentry->d_lockref.count)
>                 to_shrink_list(dentry, dispose);
>         spin_unlock(&dentry->d_lock);
> 
> thing possibly hot, and count might be commonly non-zero?
> 
> Because it's possible that we could just make it a lockref operation
> where we atomically don't take the lock if the count is non-zero so
> that we don't unnecessarily move cachelines around...
> 
> IOW, some kind of "lockref_lock_if_zero()" pattern?
> 
> I have no idea what the fuse dentry lifetime patterns might be, maybe
> this is a complete non-issue...

Far more interesting question, IMO, is what's to prevent memory
pressure from evicting the damn argument right under us.

AFAICS, fuse_dentry_tree_work() calls that thing with no locks held.
The one and only reason why that's OK in d_prune_aliases() is ->i_lock
held over that thing - that's enough to prevent eviction.  I don't
see anything to serve the same purpose here.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [GIT PULL] fuse update for 6.19
  2025-12-06  1:42   ` Al Viro
@ 2025-12-06  1:52     ` Linus Torvalds
  2025-12-06  2:28       ` Al Viro
  0 siblings, 1 reply; 12+ messages in thread
From: Linus Torvalds @ 2025-12-06  1:52 UTC (permalink / raw)
  To: Al Viro; +Cc: Miklos Szeredi, linux-fsdevel, linux-kernel

On Fri, 5 Dec 2025 at 17:42, Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> Far more interesting question, IMO, is what's to prevent memory
> pressure from evicting the damn argument right under us.

That was my first reaction, but look at the 'fuse_dentry_prune()' logic.

So if the dentry is removed by the VFS layer, it should be removed here too.

But maybe I missed something,

            Linus

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [GIT PULL] fuse update for 6.19
  2025-12-06  1:52     ` Linus Torvalds
@ 2025-12-06  2:28       ` Al Viro
  2025-12-06  3:10         ` Al Viro
  2025-12-06  3:29         ` Linus Torvalds
  0 siblings, 2 replies; 12+ messages in thread
From: Al Viro @ 2025-12-06  2:28 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Miklos Szeredi, linux-fsdevel, linux-kernel

On Fri, Dec 05, 2025 at 05:52:51PM -0800, Linus Torvalds wrote:
> On Fri, 5 Dec 2025 at 17:42, Al Viro <viro@zeniv.linux.org.uk> wrote:
> >
> > Far more interesting question, IMO, is what's to prevent memory
> > pressure from evicting the damn argument right under us.
> 
> That was my first reaction, but look at the 'fuse_dentry_prune()' logic.
> 
> So if the dentry is removed by the VFS layer, it should be removed here too.

Sure, ->d_prune() would take it out of the rbtree, but what if it hits
                                rb_erase(&fd->node, &dentry_hash[i].tree);
                                RB_CLEAR_NODE(&fd->node);
                                spin_unlock(&dentry_hash[i].lock);
... right here, when we are not holding any locks anymore?
                                d_dispose_if_unused(fd->dentry, &dispose);
                                cond_resched();
                                spin_lock(&dentry_hash[i].lock);


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [GIT PULL] fuse update for 6.19
  2025-12-06  2:28       ` Al Viro
@ 2025-12-06  3:10         ` Al Viro
  2025-12-06  3:29         ` Linus Torvalds
  1 sibling, 0 replies; 12+ messages in thread
From: Al Viro @ 2025-12-06  3:10 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Miklos Szeredi, linux-fsdevel, linux-kernel

On Sat, Dec 06, 2025 at 02:28:26AM +0000, Al Viro wrote:
> On Fri, Dec 05, 2025 at 05:52:51PM -0800, Linus Torvalds wrote:
> > On Fri, 5 Dec 2025 at 17:42, Al Viro <viro@zeniv.linux.org.uk> wrote:
> > >
> > > Far more interesting question, IMO, is what's to prevent memory
> > > pressure from evicting the damn argument right under us.
> > 
> > That was my first reaction, but look at the 'fuse_dentry_prune()' logic.
> > 
> > So if the dentry is removed by the VFS layer, it should be removed here too.
> 
> Sure, ->d_prune() would take it out of the rbtree, but what if it hits
>                                 rb_erase(&fd->node, &dentry_hash[i].tree);
>                                 RB_CLEAR_NODE(&fd->node);
>                                 spin_unlock(&dentry_hash[i].lock);
> ... right here, when we are not holding any locks anymore?
>                                 d_dispose_if_unused(fd->dentry, &dispose);
>                                 cond_resched();
>                                 spin_lock(&dentry_hash[i].lock);

... and with what fuse_dentry_prune() is doing, we can't grab ->d_lock
or bump ->d_count before dropping dentry_hash[...].lock.  ->d_release()
is the one called outside of ->d_lock; ->d_prune() is under it, so we'd
get AB-BA deadlock if we tried to do that kind of stuff.

Moving the eviction to ->d_release() might be doable; then we'd have
fuse locks outside of ->d_lock and could call that thing under those.

I'll need to poke around some more, but TBH I don't like that primitive -
it's really easy to fuck up and conditions for its safe use are, AFAICS,
never spelled out.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [GIT PULL] fuse update for 6.19
  2025-12-06  2:28       ` Al Viro
  2025-12-06  3:10         ` Al Viro
@ 2025-12-06  3:29         ` Linus Torvalds
  2025-12-06  3:54           ` Al Viro
  1 sibling, 1 reply; 12+ messages in thread
From: Linus Torvalds @ 2025-12-06  3:29 UTC (permalink / raw)
  To: Al Viro; +Cc: Miklos Szeredi, linux-fsdevel, linux-kernel

On Fri, 5 Dec 2025 at 18:28, Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> Sure, ->d_prune() would take it out of the rbtree, but what if it hits

Ahh.

Maybe increase the d_count before releasing that rbtree lock?

Or yeah, maybe moving it to d_release. Miklos?

           Linus

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [GIT PULL] fuse update for 6.19
  2025-12-06  3:29         ` Linus Torvalds
@ 2025-12-06  3:54           ` Al Viro
  2025-12-06  4:22             ` Al Viro
  0 siblings, 1 reply; 12+ messages in thread
From: Al Viro @ 2025-12-06  3:54 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Miklos Szeredi, linux-fsdevel, linux-kernel

On Fri, Dec 05, 2025 at 07:29:13PM -0800, Linus Torvalds wrote:
> On Fri, 5 Dec 2025 at 18:28, Al Viro <viro@zeniv.linux.org.uk> wrote:
> >
> > Sure, ->d_prune() would take it out of the rbtree, but what if it hits
> 
> Ahh.
> 
> Maybe increase the d_count before releasing that rbtree lock?
> 
> Or yeah, maybe moving it to d_release. Miklos?

Moving it to ->d_release() would be my preference, TBH.  Then
we could simply dget() the sucker under the lock and follow
that with existing dput_to_list() after dropping the lock...

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [GIT PULL] fuse update for 6.19
  2025-12-06  3:54           ` Al Viro
@ 2025-12-06  4:22             ` Al Viro
  2025-12-08 10:37               ` Miklos Szeredi
  2026-01-14 15:23               ` Miklos Szeredi
  0 siblings, 2 replies; 12+ messages in thread
From: Al Viro @ 2025-12-06  4:22 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Miklos Szeredi, linux-fsdevel, linux-kernel

On Sat, Dec 06, 2025 at 03:54:03AM +0000, Al Viro wrote:
> On Fri, Dec 05, 2025 at 07:29:13PM -0800, Linus Torvalds wrote:
> > On Fri, 5 Dec 2025 at 18:28, Al Viro <viro@zeniv.linux.org.uk> wrote:
> > >
> > > Sure, ->d_prune() would take it out of the rbtree, but what if it hits
> > 
> > Ahh.
> > 
> > Maybe increase the d_count before releasing that rbtree lock?
> > 
> > Or yeah, maybe moving it to d_release. Miklos?
> 
> Moving it to ->d_release() would be my preference, TBH.  Then
> we could simply dget() the sucker under the lock and follow
> that with existing dput_to_list() after dropping the lock...

s/dget/grab ->d_lock, increment ->d_count if not negative,
drop ->d_lock/ - we need to deal with the possibility of
the victim just going into __dentry_kill() as we find it.

And yes, it would be better off with something like
lockref_get_if_zero(struct lockref *lockref)
{
	bool retval = false;
	CMPXCHG_LOOP(
		new.count++;
		if (old_count != 0)
			return false;
	,
		return true;
	);
	spin_lock(&lockref->lock);
	if (lockref->count == 0)
		lockref->count = 1;
		retval = true;
	}
	spin_unlock(&lockref->lock);
	return retval;
}

with
		while (node) {
			fd = rb_entry(node, struct fuse_dentry, node);
			if (!time_after64(get_jiffies_64(), fd->time))
				break;
			rb_erase(&fd->node, &dentry_hash[i].tree);
			RB_CLEAR_NODE(&fd->node);
			if (lockref_get_if_zero(&dentry->d_lockref))
				dput_to_list(dentry);
			if (need_resched()) {
				spin_unlock(&dentry_hash[i].lock);
				schedule();
				spin_lock(&dentry_hash[i].lock);
			}
			node = rb_first(&dentry_hash[i].tree);
		}
in that loop.  Actually... a couple of questions:
	* why do we call shrink_dentry_list() separately for each hash
bucket?  Easier to gather everything and call it once...
	* what's the point of rbtree there?  What's wrong with plain
hlist?  Folks?

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [GIT PULL] fuse update for 6.19
  2025-12-06  4:22             ` Al Viro
@ 2025-12-08 10:37               ` Miklos Szeredi
  2026-01-14 15:23               ` Miklos Szeredi
  1 sibling, 0 replies; 12+ messages in thread
From: Miklos Szeredi @ 2025-12-08 10:37 UTC (permalink / raw)
  To: Al Viro; +Cc: Linus Torvalds, linux-fsdevel, linux-kernel

On Sat, 6 Dec 2025 at 05:22, Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Sat, Dec 06, 2025 at 03:54:03AM +0000, Al Viro wrote:
> > On Fri, Dec 05, 2025 at 07:29:13PM -0800, Linus Torvalds wrote:
> > > On Fri, 5 Dec 2025 at 18:28, Al Viro <viro@zeniv.linux.org.uk> wrote:
> > > >
> > > > Sure, ->d_prune() would take it out of the rbtree, but what if it hits
> > >
> > > Ahh.
> > >
> > > Maybe increase the d_count before releasing that rbtree lock?
> > >
> > > Or yeah, maybe moving it to d_release. Miklos?
> >
> > Moving it to ->d_release() would be my preference, TBH.  Then
> > we could simply dget() the sucker under the lock and follow
> > that with existing dput_to_list() after dropping the lock...
>
> s/dget/grab ->d_lock, increment ->d_count if not negative,
> drop ->d_lock/ - we need to deal with the possibility of
> the victim just going into __dentry_kill() as we find it.
>
> And yes, it would be better off with something like
> lockref_get_if_zero(struct lockref *lockref)
> {
>         bool retval = false;
>         CMPXCHG_LOOP(
>                 new.count++;
>                 if (old_count != 0)
>                         return false;
>         ,
>                 return true;
>         );
>         spin_lock(&lockref->lock);
>         if (lockref->count == 0)
>                 lockref->count = 1;
>                 retval = true;
>         }
>         spin_unlock(&lockref->lock);
>         return retval;
> }
>
> with
>                 while (node) {
>                         fd = rb_entry(node, struct fuse_dentry, node);
>                         if (!time_after64(get_jiffies_64(), fd->time))
>                                 break;
>                         rb_erase(&fd->node, &dentry_hash[i].tree);
>                         RB_CLEAR_NODE(&fd->node);
>                         if (lockref_get_if_zero(&dentry->d_lockref))
>                                 dput_to_list(dentry);
>                         if (need_resched()) {
>                                 spin_unlock(&dentry_hash[i].lock);
>                                 schedule();
>                                 spin_lock(&dentry_hash[i].lock);
>                         }
>                         node = rb_first(&dentry_hash[i].tree);
>                 }
> in that loop.  Actually... a couple of questions:

Looks good.  Do you want me to submit a proper patch?

>         * why do we call shrink_dentry_list() separately for each hash
> bucket?  Easier to gather everything and call it once...

No good reason.

>         * what's the point of rbtree there?  What's wrong with plain
> hlist?  Folks?

The list needs to be ordered wrt. end of validity time.  The timeout
can be different from one dentry to another even within a fuse fs, but
more likely to be varied between different fuse filesystems, so
insertion time itself doesn't determine the validity end time.

Thanks,
Miklos

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [GIT PULL] fuse update for 6.19
  2025-12-06  4:22             ` Al Viro
  2025-12-08 10:37               ` Miklos Szeredi
@ 2026-01-14 15:23               ` Miklos Szeredi
  1 sibling, 0 replies; 12+ messages in thread
From: Miklos Szeredi @ 2026-01-14 15:23 UTC (permalink / raw)
  To: Al Viro; +Cc: Linus Torvalds, linux-fsdevel, linux-kernel

On Sat, 6 Dec 2025 at 05:22, Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Sat, Dec 06, 2025 at 03:54:03AM +0000, Al Viro wrote:
> > On Fri, Dec 05, 2025 at 07:29:13PM -0800, Linus Torvalds wrote:
> > > On Fri, 5 Dec 2025 at 18:28, Al Viro <viro@zeniv.linux.org.uk> wrote:
> > > >
> > > > Sure, ->d_prune() would take it out of the rbtree, but what if it hits
> > >
> > > Ahh.
> > >
> > > Maybe increase the d_count before releasing that rbtree lock?
> > >
> > > Or yeah, maybe moving it to d_release. Miklos?
> >
> > Moving it to ->d_release() would be my preference, TBH.  Then
> > we could simply dget() the sucker under the lock and follow
> > that with existing dput_to_list() after dropping the lock...
>
> s/dget/grab ->d_lock, increment ->d_count if not negative,
> drop ->d_lock/ - we need to deal with the possibility of
> the victim just going into __dentry_kill() as we find it.
>
> And yes, it would be better off with something like
> lockref_get_if_zero(struct lockref *lockref)
> {
>         bool retval = false;
>         CMPXCHG_LOOP(
>                 new.count++;
>                 if (old_count != 0)
>                         return false;
>         ,
>                 return true;
>         );
>         spin_lock(&lockref->lock);
>         if (lockref->count == 0)
>                 lockref->count = 1;
>                 retval = true;
>         }
>         spin_unlock(&lockref->lock);
>         return retval;
> }
>
> with
>                 while (node) {
>                         fd = rb_entry(node, struct fuse_dentry, node);
>                         if (!time_after64(get_jiffies_64(), fd->time))
>                                 break;
>                         rb_erase(&fd->node, &dentry_hash[i].tree);
>                         RB_CLEAR_NODE(&fd->node);
>                         if (lockref_get_if_zero(&dentry->d_lockref))
>                                 dput_to_list(dentry);
>                         if (need_resched()) {
>                                 spin_unlock(&dentry_hash[i].lock);
>                                 schedule();
>                                 spin_lock(&dentry_hash[i].lock);
>                         }
>                         node = rb_first(&dentry_hash[i].tree);
>                 }

Posted a short patchset fixing this.

I really think there's no point in doing the get-ref, drop-ref dance.
 Retrieving the dentry from any source will require locking and that
needs to nest d_lock with or without the refcount manipulation.

So I kept the d_dispose_if_unused() API, but added a note to the
function doc that additional locking is necessary to prevent eviction.

Thanks,
Miklos

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-01-14 15:23 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-04  8:25 [GIT PULL] fuse update for 6.19 Miklos Szeredi
2025-12-05 23:47 ` Linus Torvalds
2025-12-06  1:42   ` Al Viro
2025-12-06  1:52     ` Linus Torvalds
2025-12-06  2:28       ` Al Viro
2025-12-06  3:10         ` Al Viro
2025-12-06  3:29         ` Linus Torvalds
2025-12-06  3:54           ` Al Viro
2025-12-06  4:22             ` Al Viro
2025-12-08 10:37               ` Miklos Szeredi
2026-01-14 15:23               ` Miklos Szeredi
2025-12-06  0:17 ` pr-tracker-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox