* Re: [PATCH v4 00/54] tree-in-dcache stuff
[not found] <20251118051604.3868588-1-viro@zeniv.linux.org.uk>
@ 2026-01-27 0:56 ` Samuel Wu
2026-01-27 7:42 ` Greg KH
0 siblings, 1 reply; 26+ messages in thread
From: Samuel Wu @ 2026-01-27 0:56 UTC (permalink / raw)
To: Al Viro
Cc: linux-fsdevel, torvalds, brauner, jack, raven, miklos, neil,
a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
gregkh, linux-usb, paul, casey, linuxppc-dev, john.johansen,
selinux, borntraeger, bpf, clm, android-kernel-team
On Mon, Nov 17, 2025 at 9:15 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> Some filesystems use a kinda-sorta controlled dentry refcount leak to pin
> dentries of created objects in dcache (and undo it when removing those).
> Reference is grabbed and not released, but it's not actually _stored_
> anywhere. That works, but it's hard to follow and verify; among other
> things, we have no way to tell _which_ of the increments is intended
> to be an unpaired one. Worse, on removal we need to decide whether
> the reference had already been dropped, which can be non-trivial if
> that removal is on umount and we need to figure out if this dentry is
> pinned due to e.g. unlink() not done. Usually that is handled by using
> kill_litter_super() as ->kill_sb(), but there are open-coded special
> cases of the same (consider e.g. /proc/self).
>
> Things get simpler if we introduce a new dentry flag (DCACHE_PERSISTENT)
> marking those "leaked" dentries. Having it set claims responsibility
> for +1 in refcount.
>
> The end result this series is aiming for:
>
> * get these unbalanced dget() and dput() replaced with new primitives that
> would, in addition to adjusting refcount, set and clear persistency flag.
> * instead of having kill_litter_super() mess with removing the remaining
> "leaked" references (e.g. for all tmpfs files that hadn't been removed
> prior to umount), have the regular shrink_dcache_for_umount() strip
> DCACHE_PERSISTENT of all dentries, dropping the corresponding
> reference if it had been set. After that kill_litter_super() becomes
> an equivalent of kill_anon_super().
>
> Doing that in a single step is not feasible - it would affect too many places
> in too many filesystems. It has to be split into a series.
>
> This work has really started early in 2024; quite a few preliminary pieces
> have already gone into mainline. This chunk is finally getting to the
> meat of that stuff - infrastructure and most of the conversions to it.
>
> Some pieces are still sitting in the local branches, but the bulk of
> that stuff is here.
>
> Compared to v3:
> * fixed a functionfs braino around ffs_epfiles_destroy() (in #40/54,
> used to be #36/50).
> * added fixes for a couple of UAF in functionfs (##36--39); that
> does *NOT* include any fixes for dmabuf bugs Chris posted last week, though.
>
> The branch is -rc5-based; it lives in
> git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #work.persistency
> individual patches in followups.
>
> Please, help with review and testing. If nobody objects, in a few days it
> goes into #for-next.
>
> Shortlog:
> fuse_ctl_add_conn(): fix nlink breakage in case of early failure
> tracefs: fix a leak in eventfs_create_events_dir()
> new helper: simple_remove_by_name()
> new helper: simple_done_creating()
> introduce a flag for explicitly marking persistently pinned dentries
> primitives for maintaining persisitency
> convert simple_{link,unlink,rmdir,rename,fill_super}() to new primitives
> convert ramfs and tmpfs
> procfs: make /self and /thread_self dentries persistent
> configfs, securityfs: kill_litter_super() not needed
> convert xenfs
> convert smackfs
> convert hugetlbfs
> convert mqueue
> convert bpf
> convert dlmfs
> convert fuse_ctl
> convert pstore
> convert tracefs
> convert debugfs
> debugfs: remove duplicate checks in callers of start_creating()
> convert efivarfs
> convert spufs
> convert ibmasmfs
> ibmasmfs: get rid of ibmasmfs_dir_ops
> convert devpts
> binderfs: use simple_start_creating()
> binderfs_binder_ctl_create(): kill a bogus check
> convert binderfs
> autofs_{rmdir,unlink}: dentry->d_fsdata->dentry == dentry there
> convert autofs
> convert binfmt_misc
> selinuxfs: don't stash the dentry of /policy_capabilities
> selinuxfs: new helper for attaching files to tree
> convert selinuxfs
> functionfs: don't abuse ffs_data_closed() on fs shutdown
> functionfs: don't bother with ffs->ref in ffs_data_{opened,closed}()
> functionfs: need to cancel ->reset_work in ->kill_sb()
> functionfs: fix the open/removal races
> functionfs: switch to simple_remove_by_name()
> convert functionfs
> gadgetfs: switch to simple_remove_by_name()
> convert gadgetfs
> hypfs: don't pin dentries twice
> hypfs: switch hypfs_create_str() to returning int
> hypfs: swich hypfs_create_u64() to returning int
> convert hypfs
> convert rpc_pipefs
> convert nfsctl
> convert rust_binderfs
> get rid of kill_litter_super()
> convert securityfs
> kill securityfs_recursive_remove()
> d_make_discardable(): warn if given a non-persistent dentry
>
> Diffstat:
> Documentation/filesystems/porting.rst | 7 ++
> arch/powerpc/platforms/cell/spufs/inode.c | 17 ++-
> arch/s390/hypfs/hypfs.h | 6 +-
> arch/s390/hypfs/hypfs_diag_fs.c | 60 ++++------
> arch/s390/hypfs/hypfs_vm_fs.c | 21 ++--
> arch/s390/hypfs/inode.c | 82 +++++--------
> drivers/android/binder/rust_binderfs.c | 121 ++++++-------------
> drivers/android/binderfs.c | 82 +++----------
> drivers/base/devtmpfs.c | 2 +-
> drivers/misc/ibmasm/ibmasmfs.c | 24 ++--
> drivers/usb/gadget/function/f_fs.c | 144 +++++++++++++----------
> drivers/usb/gadget/legacy/inode.c | 49 ++++----
> drivers/xen/xenfs/super.c | 2 +-
> fs/autofs/inode.c | 2 +-
> fs/autofs/root.c | 11 +-
> fs/binfmt_misc.c | 69 ++++++-----
> fs/configfs/dir.c | 10 +-
> fs/configfs/inode.c | 3 +-
> fs/configfs/mount.c | 2 +-
> fs/dcache.c | 111 +++++++++++-------
> fs/debugfs/inode.c | 32 ++----
> fs/devpts/inode.c | 57 ++++-----
> fs/efivarfs/inode.c | 7 +-
> fs/efivarfs/super.c | 5 +-
> fs/fuse/control.c | 38 +++---
> fs/hugetlbfs/inode.c | 12 +-
> fs/internal.h | 1 -
> fs/libfs.c | 52 +++++++--
> fs/nfsd/nfsctl.c | 18 +--
> fs/ocfs2/dlmfs/dlmfs.c | 8 +-
> fs/proc/base.c | 6 +-
> fs/proc/internal.h | 1 +
> fs/proc/root.c | 14 +--
> fs/proc/self.c | 10 +-
> fs/proc/thread_self.c | 11 +-
> fs/pstore/inode.c | 7 +-
> fs/ramfs/inode.c | 8 +-
> fs/super.c | 8 --
> fs/tracefs/event_inode.c | 7 +-
> fs/tracefs/inode.c | 13 +--
> include/linux/dcache.h | 4 +-
> include/linux/fs.h | 6 +-
> include/linux/proc_fs.h | 2 -
> include/linux/security.h | 2 -
> init/do_mounts.c | 2 +-
> ipc/mqueue.c | 12 +-
> kernel/bpf/inode.c | 15 +--
> mm/shmem.c | 38 ++----
> net/sunrpc/rpc_pipe.c | 27 ++---
> security/apparmor/apparmorfs.c | 13 ++-
> security/inode.c | 35 +++---
> security/selinux/selinuxfs.c | 185 +++++++++++++-----------------
> security/smack/smackfs.c | 2 +-
> 53 files changed, 649 insertions(+), 834 deletions(-)
>
> Overview:
>
> First two commits are bugfixes (fusectl and tracefs resp.)
>
> [1/54] fuse_ctl_add_conn(): fix nlink breakage in case of early failure
> [2/54] tracefs: fix a leak in eventfs_create_events_dir()
>
> Next, two commits adding a couple of useful helpers, the next three adding
> the infrastructure and the rest consists of per-filesystem conversions.
>
> [3/54] new helper: simple_remove_by_name()
> [4/54] new helper: simple_done_creating()
> end_creating_path() analogue for internal object creation; unlike
> end_creating_path() no mount is passed to it (or guaranteed to exist, for
> that matter - it might be used during the filesystem setup, before the
> superblock gets attached to any mounts).
>
> Infrastructure:
> [5/54] introduce a flag for explicitly marking persistently pinned dentries
> * introduce the new flag
> * teach shrink_dcache_for_umount() to handle it (i.e. remove
> and drop refcount on anything that survives to umount with that flag
> still set)
> * teach kill_litter_super() that anything with that flag does
> *not* need to be unpinned.
> [6/54] primitives for maintaining persisitency
> * d_make_persistent(dentry, inode) - bump refcount, mark persistent
> and make hashed positive. Return value is a borrowed reference to dentry;
> it can be used until something removes persistency (at the very least,
> until the parent gets unlocked, but some filesystems may have stronger
> exclusion).
> * d_make_discardable() - remove persistency mark and drop reference.
>
> NOTE: at that stage d_make_discardable() does not reject dentries not
> marked persistent - it acts as if the mark been set.
>
> Rationale: less noise in series splitup that way. We want (and on the
> next commit will get) simple_unlink() to do the right thing - remove
> persistency, if it's there. However, it's used by many filesystems.
> We would have either to convert them all at once or split simple_unlink()
> into "want persistent" and "don't want persistent" versions, the latter
> being the old one. In the course of the series almost all callers
> would migrate to the replacement, leaving only two pathological cases
> with the old one. The same goes for simple_rmdir() (two callers left in
> the end), simple_recursive_removal() (all callers gone in the end), etc.
> That's a lot of noise and it's easier to start with d_make_discardable()
> quietly accepting non-persistent dentries, then, in the end, add private
> copies of simple_unlink() and simple_rmdir() for two weird users (configfs
> and apparmorfs) and have those use dput() instead of d_make_discardable().
> At that point we'd be left with all callers of d_make_discardable()
> always passing persistent dentries, allowing to add a warning in it.
>
> [7/54] convert simple_{link,unlink,rmdir,rename,fill_super}() to new primitives
> See above re quietly accepting non-peristent dentries in
> simple_unlink(), simple_rmdir(), etc.
>
> Converting filesystems:
> [8/54] convert ramfs and tmpfs
> [9/54] procfs: make /self and /thread_self dentries persistent
> [10/54] configfs, securityfs: kill_litter_super() not needed
> [11/54] convert xenfs
> [12/54] convert smackfs
> [13/54] convert hugetlbfs
> [14/54] convert mqueue
> [15/54] convert bpf
> [16/54] convert dlmfs
> [17/54] convert fuse_ctl
> [18/54] convert pstore
> [19/54] convert tracefs
> [20/54] convert debugfs
> [21/54] debugfs: remove duplicate checks in callers of start_creating()
> [22/54] convert efivarfs
> [23/54] convert spufs
> [24/54] convert ibmasmfs
> [25/54] ibmasmfs: get rid of ibmasmfs_dir_ops
> [26/54] convert devpts
> [27/54] binderfs: use simple_start_creating()
> [28/54] binderfs_binder_ctl_create(): kill a bogus check
> [29/54] convert binderfs
> [30/54] autofs_{rmdir,unlink}: dentry->d_fsdata->dentry == dentry there
> [31/54] convert autofs
> [32/54] convert binfmt_misc
> [33/54] selinuxfs: don't stash the dentry of /policy_capabilities
> [34/54] selinuxfs: new helper for attaching files to tree
> [35/54] convert selinuxfs
>
> Several functionfs fixes, before converting it, to make life
> simpler for backporting:
> [36/54] functionfs: don't abuse ffs_data_closed() on fs shutdown
> [37/54] functionfs: don't bother with ffs->ref in ffs_data_{opened,closed}()
> [38/54] functionfs: need to cancel ->reset_work in ->kill_sb()
> [39/54] functionfs: fix the open/removal races
>
> ... and back to filesystems conversions:
>
> [40/54] functionfs: switch to simple_remove_by_name()
> [41/54] convert functionfs
> [42/54] gadgetfs: switch to simple_remove_by_name()
> [43/54] convert gadgetfs
> [44/54] hypfs: don't pin dentries twice
> [45/54] hypfs: switch hypfs_create_str() to returning int
> [46/54] hypfs: swich hypfs_create_u64() to returning int
> [47/54] convert hypfs
> [48/54] convert rpc_pipefs
> [49/54] convert nfsctl
> [50/54] convert rust_binderfs
>
> ... and no kill_litter_super() callers remain, so we
> can take it out:
> [51/54] get rid of kill_litter_super()
>
> Followups:
> [52/54] convert securityfs
> That was the last remaining user of simple_recursive_removal()
> that did *not* mark things persistent. Now the only places where
> d_make_discardable() is still called for dentries that are not marked
> persistent are the calls of simple_{unlink,rmdir}() in configfs and
> apparmorfs.
>
> [53/54] kill securityfs_recursive_remove()
> Unused macro...
>
> [54/54] d_make_discardable(): warn if given a non-persistent dentry
>
> At this point there are very few call chains that might lead to
> d_make_discardable() on a dentry that hadn't been made persistent:
> calls of simple_unlink() and simple_rmdir() in configfs and
> apparmorfs.
>
> Both filesystems do pin (part of) their contents in dcache, but
> they are currently playing very unusual games with that. Converting
> them to more usual patterns might be possible, but it's definitely
> going to be a long series of changes in both cases.
>
> For now the easiest solution is to have both stop using simple_unlink()
> and simple_rmdir() - that allows to make d_make_discardable() warn
> when given a non-persistent dentry.
>
> Rather than giving them full-blown private copies (with calls of
> d_make_discardable() replaced with dput()), let's pull the parts of
> simple_unlink() and simple_rmdir() that deal with timestamps and link
> counts into separate helpers (__simple_unlink() and __simple_rmdir()
> resp.) and have those used by configfs and apparmorfs.
>
Hi Al, when I apply this patchset my Pixel 6 no longer enumerates on
lsusb or ADB. It was quite hard to bisect to this point, as this is
non-deterministic and seems to be setup specific. Note, I am using
android-mainline, but my understanding is that this build does not
have any out-of-tree USB patches, and that there are no vendor hooks
in the build.
My apologies as I can't offer any other clues; there are no obviously
bad dmesg logs and I'm still working on narrowing down the exact
commit(s) that started this, but just wanted to send a FYI in case
something stands out as obvious.
Thanks!
Sam
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-27 0:56 ` [PATCH v4 00/54] tree-in-dcache stuff Samuel Wu
@ 2026-01-27 7:42 ` Greg KH
2026-01-27 18:39 ` Linus Torvalds
2026-01-28 2:02 ` Samuel Wu
0 siblings, 2 replies; 26+ messages in thread
From: Greg KH @ 2026-01-27 7:42 UTC (permalink / raw)
To: Samuel Wu
Cc: Al Viro, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Mon, Jan 26, 2026 at 04:56:42PM -0800, Samuel Wu wrote:
> On Mon, Nov 17, 2025 at 9:15 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> >
> > Some filesystems use a kinda-sorta controlled dentry refcount leak to pin
> > dentries of created objects in dcache (and undo it when removing those).
> > Reference is grabbed and not released, but it's not actually _stored_
> > anywhere. That works, but it's hard to follow and verify; among other
> > things, we have no way to tell _which_ of the increments is intended
> > to be an unpaired one. Worse, on removal we need to decide whether
> > the reference had already been dropped, which can be non-trivial if
> > that removal is on umount and we need to figure out if this dentry is
> > pinned due to e.g. unlink() not done. Usually that is handled by using
> > kill_litter_super() as ->kill_sb(), but there are open-coded special
> > cases of the same (consider e.g. /proc/self).
> >
> > Things get simpler if we introduce a new dentry flag (DCACHE_PERSISTENT)
> > marking those "leaked" dentries. Having it set claims responsibility
> > for +1 in refcount.
> >
> > The end result this series is aiming for:
> >
> > * get these unbalanced dget() and dput() replaced with new primitives that
> > would, in addition to adjusting refcount, set and clear persistency flag.
> > * instead of having kill_litter_super() mess with removing the remaining
> > "leaked" references (e.g. for all tmpfs files that hadn't been removed
> > prior to umount), have the regular shrink_dcache_for_umount() strip
> > DCACHE_PERSISTENT of all dentries, dropping the corresponding
> > reference if it had been set. After that kill_litter_super() becomes
> > an equivalent of kill_anon_super().
> >
> > Doing that in a single step is not feasible - it would affect too many places
> > in too many filesystems. It has to be split into a series.
> >
> > This work has really started early in 2024; quite a few preliminary pieces
> > have already gone into mainline. This chunk is finally getting to the
> > meat of that stuff - infrastructure and most of the conversions to it.
> >
> > Some pieces are still sitting in the local branches, but the bulk of
> > that stuff is here.
> >
> > Compared to v3:
> > * fixed a functionfs braino around ffs_epfiles_destroy() (in #40/54,
> > used to be #36/50).
> > * added fixes for a couple of UAF in functionfs (##36--39); that
> > does *NOT* include any fixes for dmabuf bugs Chris posted last week, though.
> >
> > The branch is -rc5-based; it lives in
> > git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #work.persistency
> > individual patches in followups.
> >
> > Please, help with review and testing. If nobody objects, in a few days it
> > goes into #for-next.
> >
> > Shortlog:
> > fuse_ctl_add_conn(): fix nlink breakage in case of early failure
> > tracefs: fix a leak in eventfs_create_events_dir()
> > new helper: simple_remove_by_name()
> > new helper: simple_done_creating()
> > introduce a flag for explicitly marking persistently pinned dentries
> > primitives for maintaining persisitency
> > convert simple_{link,unlink,rmdir,rename,fill_super}() to new primitives
> > convert ramfs and tmpfs
> > procfs: make /self and /thread_self dentries persistent
> > configfs, securityfs: kill_litter_super() not needed
> > convert xenfs
> > convert smackfs
> > convert hugetlbfs
> > convert mqueue
> > convert bpf
> > convert dlmfs
> > convert fuse_ctl
> > convert pstore
> > convert tracefs
> > convert debugfs
> > debugfs: remove duplicate checks in callers of start_creating()
> > convert efivarfs
> > convert spufs
> > convert ibmasmfs
> > ibmasmfs: get rid of ibmasmfs_dir_ops
> > convert devpts
> > binderfs: use simple_start_creating()
> > binderfs_binder_ctl_create(): kill a bogus check
> > convert binderfs
> > autofs_{rmdir,unlink}: dentry->d_fsdata->dentry == dentry there
> > convert autofs
> > convert binfmt_misc
> > selinuxfs: don't stash the dentry of /policy_capabilities
> > selinuxfs: new helper for attaching files to tree
> > convert selinuxfs
> > functionfs: don't abuse ffs_data_closed() on fs shutdown
> > functionfs: don't bother with ffs->ref in ffs_data_{opened,closed}()
> > functionfs: need to cancel ->reset_work in ->kill_sb()
> > functionfs: fix the open/removal races
> > functionfs: switch to simple_remove_by_name()
> > convert functionfs
> > gadgetfs: switch to simple_remove_by_name()
> > convert gadgetfs
> > hypfs: don't pin dentries twice
> > hypfs: switch hypfs_create_str() to returning int
> > hypfs: swich hypfs_create_u64() to returning int
> > convert hypfs
> > convert rpc_pipefs
> > convert nfsctl
> > convert rust_binderfs
> > get rid of kill_litter_super()
> > convert securityfs
> > kill securityfs_recursive_remove()
> > d_make_discardable(): warn if given a non-persistent dentry
> >
> > Diffstat:
> > Documentation/filesystems/porting.rst | 7 ++
> > arch/powerpc/platforms/cell/spufs/inode.c | 17 ++-
> > arch/s390/hypfs/hypfs.h | 6 +-
> > arch/s390/hypfs/hypfs_diag_fs.c | 60 ++++------
> > arch/s390/hypfs/hypfs_vm_fs.c | 21 ++--
> > arch/s390/hypfs/inode.c | 82 +++++--------
> > drivers/android/binder/rust_binderfs.c | 121 ++++++-------------
> > drivers/android/binderfs.c | 82 +++----------
> > drivers/base/devtmpfs.c | 2 +-
> > drivers/misc/ibmasm/ibmasmfs.c | 24 ++--
> > drivers/usb/gadget/function/f_fs.c | 144 +++++++++++++----------
> > drivers/usb/gadget/legacy/inode.c | 49 ++++----
> > drivers/xen/xenfs/super.c | 2 +-
> > fs/autofs/inode.c | 2 +-
> > fs/autofs/root.c | 11 +-
> > fs/binfmt_misc.c | 69 ++++++-----
> > fs/configfs/dir.c | 10 +-
> > fs/configfs/inode.c | 3 +-
> > fs/configfs/mount.c | 2 +-
> > fs/dcache.c | 111 +++++++++++-------
> > fs/debugfs/inode.c | 32 ++----
> > fs/devpts/inode.c | 57 ++++-----
> > fs/efivarfs/inode.c | 7 +-
> > fs/efivarfs/super.c | 5 +-
> > fs/fuse/control.c | 38 +++---
> > fs/hugetlbfs/inode.c | 12 +-
> > fs/internal.h | 1 -
> > fs/libfs.c | 52 +++++++--
> > fs/nfsd/nfsctl.c | 18 +--
> > fs/ocfs2/dlmfs/dlmfs.c | 8 +-
> > fs/proc/base.c | 6 +-
> > fs/proc/internal.h | 1 +
> > fs/proc/root.c | 14 +--
> > fs/proc/self.c | 10 +-
> > fs/proc/thread_self.c | 11 +-
> > fs/pstore/inode.c | 7 +-
> > fs/ramfs/inode.c | 8 +-
> > fs/super.c | 8 --
> > fs/tracefs/event_inode.c | 7 +-
> > fs/tracefs/inode.c | 13 +--
> > include/linux/dcache.h | 4 +-
> > include/linux/fs.h | 6 +-
> > include/linux/proc_fs.h | 2 -
> > include/linux/security.h | 2 -
> > init/do_mounts.c | 2 +-
> > ipc/mqueue.c | 12 +-
> > kernel/bpf/inode.c | 15 +--
> > mm/shmem.c | 38 ++----
> > net/sunrpc/rpc_pipe.c | 27 ++---
> > security/apparmor/apparmorfs.c | 13 ++-
> > security/inode.c | 35 +++---
> > security/selinux/selinuxfs.c | 185 +++++++++++++-----------------
> > security/smack/smackfs.c | 2 +-
> > 53 files changed, 649 insertions(+), 834 deletions(-)
> >
> > Overview:
> >
> > First two commits are bugfixes (fusectl and tracefs resp.)
> >
> > [1/54] fuse_ctl_add_conn(): fix nlink breakage in case of early failure
> > [2/54] tracefs: fix a leak in eventfs_create_events_dir()
> >
> > Next, two commits adding a couple of useful helpers, the next three adding
> > the infrastructure and the rest consists of per-filesystem conversions.
> >
> > [3/54] new helper: simple_remove_by_name()
> > [4/54] new helper: simple_done_creating()
> > end_creating_path() analogue for internal object creation; unlike
> > end_creating_path() no mount is passed to it (or guaranteed to exist, for
> > that matter - it might be used during the filesystem setup, before the
> > superblock gets attached to any mounts).
> >
> > Infrastructure:
> > [5/54] introduce a flag for explicitly marking persistently pinned dentries
> > * introduce the new flag
> > * teach shrink_dcache_for_umount() to handle it (i.e. remove
> > and drop refcount on anything that survives to umount with that flag
> > still set)
> > * teach kill_litter_super() that anything with that flag does
> > *not* need to be unpinned.
> > [6/54] primitives for maintaining persisitency
> > * d_make_persistent(dentry, inode) - bump refcount, mark persistent
> > and make hashed positive. Return value is a borrowed reference to dentry;
> > it can be used until something removes persistency (at the very least,
> > until the parent gets unlocked, but some filesystems may have stronger
> > exclusion).
> > * d_make_discardable() - remove persistency mark and drop reference.
> >
> > NOTE: at that stage d_make_discardable() does not reject dentries not
> > marked persistent - it acts as if the mark been set.
> >
> > Rationale: less noise in series splitup that way. We want (and on the
> > next commit will get) simple_unlink() to do the right thing - remove
> > persistency, if it's there. However, it's used by many filesystems.
> > We would have either to convert them all at once or split simple_unlink()
> > into "want persistent" and "don't want persistent" versions, the latter
> > being the old one. In the course of the series almost all callers
> > would migrate to the replacement, leaving only two pathological cases
> > with the old one. The same goes for simple_rmdir() (two callers left in
> > the end), simple_recursive_removal() (all callers gone in the end), etc.
> > That's a lot of noise and it's easier to start with d_make_discardable()
> > quietly accepting non-persistent dentries, then, in the end, add private
> > copies of simple_unlink() and simple_rmdir() for two weird users (configfs
> > and apparmorfs) and have those use dput() instead of d_make_discardable().
> > At that point we'd be left with all callers of d_make_discardable()
> > always passing persistent dentries, allowing to add a warning in it.
> >
> > [7/54] convert simple_{link,unlink,rmdir,rename,fill_super}() to new primitives
> > See above re quietly accepting non-peristent dentries in
> > simple_unlink(), simple_rmdir(), etc.
> >
> > Converting filesystems:
> > [8/54] convert ramfs and tmpfs
> > [9/54] procfs: make /self and /thread_self dentries persistent
> > [10/54] configfs, securityfs: kill_litter_super() not needed
> > [11/54] convert xenfs
> > [12/54] convert smackfs
> > [13/54] convert hugetlbfs
> > [14/54] convert mqueue
> > [15/54] convert bpf
> > [16/54] convert dlmfs
> > [17/54] convert fuse_ctl
> > [18/54] convert pstore
> > [19/54] convert tracefs
> > [20/54] convert debugfs
> > [21/54] debugfs: remove duplicate checks in callers of start_creating()
> > [22/54] convert efivarfs
> > [23/54] convert spufs
> > [24/54] convert ibmasmfs
> > [25/54] ibmasmfs: get rid of ibmasmfs_dir_ops
> > [26/54] convert devpts
> > [27/54] binderfs: use simple_start_creating()
> > [28/54] binderfs_binder_ctl_create(): kill a bogus check
> > [29/54] convert binderfs
> > [30/54] autofs_{rmdir,unlink}: dentry->d_fsdata->dentry == dentry there
> > [31/54] convert autofs
> > [32/54] convert binfmt_misc
> > [33/54] selinuxfs: don't stash the dentry of /policy_capabilities
> > [34/54] selinuxfs: new helper for attaching files to tree
> > [35/54] convert selinuxfs
> >
> > Several functionfs fixes, before converting it, to make life
> > simpler for backporting:
> > [36/54] functionfs: don't abuse ffs_data_closed() on fs shutdown
> > [37/54] functionfs: don't bother with ffs->ref in ffs_data_{opened,closed}()
> > [38/54] functionfs: need to cancel ->reset_work in ->kill_sb()
> > [39/54] functionfs: fix the open/removal races
> >
> > ... and back to filesystems conversions:
> >
> > [40/54] functionfs: switch to simple_remove_by_name()
> > [41/54] convert functionfs
> > [42/54] gadgetfs: switch to simple_remove_by_name()
> > [43/54] convert gadgetfs
> > [44/54] hypfs: don't pin dentries twice
> > [45/54] hypfs: switch hypfs_create_str() to returning int
> > [46/54] hypfs: swich hypfs_create_u64() to returning int
> > [47/54] convert hypfs
> > [48/54] convert rpc_pipefs
> > [49/54] convert nfsctl
> > [50/54] convert rust_binderfs
> >
> > ... and no kill_litter_super() callers remain, so we
> > can take it out:
> > [51/54] get rid of kill_litter_super()
> >
> > Followups:
> > [52/54] convert securityfs
> > That was the last remaining user of simple_recursive_removal()
> > that did *not* mark things persistent. Now the only places where
> > d_make_discardable() is still called for dentries that are not marked
> > persistent are the calls of simple_{unlink,rmdir}() in configfs and
> > apparmorfs.
> >
> > [53/54] kill securityfs_recursive_remove()
> > Unused macro...
> >
> > [54/54] d_make_discardable(): warn if given a non-persistent dentry
> >
> > At this point there are very few call chains that might lead to
> > d_make_discardable() on a dentry that hadn't been made persistent:
> > calls of simple_unlink() and simple_rmdir() in configfs and
> > apparmorfs.
> >
> > Both filesystems do pin (part of) their contents in dcache, but
> > they are currently playing very unusual games with that. Converting
> > them to more usual patterns might be possible, but it's definitely
> > going to be a long series of changes in both cases.
> >
> > For now the easiest solution is to have both stop using simple_unlink()
> > and simple_rmdir() - that allows to make d_make_discardable() warn
> > when given a non-persistent dentry.
> >
> > Rather than giving them full-blown private copies (with calls of
> > d_make_discardable() replaced with dput()), let's pull the parts of
> > simple_unlink() and simple_rmdir() that deal with timestamps and link
> > counts into separate helpers (__simple_unlink() and __simple_rmdir()
> > resp.) and have those used by configfs and apparmorfs.
> >
>
> Hi Al, when I apply this patchset my Pixel 6 no longer enumerates on
> lsusb or ADB. It was quite hard to bisect to this point, as this is
> non-deterministic and seems to be setup specific. Note, I am using
> android-mainline, but my understanding is that this build does not
> have any out-of-tree USB patches, and that there are no vendor hooks
> in the build.
>
> My apologies as I can't offer any other clues; there are no obviously
> bad dmesg logs and I'm still working on narrowing down the exact
> commit(s) that started this, but just wanted to send a FYI in case
> something stands out as obvious.
Note that I had to revert commit e5bf5ee26663 ("functionfs: fix the
open/removal races") from the stable backports, as it was causing issues
on the pixel devices it got backported to. So perhaps look there?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-27 7:42 ` Greg KH
@ 2026-01-27 18:39 ` Linus Torvalds
2026-01-27 20:14 ` Al Viro
2026-01-28 2:02 ` Samuel Wu
1 sibling, 1 reply; 26+ messages in thread
From: Linus Torvalds @ 2026-01-27 18:39 UTC (permalink / raw)
To: Greg KH
Cc: Samuel Wu, Al Viro, linux-fsdevel, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Mon, 26 Jan 2026 at 23:42, Greg KH <gregkh@linuxfoundation.org> wrote:
>
> Note that I had to revert commit e5bf5ee26663 ("functionfs: fix the
> open/removal races") from the stable backports, as it was causing issues
> on the pixel devices it got backported to. So perhaps look there?
Hmm. That commit is obviously still upstream, do we understand why it
caused problems in the backports?
Linus
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-27 18:39 ` Linus Torvalds
@ 2026-01-27 20:14 ` Al Viro
2026-01-28 8:53 ` Greg KH
0 siblings, 1 reply; 26+ messages in thread
From: Al Viro @ 2026-01-27 20:14 UTC (permalink / raw)
To: Linus Torvalds
Cc: Greg KH, Samuel Wu, linux-fsdevel, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Tue, Jan 27, 2026 at 10:39:04AM -0800, Linus Torvalds wrote:
> On Mon, 26 Jan 2026 at 23:42, Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > Note that I had to revert commit e5bf5ee26663 ("functionfs: fix the
> > open/removal races") from the stable backports, as it was causing issues
> > on the pixel devices it got backported to. So perhaps look there?
>
> Hmm. That commit is obviously still upstream, do we understand why it
> caused problems in the backports?
This is all I've seen:
| It has been reported to cause test problems in Android devices. As the
| other functionfs changes were not also backported at the same time,
| something is out of sync. So just revert this one for now and it can
| come back in the future as a patch series if it is tested.
My apologies for not following up on that one; Greg, could you give some
references to those reports?
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-27 7:42 ` Greg KH
2026-01-27 18:39 ` Linus Torvalds
@ 2026-01-28 2:02 ` Samuel Wu
2026-01-28 4:59 ` Al Viro
1 sibling, 1 reply; 26+ messages in thread
From: Samuel Wu @ 2026-01-28 2:02 UTC (permalink / raw)
To: Greg KH
Cc: Al Viro, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Mon, Jan 26, 2026 at 11:42 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Mon, Jan 26, 2026 at 04:56:42PM -0800, Samuel Wu wrote:
> > On Mon, Nov 17, 2025 at 9:15 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> > >
> > > Some filesystems use a kinda-sorta controlled dentry refcount leak to pin
> > > dentries of created objects in dcache (and undo it when removing those).
> > > Reference is grabbed and not released, but it's not actually _stored_
> > > anywhere. That works, but it's hard to follow and verify; among other
> > > things, we have no way to tell _which_ of the increments is intended
> > > to be an unpaired one. Worse, on removal we need to decide whether
> > > the reference had already been dropped, which can be non-trivial if
> > > that removal is on umount and we need to figure out if this dentry is
> > > pinned due to e.g. unlink() not done. Usually that is handled by using
> > > kill_litter_super() as ->kill_sb(), but there are open-coded special
> > > cases of the same (consider e.g. /proc/self).
> > >
> > > Things get simpler if we introduce a new dentry flag (DCACHE_PERSISTENT)
> > > marking those "leaked" dentries. Having it set claims responsibility
> > > for +1 in refcount.
> > >
> > > The end result this series is aiming for:
> > >
> > > * get these unbalanced dget() and dput() replaced with new primitives that
> > > would, in addition to adjusting refcount, set and clear persistency flag.
> > > * instead of having kill_litter_super() mess with removing the remaining
> > > "leaked" references (e.g. for all tmpfs files that hadn't been removed
> > > prior to umount), have the regular shrink_dcache_for_umount() strip
> > > DCACHE_PERSISTENT of all dentries, dropping the corresponding
> > > reference if it had been set. After that kill_litter_super() becomes
> > > an equivalent of kill_anon_super().
> > >
> > > Doing that in a single step is not feasible - it would affect too many places
> > > in too many filesystems. It has to be split into a series.
> > >
> > > This work has really started early in 2024; quite a few preliminary pieces
> > > have already gone into mainline. This chunk is finally getting to the
> > > meat of that stuff - infrastructure and most of the conversions to it.
> > >
> > > Some pieces are still sitting in the local branches, but the bulk of
> > > that stuff is here.
> > >
> > > Compared to v3:
> > > * fixed a functionfs braino around ffs_epfiles_destroy() (in #40/54,
> > > used to be #36/50).
> > > * added fixes for a couple of UAF in functionfs (##36--39); that
> > > does *NOT* include any fixes for dmabuf bugs Chris posted last week, though.
> > >
> > > The branch is -rc5-based; it lives in
> > > git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #work.persistency
> > > individual patches in followups.
> > >
> > > Please, help with review and testing. If nobody objects, in a few days it
> > > goes into #for-next.
> > >
> > > Shortlog:
> > > fuse_ctl_add_conn(): fix nlink breakage in case of early failure
> > > tracefs: fix a leak in eventfs_create_events_dir()
> > > new helper: simple_remove_by_name()
> > > new helper: simple_done_creating()
> > > introduce a flag for explicitly marking persistently pinned dentries
> > > primitives for maintaining persisitency
> > > convert simple_{link,unlink,rmdir,rename,fill_super}() to new primitives
> > > convert ramfs and tmpfs
> > > procfs: make /self and /thread_self dentries persistent
> > > configfs, securityfs: kill_litter_super() not needed
> > > convert xenfs
> > > convert smackfs
> > > convert hugetlbfs
> > > convert mqueue
> > > convert bpf
> > > convert dlmfs
> > > convert fuse_ctl
> > > convert pstore
> > > convert tracefs
> > > convert debugfs
> > > debugfs: remove duplicate checks in callers of start_creating()
> > > convert efivarfs
> > > convert spufs
> > > convert ibmasmfs
> > > ibmasmfs: get rid of ibmasmfs_dir_ops
> > > convert devpts
> > > binderfs: use simple_start_creating()
> > > binderfs_binder_ctl_create(): kill a bogus check
> > > convert binderfs
> > > autofs_{rmdir,unlink}: dentry->d_fsdata->dentry == dentry there
> > > convert autofs
> > > convert binfmt_misc
> > > selinuxfs: don't stash the dentry of /policy_capabilities
> > > selinuxfs: new helper for attaching files to tree
> > > convert selinuxfs
> > > functionfs: don't abuse ffs_data_closed() on fs shutdown
> > > functionfs: don't bother with ffs->ref in ffs_data_{opened,closed}()
> > > functionfs: need to cancel ->reset_work in ->kill_sb()
> > > functionfs: fix the open/removal races
> > > functionfs: switch to simple_remove_by_name()
> > > convert functionfs
> > > gadgetfs: switch to simple_remove_by_name()
> > > convert gadgetfs
> > > hypfs: don't pin dentries twice
> > > hypfs: switch hypfs_create_str() to returning int
> > > hypfs: swich hypfs_create_u64() to returning int
> > > convert hypfs
> > > convert rpc_pipefs
> > > convert nfsctl
> > > convert rust_binderfs
> > > get rid of kill_litter_super()
> > > convert securityfs
> > > kill securityfs_recursive_remove()
> > > d_make_discardable(): warn if given a non-persistent dentry
> > >
> > > Diffstat:
> > > Documentation/filesystems/porting.rst | 7 ++
> > > arch/powerpc/platforms/cell/spufs/inode.c | 17 ++-
> > > arch/s390/hypfs/hypfs.h | 6 +-
> > > arch/s390/hypfs/hypfs_diag_fs.c | 60 ++++------
> > > arch/s390/hypfs/hypfs_vm_fs.c | 21 ++--
> > > arch/s390/hypfs/inode.c | 82 +++++--------
> > > drivers/android/binder/rust_binderfs.c | 121 ++++++-------------
> > > drivers/android/binderfs.c | 82 +++----------
> > > drivers/base/devtmpfs.c | 2 +-
> > > drivers/misc/ibmasm/ibmasmfs.c | 24 ++--
> > > drivers/usb/gadget/function/f_fs.c | 144 +++++++++++++----------
> > > drivers/usb/gadget/legacy/inode.c | 49 ++++----
> > > drivers/xen/xenfs/super.c | 2 +-
> > > fs/autofs/inode.c | 2 +-
> > > fs/autofs/root.c | 11 +-
> > > fs/binfmt_misc.c | 69 ++++++-----
> > > fs/configfs/dir.c | 10 +-
> > > fs/configfs/inode.c | 3 +-
> > > fs/configfs/mount.c | 2 +-
> > > fs/dcache.c | 111 +++++++++++-------
> > > fs/debugfs/inode.c | 32 ++----
> > > fs/devpts/inode.c | 57 ++++-----
> > > fs/efivarfs/inode.c | 7 +-
> > > fs/efivarfs/super.c | 5 +-
> > > fs/fuse/control.c | 38 +++---
> > > fs/hugetlbfs/inode.c | 12 +-
> > > fs/internal.h | 1 -
> > > fs/libfs.c | 52 +++++++--
> > > fs/nfsd/nfsctl.c | 18 +--
> > > fs/ocfs2/dlmfs/dlmfs.c | 8 +-
> > > fs/proc/base.c | 6 +-
> > > fs/proc/internal.h | 1 +
> > > fs/proc/root.c | 14 +--
> > > fs/proc/self.c | 10 +-
> > > fs/proc/thread_self.c | 11 +-
> > > fs/pstore/inode.c | 7 +-
> > > fs/ramfs/inode.c | 8 +-
> > > fs/super.c | 8 --
> > > fs/tracefs/event_inode.c | 7 +-
> > > fs/tracefs/inode.c | 13 +--
> > > include/linux/dcache.h | 4 +-
> > > include/linux/fs.h | 6 +-
> > > include/linux/proc_fs.h | 2 -
> > > include/linux/security.h | 2 -
> > > init/do_mounts.c | 2 +-
> > > ipc/mqueue.c | 12 +-
> > > kernel/bpf/inode.c | 15 +--
> > > mm/shmem.c | 38 ++----
> > > net/sunrpc/rpc_pipe.c | 27 ++---
> > > security/apparmor/apparmorfs.c | 13 ++-
> > > security/inode.c | 35 +++---
> > > security/selinux/selinuxfs.c | 185 +++++++++++++-----------------
> > > security/smack/smackfs.c | 2 +-
> > > 53 files changed, 649 insertions(+), 834 deletions(-)
> > >
> > > Overview:
> > >
> > > First two commits are bugfixes (fusectl and tracefs resp.)
> > >
> > > [1/54] fuse_ctl_add_conn(): fix nlink breakage in case of early failure
> > > [2/54] tracefs: fix a leak in eventfs_create_events_dir()
> > >
> > > Next, two commits adding a couple of useful helpers, the next three adding
> > > the infrastructure and the rest consists of per-filesystem conversions.
> > >
> > > [3/54] new helper: simple_remove_by_name()
> > > [4/54] new helper: simple_done_creating()
> > > end_creating_path() analogue for internal object creation; unlike
> > > end_creating_path() no mount is passed to it (or guaranteed to exist, for
> > > that matter - it might be used during the filesystem setup, before the
> > > superblock gets attached to any mounts).
> > >
> > > Infrastructure:
> > > [5/54] introduce a flag for explicitly marking persistently pinned dentries
> > > * introduce the new flag
> > > * teach shrink_dcache_for_umount() to handle it (i.e. remove
> > > and drop refcount on anything that survives to umount with that flag
> > > still set)
> > > * teach kill_litter_super() that anything with that flag does
> > > *not* need to be unpinned.
> > > [6/54] primitives for maintaining persisitency
> > > * d_make_persistent(dentry, inode) - bump refcount, mark persistent
> > > and make hashed positive. Return value is a borrowed reference to dentry;
> > > it can be used until something removes persistency (at the very least,
> > > until the parent gets unlocked, but some filesystems may have stronger
> > > exclusion).
> > > * d_make_discardable() - remove persistency mark and drop reference.
> > >
> > > NOTE: at that stage d_make_discardable() does not reject dentries not
> > > marked persistent - it acts as if the mark been set.
> > >
> > > Rationale: less noise in series splitup that way. We want (and on the
> > > next commit will get) simple_unlink() to do the right thing - remove
> > > persistency, if it's there. However, it's used by many filesystems.
> > > We would have either to convert them all at once or split simple_unlink()
> > > into "want persistent" and "don't want persistent" versions, the latter
> > > being the old one. In the course of the series almost all callers
> > > would migrate to the replacement, leaving only two pathological cases
> > > with the old one. The same goes for simple_rmdir() (two callers left in
> > > the end), simple_recursive_removal() (all callers gone in the end), etc.
> > > That's a lot of noise and it's easier to start with d_make_discardable()
> > > quietly accepting non-persistent dentries, then, in the end, add private
> > > copies of simple_unlink() and simple_rmdir() for two weird users (configfs
> > > and apparmorfs) and have those use dput() instead of d_make_discardable().
> > > At that point we'd be left with all callers of d_make_discardable()
> > > always passing persistent dentries, allowing to add a warning in it.
> > >
> > > [7/54] convert simple_{link,unlink,rmdir,rename,fill_super}() to new primitives
> > > See above re quietly accepting non-peristent dentries in
> > > simple_unlink(), simple_rmdir(), etc.
> > >
> > > Converting filesystems:
> > > [8/54] convert ramfs and tmpfs
> > > [9/54] procfs: make /self and /thread_self dentries persistent
> > > [10/54] configfs, securityfs: kill_litter_super() not needed
> > > [11/54] convert xenfs
> > > [12/54] convert smackfs
> > > [13/54] convert hugetlbfs
> > > [14/54] convert mqueue
> > > [15/54] convert bpf
> > > [16/54] convert dlmfs
> > > [17/54] convert fuse_ctl
> > > [18/54] convert pstore
> > > [19/54] convert tracefs
> > > [20/54] convert debugfs
> > > [21/54] debugfs: remove duplicate checks in callers of start_creating()
> > > [22/54] convert efivarfs
> > > [23/54] convert spufs
> > > [24/54] convert ibmasmfs
> > > [25/54] ibmasmfs: get rid of ibmasmfs_dir_ops
> > > [26/54] convert devpts
> > > [27/54] binderfs: use simple_start_creating()
> > > [28/54] binderfs_binder_ctl_create(): kill a bogus check
> > > [29/54] convert binderfs
> > > [30/54] autofs_{rmdir,unlink}: dentry->d_fsdata->dentry == dentry there
> > > [31/54] convert autofs
> > > [32/54] convert binfmt_misc
> > > [33/54] selinuxfs: don't stash the dentry of /policy_capabilities
> > > [34/54] selinuxfs: new helper for attaching files to tree
> > > [35/54] convert selinuxfs
> > >
> > > Several functionfs fixes, before converting it, to make life
> > > simpler for backporting:
> > > [36/54] functionfs: don't abuse ffs_data_closed() on fs shutdown
> > > [37/54] functionfs: don't bother with ffs->ref in ffs_data_{opened,closed}()
> > > [38/54] functionfs: need to cancel ->reset_work in ->kill_sb()
> > > [39/54] functionfs: fix the open/removal races
> > >
> > > ... and back to filesystems conversions:
> > >
> > > [40/54] functionfs: switch to simple_remove_by_name()
> > > [41/54] convert functionfs
> > > [42/54] gadgetfs: switch to simple_remove_by_name()
> > > [43/54] convert gadgetfs
> > > [44/54] hypfs: don't pin dentries twice
> > > [45/54] hypfs: switch hypfs_create_str() to returning int
> > > [46/54] hypfs: swich hypfs_create_u64() to returning int
> > > [47/54] convert hypfs
> > > [48/54] convert rpc_pipefs
> > > [49/54] convert nfsctl
> > > [50/54] convert rust_binderfs
> > >
> > > ... and no kill_litter_super() callers remain, so we
> > > can take it out:
> > > [51/54] get rid of kill_litter_super()
> > >
> > > Followups:
> > > [52/54] convert securityfs
> > > That was the last remaining user of simple_recursive_removal()
> > > that did *not* mark things persistent. Now the only places where
> > > d_make_discardable() is still called for dentries that are not marked
> > > persistent are the calls of simple_{unlink,rmdir}() in configfs and
> > > apparmorfs.
> > >
> > > [53/54] kill securityfs_recursive_remove()
> > > Unused macro...
> > >
> > > [54/54] d_make_discardable(): warn if given a non-persistent dentry
> > >
> > > At this point there are very few call chains that might lead to
> > > d_make_discardable() on a dentry that hadn't been made persistent:
> > > calls of simple_unlink() and simple_rmdir() in configfs and
> > > apparmorfs.
> > >
> > > Both filesystems do pin (part of) their contents in dcache, but
> > > they are currently playing very unusual games with that. Converting
> > > them to more usual patterns might be possible, but it's definitely
> > > going to be a long series of changes in both cases.
> > >
> > > For now the easiest solution is to have both stop using simple_unlink()
> > > and simple_rmdir() - that allows to make d_make_discardable() warn
> > > when given a non-persistent dentry.
> > >
> > > Rather than giving them full-blown private copies (with calls of
> > > d_make_discardable() replaced with dput()), let's pull the parts of
> > > simple_unlink() and simple_rmdir() that deal with timestamps and link
> > > counts into separate helpers (__simple_unlink() and __simple_rmdir()
> > > resp.) and have those used by configfs and apparmorfs.
> > >
> >
> > Hi Al, when I apply this patchset my Pixel 6 no longer enumerates on
> > lsusb or ADB. It was quite hard to bisect to this point, as this is
> > non-deterministic and seems to be setup specific. Note, I am using
> > android-mainline, but my understanding is that this build does not
> > have any out-of-tree USB patches, and that there are no vendor hooks
> > in the build.
> >
> > My apologies as I can't offer any other clues; there are no obviously
> > bad dmesg logs and I'm still working on narrowing down the exact
> > commit(s) that started this, but just wanted to send a FYI in case
> > something stands out as obvious.
>
> Note that I had to revert commit e5bf5ee26663 ("functionfs: fix the
> open/removal races") from the stable backports, as it was causing issues
> on the pixel devices it got backported to. So perhaps look there?
>
> thanks,
>
> greg k-h
Thanks for the suggestion. I tried a few different setups, and now I'm
fairly confident e5bf5ee26663 ("functionfs: fix the open/removal
races") is the culprit. I did have to revert 6ca67378d0e7 ("convert
functionfs") and c7747fafaba0 ("functionfs: switch to
simple_remove_by_name()") to successfully build, but reverting only
those two in isolation did not fix the issue.
Al, please let me know if you have any other variant of the patch(s)
that you want tested, otherwise feel free to add these tags as
appropriate:
Reported-by: Samuel Wu <wusamuel@google.com>
Tested-by: Samuel Wu <wusamuel@google.com>
Thanks!
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-28 2:02 ` Samuel Wu
@ 2026-01-28 4:59 ` Al Viro
2026-01-29 0:58 ` Samuel Wu
0 siblings, 1 reply; 26+ messages in thread
From: Al Viro @ 2026-01-28 4:59 UTC (permalink / raw)
To: Samuel Wu
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Tue, Jan 27, 2026 at 06:02:25PM -0800, Samuel Wu wrote:
> Thanks for the suggestion. I tried a few different setups, and now I'm
> fairly confident e5bf5ee26663 ("functionfs: fix the open/removal
> races") is the culprit. I did have to revert 6ca67378d0e7 ("convert
> functionfs") and c7747fafaba0 ("functionfs: switch to
> simple_remove_by_name()") to successfully build, but reverting only
> those two in isolation did not fix the issue.
Very interesting... Does 1544775687f0 (parent of e5bf5ee26663)
demonstrate that behaviour?
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-27 20:14 ` Al Viro
@ 2026-01-28 8:53 ` Greg KH
0 siblings, 0 replies; 26+ messages in thread
From: Greg KH @ 2026-01-28 8:53 UTC (permalink / raw)
To: Al Viro
Cc: Linus Torvalds, Samuel Wu, linux-fsdevel, brauner, jack, raven,
miklos, neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees,
rostedt, linux-usb, paul, casey, linuxppc-dev, john.johansen,
selinux, borntraeger, bpf, clm, android-kernel-team
On Tue, Jan 27, 2026 at 08:14:54PM +0000, Al Viro wrote:
> On Tue, Jan 27, 2026 at 10:39:04AM -0800, Linus Torvalds wrote:
> > On Mon, 26 Jan 2026 at 23:42, Greg KH <gregkh@linuxfoundation.org> wrote:
> > >
> > > Note that I had to revert commit e5bf5ee26663 ("functionfs: fix the
> > > open/removal races") from the stable backports, as it was causing issues
> > > on the pixel devices it got backported to. So perhaps look there?
> >
> > Hmm. That commit is obviously still upstream, do we understand why it
> > caused problems in the backports?
>
> This is all I've seen:
>
> | It has been reported to cause test problems in Android devices. As the
> | other functionfs changes were not also backported at the same time,
> | something is out of sync. So just revert this one for now and it can
> | come back in the future as a patch series if it is tested.
>
> My apologies for not following up on that one; Greg, could you give some
> references to those reports?
Sorry, all I got was a "this commit caused devices to fail" and was
found from bisection, on the 6.18.y tree. Samuel has much more
information as to exactly what is happening here as he can see the test
results properly, I'll let him work through this, thanks!
greg k-h
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-28 4:59 ` Al Viro
@ 2026-01-29 0:58 ` Samuel Wu
2026-01-29 3:23 ` Al Viro
0 siblings, 1 reply; 26+ messages in thread
From: Samuel Wu @ 2026-01-29 0:58 UTC (permalink / raw)
To: Al Viro
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Tue, Jan 27, 2026 at 8:58 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> Very interesting... Does 1544775687f0 (parent of e5bf5ee26663)
> demonstrate that behaviour?
Reverting only 1544775687f0 (functionfs: need to cancel ->reset_work
in ->kill_sb()) does not fix the issue. With 6.19-rc7 as baseline, the
simplest working recipe at the moment is with 6ca67378d0e7,
c7747fafaba0, and e5bf5ee26663 reverted.
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-29 0:58 ` Samuel Wu
@ 2026-01-29 3:23 ` Al Viro
2026-01-29 22:54 ` Al Viro
0 siblings, 1 reply; 26+ messages in thread
From: Al Viro @ 2026-01-29 3:23 UTC (permalink / raw)
To: Samuel Wu
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Wed, Jan 28, 2026 at 04:58:57PM -0800, Samuel Wu wrote:
> On Tue, Jan 27, 2026 at 8:58 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> > Very interesting... Does 1544775687f0 (parent of e5bf5ee26663)
> > demonstrate that behaviour?
>
> Reverting only 1544775687f0 (functionfs: need to cancel ->reset_work
> in ->kill_sb()) does not fix the issue. With 6.19-rc7 as baseline, the
> simplest working recipe at the moment is with 6ca67378d0e7,
> c7747fafaba0, and e5bf5ee26663 reverted.
Sorry, I hadn't been clear enough: if you do
git switch --detach 1544775687f0
and build the resulting tree, does the breakage reproduce? What I want
to do is to split e5bf5ee26663 into smaller steps and see which one
introduces the breakage, but the starting point would be verify that
there's no breakage prior to that.
PS: v6.19-rc7 contains fc45aee66223 ("get rid of kill_litter_super()"),
and reverting 6ca67378d0e7 ("convert functionfs") would reintroduce
the call of that function in ffs_fs_kill_sb(), so the resulting tree
won't even build on any configs with functionfs enabledd; are you sure
that you'd been testing v6.19-rc7 + reverts of just these 3 commits?
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-29 3:23 ` Al Viro
@ 2026-01-29 22:54 ` Al Viro
2026-01-30 1:16 ` Samuel Wu
0 siblings, 1 reply; 26+ messages in thread
From: Al Viro @ 2026-01-29 22:54 UTC (permalink / raw)
To: Samuel Wu
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Thu, Jan 29, 2026 at 03:23:35AM +0000, Al Viro wrote:
> On Wed, Jan 28, 2026 at 04:58:57PM -0800, Samuel Wu wrote:
> > On Tue, Jan 27, 2026 at 8:58 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> >
> > > Very interesting... Does 1544775687f0 (parent of e5bf5ee26663)
> > > demonstrate that behaviour?
> >
> > Reverting only 1544775687f0 (functionfs: need to cancel ->reset_work
> > in ->kill_sb()) does not fix the issue. With 6.19-rc7 as baseline, the
> > simplest working recipe at the moment is with 6ca67378d0e7,
> > c7747fafaba0, and e5bf5ee26663 reverted.
>
> Sorry, I hadn't been clear enough: if you do
> git switch --detach 1544775687f0
> and build the resulting tree, does the breakage reproduce? What I want
> to do is to split e5bf5ee26663 into smaller steps and see which one
> introduces the breakage, but the starting point would be verify that
> there's no breakage prior to that.
>
>
> PS: v6.19-rc7 contains fc45aee66223 ("get rid of kill_litter_super()"),
> and reverting 6ca67378d0e7 ("convert functionfs") would reintroduce
> the call of that function in ffs_fs_kill_sb(), so the resulting tree
> won't even build on any configs with functionfs enabledd; are you sure
> that you'd been testing v6.19-rc7 + reverts of just these 3 commits?
Could you try your reproducer on mainline with the following delta applied?
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 05c6750702b6..6c6d55ba0749 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -646,12 +646,11 @@ static int ffs_ep0_open(struct inode *inode, struct file *file)
if (ret < 0)
return ret;
- ffs_data_opened(ffs);
if (ffs->state == FFS_CLOSING) {
- ffs_data_closed(ffs);
mutex_unlock(&ffs->mutex);
return -EBUSY;
}
+ ffs_data_opened(ffs);
mutex_unlock(&ffs->mutex);
file->private_data = ffs;
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-29 22:54 ` Al Viro
@ 2026-01-30 1:16 ` Samuel Wu
2026-01-30 7:04 ` Al Viro
2026-01-31 14:58 ` Krishna Kurapati PSSNV
0 siblings, 2 replies; 26+ messages in thread
From: Samuel Wu @ 2026-01-30 1:16 UTC (permalink / raw)
To: Al Viro
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Thu, Jan 29, 2026 at 2:52 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> > Sorry, I hadn't been clear enough: if you do
> > git switch --detach 1544775687f0
> > and build the resulting tree, does the breakage reproduce? What I want
> > to do is to split e5bf5ee26663 into smaller steps and see which one
> > introduces the breakage, but the starting point would be verify that
> > there's no breakage prior to that.
Ultimately, same conclusion as before: 6.18-rc5 with patches up to
1544775687f0 works, but adding e5bf5ee26663 breaks it.
> > PS: v6.19-rc7 contains fc45aee66223 ("get rid of kill_litter_super()"),
> > and reverting 6ca67378d0e7 ("convert functionfs") would reintroduce
> > the call of that function in ffs_fs_kill_sb(), so the resulting tree
> > won't even build on any configs with functionfs enabledd; are you sure
> > that you'd been testing v6.19-rc7 + reverts of just these 3 commits?
I also could have been more clear- I had to
s/kill_anon_super/kill_litter_super/ as part of the revert of
6ca67378d0e7 to properly build. That felt like an appropriate change,
but if not, adding patches on top of 6.18-rc5 is perfectly fine for
testing this.
> Could you try your reproducer on mainline with the following delta applied?
>
> diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
> index 05c6750702b6..6c6d55ba0749 100644
> --- a/drivers/usb/gadget/function/f_fs.c
> +++ b/drivers/usb/gadget/function/f_fs.c
> @@ -646,12 +646,11 @@ static int ffs_ep0_open(struct inode *inode, struct file *file)
> if (ret < 0)
> return ret;
>
> - ffs_data_opened(ffs);
> if (ffs->state == FFS_CLOSING) {
> - ffs_data_closed(ffs);
> mutex_unlock(&ffs->mutex);
> return -EBUSY;
> }
> + ffs_data_opened(ffs);
> mutex_unlock(&ffs->mutex);
> file->private_data = ffs;
>
This didn't work on either build variant (6.18-rc5 and 6.19-rc7).
I'm exploring a few other paths, but not having USB access makes
traditional tools a bit difficult. One thing I'm rechecking and is
worth mentioning is the lockdep below: it's been present for quite
some time now, but I'm not sure if it would have some undesired
interaction with your patch.
[ BUG: Invalid wait context ]
6.18.0-rc5-mainline-maybe-dirty-4k
-----------------------------
irq/360-dwc3/352 is trying to lock:
ffffff800792deb8 (&psy->extensions_sem){.+.+}-{3:3}, at:
__power_supply_set_property+0x40/0x180
other info that might help us debug this:
context-{4:4}
1 lock held by irq/360-dwc3/352:
#0: ffffff8017bb98f0 (&gi->spinlock){....}-{2:2}, at:
configfs_composite_suspend+0x28/0x68
Call trace:
show_stack+0x18/0x28 (C)
__dump_stack+0x28/0x3c
dump_stack_lvl+0xac/0xf0
dump_stack+0x18/0x3c
__lock_acquire+0x794/0x2bec
lock_acquire+0x148/0x2cc
down_read+0x3c/0x194
__power_supply_set_property+0x40/0x180
power_supply_set_property+0x14/0x20
dwc3_gadget_vbus_draw+0x8c/0xcc
usb_gadget_vbus_draw+0x48/0x130
composite_suspend+0xcc/0xe4
configfs_composite_suspend+0x44/0x68
dwc3_thread_interrupt+0x8f8/0xc88
irq_thread_fn+0x48/0xa8
irq_thread+0x150/0x31c
kthread+0x150/0x280
ret_from_fork+0x10/0x20
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-30 1:16 ` Samuel Wu
@ 2026-01-30 7:04 ` Al Viro
2026-01-30 22:31 ` Samuel Wu
2026-01-31 14:58 ` Krishna Kurapati PSSNV
1 sibling, 1 reply; 26+ messages in thread
From: Al Viro @ 2026-01-30 7:04 UTC (permalink / raw)
To: Samuel Wu
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Thu, Jan 29, 2026 at 05:16:20PM -0800, Samuel Wu wrote:
> On Thu, Jan 29, 2026 at 2:52 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> > > Sorry, I hadn't been clear enough: if you do
> > > git switch --detach 1544775687f0
> > > and build the resulting tree, does the breakage reproduce? What I want
> > > to do is to split e5bf5ee26663 into smaller steps and see which one
> > > introduces the breakage, but the starting point would be verify that
> > > there's no breakage prior to that.
>
> Ultimately, same conclusion as before: 6.18-rc5 with patches up to
> 1544775687f0 works, but adding e5bf5ee26663 breaks it.
OK. Could you take a clone of mainline repository and in there run
; git fetch git://git.kernel.org:/pub/scm/linux/kernel/git/viro/vfs.git for-wsamuel:for-wsamuel
then
; git diff for-wsamuel e5bf5ee26663
to verify that for-wsamuel is identical to tree you've seen breakage on
; git diff for-wsamuel-base 1544775687f0
to verify that for-wsamuel-base is the tree where the breakage did not reproduce
Then bisect from for-wsamuel-base to for-wsamuel.
Basically, that's the offending commit split into steps; let's try to figure
out what causes the breakage with better resolution...
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-30 7:04 ` Al Viro
@ 2026-01-30 22:31 ` Samuel Wu
2026-01-30 23:57 ` Al Viro
0 siblings, 1 reply; 26+ messages in thread
From: Samuel Wu @ 2026-01-30 22:31 UTC (permalink / raw)
To: Al Viro
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Thu, Jan 29, 2026 at 11:02 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> OK. Could you take a clone of mainline repository and in there run
> ; git fetch git://git.kernel.org:/pub/scm/linux/kernel/git/viro/vfs.git for-wsamuel:for-wsamuel
> then
> ; git diff for-wsamuel e5bf5ee26663
> to verify that for-wsamuel is identical to tree you've seen breakage on
> ; git diff for-wsamuel-base 1544775687f0
> to verify that for-wsamuel-base is the tree where the breakage did not reproduce
> Then bisect from for-wsamuel-base to for-wsamuel.
>
> Basically, that's the offending commit split into steps; let's try to figure
> out what causes the breakage with better resolution...
Confirming that bisect points to this patch: 09e88dc22ea2 (serialize
ffs_ep0_open() on ffs->mutex)
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-30 22:31 ` Samuel Wu
@ 2026-01-30 23:57 ` Al Viro
2026-01-31 0:14 ` Linus Torvalds
` (2 more replies)
0 siblings, 3 replies; 26+ messages in thread
From: Al Viro @ 2026-01-30 23:57 UTC (permalink / raw)
To: Samuel Wu
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Fri, Jan 30, 2026 at 02:31:54PM -0800, Samuel Wu wrote:
> On Thu, Jan 29, 2026 at 11:02 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> > OK. Could you take a clone of mainline repository and in there run
> > ; git fetch git://git.kernel.org:/pub/scm/linux/kernel/git/viro/vfs.git for-wsamuel:for-wsamuel
> > then
> > ; git diff for-wsamuel e5bf5ee26663
> > to verify that for-wsamuel is identical to tree you've seen breakage on
> > ; git diff for-wsamuel-base 1544775687f0
> > to verify that for-wsamuel-base is the tree where the breakage did not reproduce
> > Then bisect from for-wsamuel-base to for-wsamuel.
> >
> > Basically, that's the offending commit split into steps; let's try to figure
> > out what causes the breakage with better resolution...
>
> Confirming that bisect points to this patch: 09e88dc22ea2 (serialize
> ffs_ep0_open() on ffs->mutex)
So we have something that does O_NDELAY opens of ep0 *and* does not retry on
EAGAIN?
How lovely... Could you slap
WARN_ON(ret == -EAGAIN);
right before that
if (ret < 0)
return ret;
in there and see which process is doing that? Regression is a regression,
odd userland or not, but I would like to see what is that userland actually
trying to do there.
*grumble*
IMO at that point we have two problems - one is how to avoid a revert of the
tail of tree-in-dcache series, another is how to deal with quite real
preexisting bugs in functionfs.
Another thing to try (not as a suggestion of a fix, just an attempt to figure
out how badly would the things break): in current mainline replace that
ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK)
in ffs_ep0_open() with
ffs_mutex_lock(&ffs->mutex, false)
and see how badly do the things regress for userland. Again, I'm not saying
that this is a fix - just trying to get some sense of what's the userland
is doing.
FWIW, it might make sense to try a lighter serialization in ffs_ep0_open() -
taking it there is due to the following scenario (assuming 6.18 or earlier):
ffs->state is FFS_DEACTIVATED. ffs->opened is 0. Two threads attempt to
open ep0. Here's what happens prior to these patches:
static int ffs_ep0_open(struct inode *inode, struct file *file)
{
struct ffs_data *ffs = inode->i_private;
if (ffs->state == FFS_CLOSING)
return -EBUSY;
file->private_data = ffs;
ffs_data_opened(ffs);
with
static void ffs_data_opened(struct ffs_data *ffs)
{
refcount_inc(&ffs->ref);
if (atomic_add_return(1, &ffs->opened) == 1 &&
ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
ffs_data_reset(ffs);
}
}
IOW, the sequence is
if (state == FFS_CLOSING)
return -EBUSY;
n = atomic_add_return(1, &opened);
if (n == 1 && state == FFS_DEACTIVATED) {
state = FFS_CLOSING;
ffs_data_reset();
See the race there? If the second open() comes between the
increment of ffs->opened and setting the state to FFS_CLOSING,
it will *not* fail with EBUSY - it will proceed to return to
userland, while the first sucker is crawling through the work
in ffs_data_reset()/ffs_data_clear()/ffs_epfiles_destroy().
What's more, there's nothing to stop that second opener from
calling write() on the descriptor it got. No exclusion there -
ffs->state = FFS_READ_DESCRIPTORS;
ffs->setup_state = FFS_NO_SETUP;
ffs->flags = 0;
in ffs_data_reset() is *not* serialized against ffs_ep0_write().
Get preempted right after setting ->state and that write()
will go just fine, only to be surprised when the first thread
regains CPU and continues modifying the contents of *ffs
under whatever the second thread is doing.
That code obviously relies upon that kind of shit being prevented
by that -EBUSY logics in ep0 open() and that logics is obviously
racy as it is. Note that other callers of ffs_data_reset() have
similar problem: ffs_func_set_alt(), for example has
if (ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
INIT_WORK(&ffs->reset_work, ffs_reset_work);
schedule_work(&ffs->reset_work);
return -ENODEV;
}
again, with no exclusion. Lose CPU just after seeing FFS_DEACTIVATED,
then have another thread open() the sucker and start going through
ffs_data_reset(), only to have us regain CPU and schedule this for
execution:
static void ffs_reset_work(struct work_struct *work)
{
struct ffs_data *ffs = container_of(work,
struct ffs_data, reset_work);
ffs_data_reset(ffs);
}
IOW, stray ffs_data_reset() coming to surprise the opener who'd
just finished ffs_data_reset() during open(2) and proceeded to
write to the damn thing, etc.
That's obviously on the "how do we fix the preexisting bugs" side
of things, though - regression needs to be dealt with ASAP anyway.
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-30 23:57 ` Al Viro
@ 2026-01-31 0:14 ` Linus Torvalds
2026-01-31 1:08 ` Al Viro
2026-01-31 0:59 ` Al Viro
2026-01-31 1:05 ` Samuel Wu
2 siblings, 1 reply; 26+ messages in thread
From: Linus Torvalds @ 2026-01-31 0:14 UTC (permalink / raw)
To: Al Viro
Cc: Samuel Wu, Greg KH, linux-fsdevel, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Fri, 30 Jan 2026 at 15:55, Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> So we have something that does O_NDELAY opens of ep0 *and* does not retry on
> EAGAIN?
>
> How lovely...
Actually, I think that is pretty normal behavior.
Generally, O_NDELAY and friends should *NOT* turn locks into trylocks
- because user space has no sane way to deal with kernel lock issues,
and user space simply shouldn't care.
So O_NDELAY should be about avoiding IO, not about avoiding perfectly
normal locks.
Of course, that horrendous driver locking is broken, since it holds
the lock over IO, so that driver basically conflates IO and locking,
and that's arguably the fundamental problem here.
But I suspect that for this case, we should just pass in zero to
ffs_mutex_lock() on open, and say that the O_NONBLOCK flag is purely
about the subsequent IO, not about the open() itself.
That is, after all, how the driver used to work.
Linus
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-30 23:57 ` Al Viro
2026-01-31 0:14 ` Linus Torvalds
@ 2026-01-31 0:59 ` Al Viro
2026-01-31 1:05 ` Samuel Wu
2 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2026-01-31 0:59 UTC (permalink / raw)
To: Samuel Wu
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Fri, Jan 30, 2026 at 11:57:43PM +0000, Al Viro wrote:
> Another thing to try (not as a suggestion of a fix, just an attempt to figure
> out how badly would the things break): in current mainline replace that
> ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK)
> in ffs_ep0_open() with
> ffs_mutex_lock(&ffs->mutex, false)
> and see how badly do the things regress for userland. Again, I'm not saying
> that this is a fix - just trying to get some sense of what's the userland
> is doing.
At a guess, quite badly. ffs->mutex *is* way too heavy for that purpose -
that's a geniune bug.
State transitions in that thing are messy; AFAICS, the state is a combination
of ->opened and ->state, and transitions assume atomicity that just isn't there.
All updates of ->opened are process-synchronous; the nasty part is in the
FFS_DEACTIVATED handling. We don't want it to coexist with ->opened > 0;
normally decrement of ->opened to 0 gets us into FFS_CLOSING immediately
and follows that with ffs_data_reset(). In -o no_disconnect mounts we switch
to FFS_DEACTIVATED instead. On the next open() after that we want it to
transition to the same FFS_CLOSING + the same call of ffs_data_reset().
open() running into FFS_CLOSING fails; that happens until ffs_data_reset()
switches ->state to FFS_READ_DESCRIPTORS.
Things are complicated by ffs_func_set_alt() and ffs_func_disable() - these
can come with ->opened being zero and both contain this:
if (ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
INIT_WORK(&ffs->reset_work, ffs_reset_work);
schedule_work(&ffs->reset_work);
return -ENODEV;
}
with s/return -ENODEV;/return;/ for ffs_func_disable(). The point, AFAICT,
is to avoid deadlocks from having ffs_data_reset() called in the locking
environment these two are called in. At least ->set_alt() can be called
under a spinlock and ffs_data_reset() is blocking.
Another potentially troubling part is the check for FFS_ACTIVE in the
same functions, seeing that
ffs->state = FFS_ACTIVE;
mutex_unlock(&ffs->mutex);
ret = ffs_ready(ffs);
if (ret < 0) {
ffs->state = FFS_CLOSING;
return ret;
}
in ep0 write() happens with no exclusion with those (as the matter of
fact, that transition to FFS_CLOSING holds no locks at all)...
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-30 23:57 ` Al Viro
2026-01-31 0:14 ` Linus Torvalds
2026-01-31 0:59 ` Al Viro
@ 2026-01-31 1:05 ` Samuel Wu
2026-01-31 1:18 ` Al Viro
2 siblings, 1 reply; 26+ messages in thread
From: Samuel Wu @ 2026-01-31 1:05 UTC (permalink / raw)
To: Al Viro
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Fri, Jan 30, 2026 at 3:55 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> So we have something that does O_NDELAY opens of ep0 *and* does not retry on
> EAGAIN?
>
> How lovely... Could you slap
> WARN_ON(ret == -EAGAIN);
> right before that
> if (ret < 0)
> return ret;
Surprisingly ret == 0 every time, so no difference in dmesg logs with
this addition.
> in there and see which process is doing that? Regression is a regression,
> odd userland or not, but I would like to see what is that userland actually
> trying to do there.
>
> *grumble*
>
> IMO at that point we have two problems - one is how to avoid a revert of the
> tail of tree-in-dcache series, another is how to deal with quite real
> preexisting bugs in functionfs.
>
> Another thing to try (not as a suggestion of a fix, just an attempt to figure
> out how badly would the things break): in current mainline replace that
> ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK)
> in ffs_ep0_open() with
> ffs_mutex_lock(&ffs->mutex, false)
> and see how badly do the things regress for userland. Again, I'm not saying
> that this is a fix - just trying to get some sense of what's the userland
> is doing.
Ergo this didn't make a difference either.
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-31 0:14 ` Linus Torvalds
@ 2026-01-31 1:08 ` Al Viro
2026-01-31 1:11 ` Linus Torvalds
0 siblings, 1 reply; 26+ messages in thread
From: Al Viro @ 2026-01-31 1:08 UTC (permalink / raw)
To: Linus Torvalds
Cc: Samuel Wu, Greg KH, linux-fsdevel, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Fri, Jan 30, 2026 at 04:14:48PM -0800, Linus Torvalds wrote:
> On Fri, 30 Jan 2026 at 15:55, Al Viro <viro@zeniv.linux.org.uk> wrote:
> >
> > So we have something that does O_NDELAY opens of ep0 *and* does not retry on
> > EAGAIN?
> >
> > How lovely...
>
> Actually, I think that is pretty normal behavior.
>
> Generally, O_NDELAY and friends should *NOT* turn locks into trylocks
> - because user space has no sane way to deal with kernel lock issues,
> and user space simply shouldn't care.
>
> So O_NDELAY should be about avoiding IO, not about avoiding perfectly
> normal locks.
>
> Of course, that horrendous driver locking is broken, since it holds
> the lock over IO, so that driver basically conflates IO and locking,
> and that's arguably the fundamental problem here.
>
> But I suspect that for this case, we should just pass in zero to
> ffs_mutex_lock() on open, and say that the O_NONBLOCK flag is purely
> about the subsequent IO, not about the open() itself.
>
> That is, after all, how the driver used to work.
I'd rather go for a spinlock there, protecting these FFS_DEACTIVATED
transitions; let me try and put together something along those lines.
Matter of fact, I would drop the atomics for ->opened completely
and do all changes under the same spinlock - it's really just
->open() and ->release(). Simpler that way...
The shitty part is that ->set_alt() thing and its callers seems to
be written in assumption that it can come from an interrupt, so we'd
need spin_lock_irq() in open/release and spin_lock_irqsave() in
set_alt/disable...
Another fun part is that we need a barrier on transition from
FFS_CLOSING in ffs_data_reset() - right now it's not even the last
assignment in there. Same spinlock would solve that - screw explicit
barriers, it's _not_ a hot path and the locking is convoluted enough
as it is.
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-31 1:08 ` Al Viro
@ 2026-01-31 1:11 ` Linus Torvalds
2026-02-01 0:11 ` Al Viro
0 siblings, 1 reply; 26+ messages in thread
From: Linus Torvalds @ 2026-01-31 1:11 UTC (permalink / raw)
To: Al Viro
Cc: Samuel Wu, Greg KH, linux-fsdevel, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Fri, 30 Jan 2026 at 17:06, Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> I'd rather go for a spinlock there, protecting these FFS_DEACTIVATED
> transitions;
Yes, that's the much better solution. The locking in this thing is horrendous.
But judging by Samuel's recent email, there's something else than the
open locking thing going on.
Linus
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-31 1:05 ` Samuel Wu
@ 2026-01-31 1:18 ` Al Viro
2026-01-31 2:09 ` Samuel Wu
0 siblings, 1 reply; 26+ messages in thread
From: Al Viro @ 2026-01-31 1:18 UTC (permalink / raw)
To: Samuel Wu
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Fri, Jan 30, 2026 at 05:05:34PM -0800, Samuel Wu wrote:
> > How lovely... Could you slap
> > WARN_ON(ret == -EAGAIN);
> > right before that
> > if (ret < 0)
> > return ret;
>
> Surprisingly ret == 0 every time, so no difference in dmesg logs with
> this addition.
What the hell? Other than that mutex_lock(), the only change in there
is the order of store to file->private_data and call of ffs_data_opened();
that struct file pointer is not visible to anyone at that point...
Wait, it also brings ffs_data_reset() on that transition under ffs->mutex...
For a quick check: does
git fetch git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git for-wsamuel2
git switch --detach FETCH_HEAD
demonstrate the same breakage?
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-31 1:18 ` Al Viro
@ 2026-01-31 2:09 ` Samuel Wu
2026-01-31 2:43 ` Al Viro
0 siblings, 1 reply; 26+ messages in thread
From: Samuel Wu @ 2026-01-31 2:09 UTC (permalink / raw)
To: Al Viro
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Fri, Jan 30, 2026 at 5:16 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Fri, Jan 30, 2026 at 05:05:34PM -0800, Samuel Wu wrote:
>
> > > How lovely... Could you slap
> > > WARN_ON(ret == -EAGAIN);
> > > right before that
> > > if (ret < 0)
> > > return ret;
> >
> > Surprisingly ret == 0 every time, so no difference in dmesg logs with
> > this addition.
>
> What the hell? Other than that mutex_lock(), the only change in there
> is the order of store to file->private_data and call of ffs_data_opened();
> that struct file pointer is not visible to anyone at that point...
Agree, 09e88dc22ea2 (serialize ffs_ep0_open() on ffs->mutex) in itself
is quite straightforward. Not familiar with this code path so just
speculating, but is there any interaction with previous patches (e.g.
refcounting)?
> Wait, it also brings ffs_data_reset() on that transition under ffs->mutex...
> For a quick check: does
> git fetch git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git for-wsamuel2
> git switch --detach FETCH_HEAD
> demonstrate the same breakage?
Had to adjust forward declaration of ffs_data_reset() to build, but
unfortunately same breakage.
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-31 2:09 ` Samuel Wu
@ 2026-01-31 2:43 ` Al Viro
2026-01-31 19:48 ` Samuel Wu
0 siblings, 1 reply; 26+ messages in thread
From: Al Viro @ 2026-01-31 2:43 UTC (permalink / raw)
To: Samuel Wu
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Fri, Jan 30, 2026 at 06:09:00PM -0800, Samuel Wu wrote:
> On Fri, Jan 30, 2026 at 5:16 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> >
> > On Fri, Jan 30, 2026 at 05:05:34PM -0800, Samuel Wu wrote:
> >
> > > > How lovely... Could you slap
> > > > WARN_ON(ret == -EAGAIN);
> > > > right before that
> > > > if (ret < 0)
> > > > return ret;
> > >
> > > Surprisingly ret == 0 every time, so no difference in dmesg logs with
> > > this addition.
> >
> > What the hell? Other than that mutex_lock(), the only change in there
> > is the order of store to file->private_data and call of ffs_data_opened();
> > that struct file pointer is not visible to anyone at that point...
>
> Agree, 09e88dc22ea2 (serialize ffs_ep0_open() on ffs->mutex) in itself
> is quite straightforward. Not familiar with this code path so just
> speculating, but is there any interaction with previous patches (e.g.
> refcounting)?
>
> > Wait, it also brings ffs_data_reset() on that transition under ffs->mutex...
> > For a quick check: does
> > git fetch git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git for-wsamuel2
> > git switch --detach FETCH_HEAD
> > demonstrate the same breakage?
>
> Had to adjust forward declaration of ffs_data_reset() to build, but
> unfortunately same breakage.
That really looks like a badly racy userland on top of everything else...
I mean, it smells like userland open() from one process while another
is in the middle of configuring that stuff getting delayed too much
for the entire thing to work. Bloody wonderful...
OK, let's see if a variant with serialization on spinlock works - how does
the following do on top of mainline?
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 05c6750702b6..fa467a40949d 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -59,7 +59,6 @@ static struct ffs_data *__must_check ffs_data_new(const char *dev_name)
__attribute__((malloc));
/* Opened counter handling. */
-static void ffs_data_opened(struct ffs_data *ffs);
static void ffs_data_closed(struct ffs_data *ffs);
/* Called with ffs->mutex held; take over ownership of data. */
@@ -636,23 +635,25 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
return ret;
}
+
+static void ffs_data_reset(struct ffs_data *ffs);
+
static int ffs_ep0_open(struct inode *inode, struct file *file)
{
struct ffs_data *ffs = inode->i_sb->s_fs_info;
- int ret;
- /* Acquire mutex */
- ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
- if (ret < 0)
- return ret;
-
- ffs_data_opened(ffs);
+ spin_lock_irq(&ffs->eps_lock);
if (ffs->state == FFS_CLOSING) {
- ffs_data_closed(ffs);
- mutex_unlock(&ffs->mutex);
+ spin_unlock_irq(&ffs->eps_lock);
return -EBUSY;
}
- mutex_unlock(&ffs->mutex);
+ if (!ffs->opened++ && ffs->state == FFS_DEACTIVATED) {
+ ffs->state = FFS_CLOSING;
+ spin_unlock_irq(&ffs->eps_lock);
+ ffs_data_reset(ffs);
+ } else {
+ spin_unlock_irq(&ffs->eps_lock);
+ }
file->private_data = ffs;
return stream_open(inode, file);
@@ -1202,15 +1203,10 @@ ffs_epfile_open(struct inode *inode, struct file *file)
{
struct ffs_data *ffs = inode->i_sb->s_fs_info;
struct ffs_epfile *epfile;
- int ret;
-
- /* Acquire mutex */
- ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
- if (ret < 0)
- return ret;
- if (!atomic_inc_not_zero(&ffs->opened)) {
- mutex_unlock(&ffs->mutex);
+ spin_lock_irq(&ffs->eps_lock);
+ if (!ffs->opened) {
+ spin_unlock_irq(&ffs->eps_lock);
return -ENODEV;
}
/*
@@ -1220,11 +1216,11 @@ ffs_epfile_open(struct inode *inode, struct file *file)
*/
epfile = smp_load_acquire(&inode->i_private);
if (unlikely(ffs->state != FFS_ACTIVE || !epfile)) {
- mutex_unlock(&ffs->mutex);
- ffs_data_closed(ffs);
+ spin_unlock_irq(&ffs->eps_lock);
return -ENODEV;
}
- mutex_unlock(&ffs->mutex);
+ ffs->opened++;
+ spin_unlock_irq(&ffs->eps_lock);
file->private_data = epfile;
return stream_open(inode, file);
@@ -2092,8 +2088,6 @@ static int ffs_fs_init_fs_context(struct fs_context *fc)
return 0;
}
-static void ffs_data_reset(struct ffs_data *ffs);
-
static void
ffs_fs_kill_sb(struct super_block *sb)
{
@@ -2150,15 +2144,6 @@ static void ffs_data_get(struct ffs_data *ffs)
refcount_inc(&ffs->ref);
}
-static void ffs_data_opened(struct ffs_data *ffs)
-{
- if (atomic_add_return(1, &ffs->opened) == 1 &&
- ffs->state == FFS_DEACTIVATED) {
- ffs->state = FFS_CLOSING;
- ffs_data_reset(ffs);
- }
-}
-
static void ffs_data_put(struct ffs_data *ffs)
{
if (refcount_dec_and_test(&ffs->ref)) {
@@ -2176,28 +2161,29 @@ static void ffs_data_put(struct ffs_data *ffs)
static void ffs_data_closed(struct ffs_data *ffs)
{
- if (atomic_dec_and_test(&ffs->opened)) {
- if (ffs->no_disconnect) {
- struct ffs_epfile *epfiles;
- unsigned long flags;
-
- ffs->state = FFS_DEACTIVATED;
- spin_lock_irqsave(&ffs->eps_lock, flags);
- epfiles = ffs->epfiles;
- ffs->epfiles = NULL;
- spin_unlock_irqrestore(&ffs->eps_lock,
- flags);
-
- if (epfiles)
- ffs_epfiles_destroy(ffs->sb, epfiles,
- ffs->eps_count);
-
- if (ffs->setup_state == FFS_SETUP_PENDING)
- __ffs_ep0_stall(ffs);
- } else {
- ffs->state = FFS_CLOSING;
- ffs_data_reset(ffs);
- }
+ spin_lock_irq(&ffs->eps_lock);
+ if (--ffs->opened) { // not the last opener?
+ spin_unlock_irq(&ffs->eps_lock);
+ return;
+ }
+ if (ffs->no_disconnect) {
+ struct ffs_epfile *epfiles;
+
+ ffs->state = FFS_DEACTIVATED;
+ epfiles = ffs->epfiles;
+ ffs->epfiles = NULL;
+ spin_unlock_irq(&ffs->eps_lock);
+
+ if (epfiles)
+ ffs_epfiles_destroy(ffs->sb, epfiles,
+ ffs->eps_count);
+
+ if (ffs->setup_state == FFS_SETUP_PENDING)
+ __ffs_ep0_stall(ffs);
+ } else {
+ ffs->state = FFS_CLOSING;
+ spin_unlock_irq(&ffs->eps_lock);
+ ffs_data_reset(ffs);
}
}
@@ -2214,7 +2200,7 @@ static struct ffs_data *ffs_data_new(const char *dev_name)
}
refcount_set(&ffs->ref, 1);
- atomic_set(&ffs->opened, 0);
+ ffs->opened = 0;
ffs->state = FFS_READ_DESCRIPTORS;
mutex_init(&ffs->mutex);
spin_lock_init(&ffs->eps_lock);
@@ -2266,6 +2252,7 @@ static void ffs_data_reset(struct ffs_data *ffs)
{
ffs_data_clear(ffs);
+ spin_lock_irq(&ffs->eps_lock);
ffs->raw_descs_data = NULL;
ffs->raw_descs = NULL;
ffs->raw_strings = NULL;
@@ -2289,6 +2276,7 @@ static void ffs_data_reset(struct ffs_data *ffs)
ffs->ms_os_descs_ext_prop_count = 0;
ffs->ms_os_descs_ext_prop_name_len = 0;
ffs->ms_os_descs_ext_prop_data_len = 0;
+ spin_unlock_irq(&ffs->eps_lock);
}
@@ -3756,6 +3744,7 @@ static int ffs_func_set_alt(struct usb_function *f,
{
struct ffs_function *func = ffs_func_from_usb(f);
struct ffs_data *ffs = func->ffs;
+ unsigned long flags;
int ret = 0, intf;
if (alt > MAX_ALT_SETTINGS)
@@ -3768,12 +3757,15 @@ static int ffs_func_set_alt(struct usb_function *f,
if (ffs->func)
ffs_func_eps_disable(ffs->func);
+ spin_lock_irqsave(&ffs->eps_lock, flags);
if (ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
INIT_WORK(&ffs->reset_work, ffs_reset_work);
schedule_work(&ffs->reset_work);
return -ENODEV;
}
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
if (ffs->state != FFS_ACTIVE)
return -ENODEV;
@@ -3791,16 +3783,20 @@ static void ffs_func_disable(struct usb_function *f)
{
struct ffs_function *func = ffs_func_from_usb(f);
struct ffs_data *ffs = func->ffs;
+ unsigned long flags;
if (ffs->func)
ffs_func_eps_disable(ffs->func);
+ spin_lock_irqsave(&ffs->eps_lock, flags);
if (ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
INIT_WORK(&ffs->reset_work, ffs_reset_work);
schedule_work(&ffs->reset_work);
return;
}
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
if (ffs->state == FFS_ACTIVE) {
ffs->func = NULL;
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index 4b3365f23fd7..6a80182aadd7 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -176,7 +176,7 @@ struct ffs_data {
/* reference counter */
refcount_t ref;
/* how many files are opened (EP0 and others) */
- atomic_t opened;
+ int opened;
/* EP0 state */
enum ffs_state state;
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-30 1:16 ` Samuel Wu
2026-01-30 7:04 ` Al Viro
@ 2026-01-31 14:58 ` Krishna Kurapati PSSNV
2026-01-31 20:02 ` Samuel Wu
1 sibling, 1 reply; 26+ messages in thread
From: Krishna Kurapati PSSNV @ 2026-01-31 14:58 UTC (permalink / raw)
To: Samuel Wu
Cc: Al Viro, Greg KH, linux-fsdevel, torvalds, brauner, jack, raven,
miklos, neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees,
rostedt, linux-usb, paul, casey, linuxppc-dev, john.johansen,
selinux, borntraeger, bpf, clm, android-kernel-team
On Fri, Jan 30, 2026 at 6:46 AM Samuel Wu <wusamuel@google.com> wrote:
>
> On Thu, Jan 29, 2026 at 2:52 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
[...]
> I'm exploring a few other paths, but not having USB access makes
> traditional tools a bit difficult. One thing I'm rechecking and is
> worth mentioning is the lockdep below: it's been present for quite
> some time now, but I'm not sure if it would have some undesired
> interaction with your patch.
>
> [ BUG: Invalid wait context ]
> 6.18.0-rc5-mainline-maybe-dirty-4k
> -----------------------------
> irq/360-dwc3/352 is trying to lock:
> ffffff800792deb8 (&psy->extensions_sem){.+.+}-{3:3}, at:
> __power_supply_set_property+0x40/0x180
> other info that might help us debug this:
> context-{4:4}
> 1 lock held by irq/360-dwc3/352:
> #0: ffffff8017bb98f0 (&gi->spinlock){....}-{2:2}, at:
> configfs_composite_suspend+0x28/0x68
> Call trace:
> show_stack+0x18/0x28 (C)
> __dump_stack+0x28/0x3c
> dump_stack_lvl+0xac/0xf0
> dump_stack+0x18/0x3c
> __lock_acquire+0x794/0x2bec
> lock_acquire+0x148/0x2cc
> down_read+0x3c/0x194
> __power_supply_set_property+0x40/0x180
> power_supply_set_property+0x14/0x20
> dwc3_gadget_vbus_draw+0x8c/0xcc
> usb_gadget_vbus_draw+0x48/0x130
> composite_suspend+0xcc/0xe4
> configfs_composite_suspend+0x44/0x68
> dwc3_thread_interrupt+0x8f8/0xc88
> irq_thread_fn+0x48/0xa8
> irq_thread+0x150/0x31c
> kthread+0x150/0x280
> ret_from_fork+0x10/0x20
>
Hi Samuel,
Not sure if it helps, but Prashanth recently pushed a patch to
address this vbus_draw kernel panic:
https://lore.kernel.org/all/20260129111403.3081730-1-prashanth.k@oss.qualcomm.com/
Can you check if it fixes the above crash in vbus_draw.
Regards,
Krishna,
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-31 2:43 ` Al Viro
@ 2026-01-31 19:48 ` Samuel Wu
0 siblings, 0 replies; 26+ messages in thread
From: Samuel Wu @ 2026-01-31 19:48 UTC (permalink / raw)
To: Al Viro
Cc: Greg KH, linux-fsdevel, torvalds, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Fri, Jan 30, 2026 at 6:41 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Fri, Jan 30, 2026 at 06:09:00PM -0800, Samuel Wu wrote:
> > On Fri, Jan 30, 2026 at 5:16 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> > >
> > > On Fri, Jan 30, 2026 at 05:05:34PM -0800, Samuel Wu wrote:
> > >
> > > > > How lovely... Could you slap
> > > > > WARN_ON(ret == -EAGAIN);
> > > > > right before that
> > > > > if (ret < 0)
> > > > > return ret;
> > > >
> > > > Surprisingly ret == 0 every time, so no difference in dmesg logs with
> > > > this addition.
> > >
> > > What the hell? Other than that mutex_lock(), the only change in there
> > > is the order of store to file->private_data and call of ffs_data_opened();
> > > that struct file pointer is not visible to anyone at that point...
> >
> > Agree, 09e88dc22ea2 (serialize ffs_ep0_open() on ffs->mutex) in itself
> > is quite straightforward. Not familiar with this code path so just
> > speculating, but is there any interaction with previous patches (e.g.
> > refcounting)?
> >
> > > Wait, it also brings ffs_data_reset() on that transition under ffs->mutex...
> > > For a quick check: does
> > > git fetch git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git for-wsamuel2
> > > git switch --detach FETCH_HEAD
> > > demonstrate the same breakage?
> >
> > Had to adjust forward declaration of ffs_data_reset() to build, but
> > unfortunately same breakage.
>
> That really looks like a badly racy userland on top of everything else...
> I mean, it smells like userland open() from one process while another
> is in the middle of configuring that stuff getting delayed too much
> for the entire thing to work. Bloody wonderful...
>
> OK, let's see if a variant with serialization on spinlock works - how does
> the following do on top of mainline?
Excellent, this is working consistently for me on the latest 6.19-rc7.
>
> diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
> index 05c6750702b6..fa467a40949d 100644
> --- a/drivers/usb/gadget/function/f_fs.c
> +++ b/drivers/usb/gadget/function/f_fs.c
> @@ -59,7 +59,6 @@ static struct ffs_data *__must_check ffs_data_new(const char *dev_name)
> __attribute__((malloc));
>
> /* Opened counter handling. */
> -static void ffs_data_opened(struct ffs_data *ffs);
> static void ffs_data_closed(struct ffs_data *ffs);
>
> /* Called with ffs->mutex held; take over ownership of data. */
> @@ -636,23 +635,25 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
> return ret;
> }
>
> +
> +static void ffs_data_reset(struct ffs_data *ffs);
> +
> static int ffs_ep0_open(struct inode *inode, struct file *file)
> {
> struct ffs_data *ffs = inode->i_sb->s_fs_info;
> - int ret;
>
> - /* Acquire mutex */
> - ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
> - if (ret < 0)
> - return ret;
> -
> - ffs_data_opened(ffs);
> + spin_lock_irq(&ffs->eps_lock);
> if (ffs->state == FFS_CLOSING) {
> - ffs_data_closed(ffs);
> - mutex_unlock(&ffs->mutex);
> + spin_unlock_irq(&ffs->eps_lock);
> return -EBUSY;
> }
> - mutex_unlock(&ffs->mutex);
> + if (!ffs->opened++ && ffs->state == FFS_DEACTIVATED) {
> + ffs->state = FFS_CLOSING;
> + spin_unlock_irq(&ffs->eps_lock);
> + ffs_data_reset(ffs);
> + } else {
> + spin_unlock_irq(&ffs->eps_lock);
> + }
> file->private_data = ffs;
>
> return stream_open(inode, file);
> @@ -1202,15 +1203,10 @@ ffs_epfile_open(struct inode *inode, struct file *file)
> {
> struct ffs_data *ffs = inode->i_sb->s_fs_info;
> struct ffs_epfile *epfile;
> - int ret;
> -
> - /* Acquire mutex */
> - ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
> - if (ret < 0)
> - return ret;
>
> - if (!atomic_inc_not_zero(&ffs->opened)) {
> - mutex_unlock(&ffs->mutex);
> + spin_lock_irq(&ffs->eps_lock);
> + if (!ffs->opened) {
> + spin_unlock_irq(&ffs->eps_lock);
> return -ENODEV;
> }
> /*
> @@ -1220,11 +1216,11 @@ ffs_epfile_open(struct inode *inode, struct file *file)
> */
> epfile = smp_load_acquire(&inode->i_private);
> if (unlikely(ffs->state != FFS_ACTIVE || !epfile)) {
> - mutex_unlock(&ffs->mutex);
> - ffs_data_closed(ffs);
> + spin_unlock_irq(&ffs->eps_lock);
> return -ENODEV;
> }
> - mutex_unlock(&ffs->mutex);
> + ffs->opened++;
> + spin_unlock_irq(&ffs->eps_lock);
>
> file->private_data = epfile;
> return stream_open(inode, file);
> @@ -2092,8 +2088,6 @@ static int ffs_fs_init_fs_context(struct fs_context *fc)
> return 0;
> }
>
> -static void ffs_data_reset(struct ffs_data *ffs);
> -
> static void
> ffs_fs_kill_sb(struct super_block *sb)
> {
> @@ -2150,15 +2144,6 @@ static void ffs_data_get(struct ffs_data *ffs)
> refcount_inc(&ffs->ref);
> }
>
> -static void ffs_data_opened(struct ffs_data *ffs)
> -{
> - if (atomic_add_return(1, &ffs->opened) == 1 &&
> - ffs->state == FFS_DEACTIVATED) {
> - ffs->state = FFS_CLOSING;
> - ffs_data_reset(ffs);
> - }
> -}
> -
> static void ffs_data_put(struct ffs_data *ffs)
> {
> if (refcount_dec_and_test(&ffs->ref)) {
> @@ -2176,28 +2161,29 @@ static void ffs_data_put(struct ffs_data *ffs)
>
> static void ffs_data_closed(struct ffs_data *ffs)
> {
> - if (atomic_dec_and_test(&ffs->opened)) {
> - if (ffs->no_disconnect) {
> - struct ffs_epfile *epfiles;
> - unsigned long flags;
> -
> - ffs->state = FFS_DEACTIVATED;
> - spin_lock_irqsave(&ffs->eps_lock, flags);
> - epfiles = ffs->epfiles;
> - ffs->epfiles = NULL;
> - spin_unlock_irqrestore(&ffs->eps_lock,
> - flags);
> -
> - if (epfiles)
> - ffs_epfiles_destroy(ffs->sb, epfiles,
> - ffs->eps_count);
> -
> - if (ffs->setup_state == FFS_SETUP_PENDING)
> - __ffs_ep0_stall(ffs);
> - } else {
> - ffs->state = FFS_CLOSING;
> - ffs_data_reset(ffs);
> - }
> + spin_lock_irq(&ffs->eps_lock);
> + if (--ffs->opened) { // not the last opener?
> + spin_unlock_irq(&ffs->eps_lock);
> + return;
> + }
> + if (ffs->no_disconnect) {
> + struct ffs_epfile *epfiles;
> +
> + ffs->state = FFS_DEACTIVATED;
> + epfiles = ffs->epfiles;
> + ffs->epfiles = NULL;
> + spin_unlock_irq(&ffs->eps_lock);
> +
> + if (epfiles)
> + ffs_epfiles_destroy(ffs->sb, epfiles,
> + ffs->eps_count);
> +
> + if (ffs->setup_state == FFS_SETUP_PENDING)
> + __ffs_ep0_stall(ffs);
> + } else {
> + ffs->state = FFS_CLOSING;
> + spin_unlock_irq(&ffs->eps_lock);
> + ffs_data_reset(ffs);
> }
> }
>
> @@ -2214,7 +2200,7 @@ static struct ffs_data *ffs_data_new(const char *dev_name)
> }
>
> refcount_set(&ffs->ref, 1);
> - atomic_set(&ffs->opened, 0);
> + ffs->opened = 0;
> ffs->state = FFS_READ_DESCRIPTORS;
> mutex_init(&ffs->mutex);
> spin_lock_init(&ffs->eps_lock);
> @@ -2266,6 +2252,7 @@ static void ffs_data_reset(struct ffs_data *ffs)
> {
> ffs_data_clear(ffs);
>
> + spin_lock_irq(&ffs->eps_lock);
> ffs->raw_descs_data = NULL;
> ffs->raw_descs = NULL;
> ffs->raw_strings = NULL;
> @@ -2289,6 +2276,7 @@ static void ffs_data_reset(struct ffs_data *ffs)
> ffs->ms_os_descs_ext_prop_count = 0;
> ffs->ms_os_descs_ext_prop_name_len = 0;
> ffs->ms_os_descs_ext_prop_data_len = 0;
> + spin_unlock_irq(&ffs->eps_lock);
> }
>
>
> @@ -3756,6 +3744,7 @@ static int ffs_func_set_alt(struct usb_function *f,
> {
> struct ffs_function *func = ffs_func_from_usb(f);
> struct ffs_data *ffs = func->ffs;
> + unsigned long flags;
> int ret = 0, intf;
>
> if (alt > MAX_ALT_SETTINGS)
> @@ -3768,12 +3757,15 @@ static int ffs_func_set_alt(struct usb_function *f,
> if (ffs->func)
> ffs_func_eps_disable(ffs->func);
>
> + spin_lock_irqsave(&ffs->eps_lock, flags);
> if (ffs->state == FFS_DEACTIVATED) {
> ffs->state = FFS_CLOSING;
> + spin_unlock_irqrestore(&ffs->eps_lock, flags);
> INIT_WORK(&ffs->reset_work, ffs_reset_work);
> schedule_work(&ffs->reset_work);
> return -ENODEV;
> }
> + spin_unlock_irqrestore(&ffs->eps_lock, flags);
>
> if (ffs->state != FFS_ACTIVE)
> return -ENODEV;
> @@ -3791,16 +3783,20 @@ static void ffs_func_disable(struct usb_function *f)
> {
> struct ffs_function *func = ffs_func_from_usb(f);
> struct ffs_data *ffs = func->ffs;
> + unsigned long flags;
>
> if (ffs->func)
> ffs_func_eps_disable(ffs->func);
>
> + spin_lock_irqsave(&ffs->eps_lock, flags);
> if (ffs->state == FFS_DEACTIVATED) {
> ffs->state = FFS_CLOSING;
> + spin_unlock_irqrestore(&ffs->eps_lock, flags);
> INIT_WORK(&ffs->reset_work, ffs_reset_work);
> schedule_work(&ffs->reset_work);
> return;
> }
> + spin_unlock_irqrestore(&ffs->eps_lock, flags);
>
> if (ffs->state == FFS_ACTIVE) {
> ffs->func = NULL;
> diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
> index 4b3365f23fd7..6a80182aadd7 100644
> --- a/drivers/usb/gadget/function/u_fs.h
> +++ b/drivers/usb/gadget/function/u_fs.h
> @@ -176,7 +176,7 @@ struct ffs_data {
> /* reference counter */
> refcount_t ref;
> /* how many files are opened (EP0 and others) */
> - atomic_t opened;
> + int opened;
>
> /* EP0 state */
> enum ffs_state state;
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-31 14:58 ` Krishna Kurapati PSSNV
@ 2026-01-31 20:02 ` Samuel Wu
0 siblings, 0 replies; 26+ messages in thread
From: Samuel Wu @ 2026-01-31 20:02 UTC (permalink / raw)
To: Krishna Kurapati PSSNV
Cc: Al Viro, Greg KH, linux-fsdevel, torvalds, brauner, jack, raven,
miklos, neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees,
rostedt, linux-usb, paul, casey, linuxppc-dev, john.johansen,
selinux, borntraeger, bpf, clm, android-kernel-team
On Sat, Jan 31, 2026 at 6:58 AM Krishna Kurapati PSSNV
<krishna.kurapati@oss.qualcomm.com> wrote:
>
> On Fri, Jan 30, 2026 at 6:46 AM Samuel Wu <wusamuel@google.com> wrote:
> >
> > On Thu, Jan 29, 2026 at 2:52 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> >
>
> [...]
>
> > I'm exploring a few other paths, but not having USB access makes
> > traditional tools a bit difficult. One thing I'm rechecking and is
> > worth mentioning is the lockdep below: it's been present for quite
> > some time now, but I'm not sure if it would have some undesired
> > interaction with your patch.
> >
> > [ BUG: Invalid wait context ]
> > 6.18.0-rc5-mainline-maybe-dirty-4k
> > -----------------------------
> > irq/360-dwc3/352 is trying to lock:
> > ffffff800792deb8 (&psy->extensions_sem){.+.+}-{3:3}, at:
> > __power_supply_set_property+0x40/0x180
> > other info that might help us debug this:
> > context-{4:4}
> > 1 lock held by irq/360-dwc3/352:
> > #0: ffffff8017bb98f0 (&gi->spinlock){....}-{2:2}, at:
> > configfs_composite_suspend+0x28/0x68
> > Call trace:
> > show_stack+0x18/0x28 (C)
> > __dump_stack+0x28/0x3c
> > dump_stack_lvl+0xac/0xf0
> > dump_stack+0x18/0x3c
> > __lock_acquire+0x794/0x2bec
> > lock_acquire+0x148/0x2cc
> > down_read+0x3c/0x194
> > __power_supply_set_property+0x40/0x180
> > power_supply_set_property+0x14/0x20
> > dwc3_gadget_vbus_draw+0x8c/0xcc
> > usb_gadget_vbus_draw+0x48/0x130
> > composite_suspend+0xcc/0xe4
> > configfs_composite_suspend+0x44/0x68
> > dwc3_thread_interrupt+0x8f8/0xc88
> > irq_thread_fn+0x48/0xa8
> > irq_thread+0x150/0x31c
> > kthread+0x150/0x280
> > ret_from_fork+0x10/0x20
> >
>
> Hi Samuel,
>
> Not sure if it helps, but Prashanth recently pushed a patch to
> address this vbus_draw kernel panic:
> https://lore.kernel.org/all/20260129111403.3081730-1-prashanth.k@oss.qualcomm.com/
>
> Can you check if it fixes the above crash in vbus_draw.
>
> Regards,
> Krishna,
Tested above patch, and it didn't fix the device enumerating on
lsusb/ADB issue. Seems like usb dwc3 lockdep was a red herring.
I'll respond on that thread with what I'm observing.
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v4 00/54] tree-in-dcache stuff
2026-01-31 1:11 ` Linus Torvalds
@ 2026-02-01 0:11 ` Al Viro
0 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2026-02-01 0:11 UTC (permalink / raw)
To: Linus Torvalds
Cc: Samuel Wu, Greg KH, linux-fsdevel, brauner, jack, raven, miklos,
neil, a.hindborg, linux-mm, linux-efi, ocfs2-devel, kees, rostedt,
linux-usb, paul, casey, linuxppc-dev, john.johansen, selinux,
borntraeger, bpf, clm, android-kernel-team
On Fri, Jan 30, 2026 at 05:11:39PM -0800, Linus Torvalds wrote:
> On Fri, 30 Jan 2026 at 17:06, Al Viro <viro@zeniv.linux.org.uk> wrote:
> >
> > I'd rather go for a spinlock there, protecting these FFS_DEACTIVATED
> > transitions;
>
> Yes, that's the much better solution. The locking in this thing is horrendous.
>
> But judging by Samuel's recent email, there's something else than the
> open locking thing going on.
I've put the following into viro/vfs.git#fixes; diff is identical to the
one that is reported to fix things on top of -rc7...
Objections?
commit 99a706fa47949ece1fb02b5b1206efd4fb031d25
Author: Al Viro <viro@zeniv.linux.org.uk>
Date: Sat Jan 31 18:24:41 2026 -0500
functionfs: use spinlock for FFS_DEACTIVATED/FFS_CLOSING transitions
When all files are closed, functionfs needs ffs_data_reset() to be
done before any further opens are allowed.
During that time we have ffs->state set to FFS_CLOSING; that makes
->open() fail with -EBUSY. Once ffs_data_reset() is done, it
switches state (to FFS_READ_DESCRIPTORS) indicating that opening
that thing is allowed again. There's a couple of additional twists:
* mounting with -o no_disconnect delays ffs_data_reset()
from doing that at the final ->release() to the first subsequent
open(). That's indicated by ffs->state set to FFS_DEACTIVATED;
if open() sees that, it immediately switches to FFS_CLOSING and
proceeds with doing ffs_data_reset() before returning to userland.
* a couple of usb callbacks need to force the delayed
transition; unfortunately, they are done in locking environment
that does not allow blocking and ffs_data_reset() can block.
As the result, if these callbacks see FFS_DEACTIVATED, they change
state to FFS_CLOSING and use schedule_work() to get ffs_data_reset()
executed asynchronously.
Unfortunately, the locking is rather insufficient. A fix attempted
in e5bf5ee26663 ("functionfs: fix the open/removal races") had closed
a bunch of UAF, but it didn't do anything to the callbacks, lacked
barriers in transition from FFS_CLOSING to FFS_READ_DESCRIPTORS
_and_ it had been too heavy-handed in open()/open() serialization -
I've used ffs->mutex for that, and it's being held over actual IO on
ep0, complete with copy_from_user(), etc.
Even more unfortunately, the userland side is apparently racy enough
to have the resulting timing changes (no failures, just a delayed
return of open(2)) disrupt the things quite badly. Userland bugs
or not, it's a clear regression that needs to be dealt with.
Solution is to use a spinlock for serializing these state checks and
transitions - unlike ffs->mutex it can be taken in these callbacks
and it doesn't disrupt the timings in open().
We could introduce a new spinlock, but it's easier to use the one
that is already there (ffs->eps_lock) instead - the locking
environment is safe for it in all affected places.
Since now it is held over all places that alter or check the
open count (ffs->opened), there's no need to keep that atomic_t -
int would serve just fine and it's simpler that way.
Fixes: e5bf5ee26663 ("functionfs: fix the open/removal races")
Fixes: 18d6b32fca38 ("usb: gadget: f_fs: add "no_disconnect" mode") # v4.0
Tested-by: Samuel Wu <wusamuel@google.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 05c6750702b6..fa467a40949d 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -59,7 +59,6 @@ static struct ffs_data *__must_check ffs_data_new(const char *dev_name)
__attribute__((malloc));
/* Opened counter handling. */
-static void ffs_data_opened(struct ffs_data *ffs);
static void ffs_data_closed(struct ffs_data *ffs);
/* Called with ffs->mutex held; take over ownership of data. */
@@ -636,23 +635,25 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
return ret;
}
+
+static void ffs_data_reset(struct ffs_data *ffs);
+
static int ffs_ep0_open(struct inode *inode, struct file *file)
{
struct ffs_data *ffs = inode->i_sb->s_fs_info;
- int ret;
- /* Acquire mutex */
- ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
- if (ret < 0)
- return ret;
-
- ffs_data_opened(ffs);
+ spin_lock_irq(&ffs->eps_lock);
if (ffs->state == FFS_CLOSING) {
- ffs_data_closed(ffs);
- mutex_unlock(&ffs->mutex);
+ spin_unlock_irq(&ffs->eps_lock);
return -EBUSY;
}
- mutex_unlock(&ffs->mutex);
+ if (!ffs->opened++ && ffs->state == FFS_DEACTIVATED) {
+ ffs->state = FFS_CLOSING;
+ spin_unlock_irq(&ffs->eps_lock);
+ ffs_data_reset(ffs);
+ } else {
+ spin_unlock_irq(&ffs->eps_lock);
+ }
file->private_data = ffs;
return stream_open(inode, file);
@@ -1202,15 +1203,10 @@ ffs_epfile_open(struct inode *inode, struct file *file)
{
struct ffs_data *ffs = inode->i_sb->s_fs_info;
struct ffs_epfile *epfile;
- int ret;
-
- /* Acquire mutex */
- ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
- if (ret < 0)
- return ret;
- if (!atomic_inc_not_zero(&ffs->opened)) {
- mutex_unlock(&ffs->mutex);
+ spin_lock_irq(&ffs->eps_lock);
+ if (!ffs->opened) {
+ spin_unlock_irq(&ffs->eps_lock);
return -ENODEV;
}
/*
@@ -1220,11 +1216,11 @@ ffs_epfile_open(struct inode *inode, struct file *file)
*/
epfile = smp_load_acquire(&inode->i_private);
if (unlikely(ffs->state != FFS_ACTIVE || !epfile)) {
- mutex_unlock(&ffs->mutex);
- ffs_data_closed(ffs);
+ spin_unlock_irq(&ffs->eps_lock);
return -ENODEV;
}
- mutex_unlock(&ffs->mutex);
+ ffs->opened++;
+ spin_unlock_irq(&ffs->eps_lock);
file->private_data = epfile;
return stream_open(inode, file);
@@ -2092,8 +2088,6 @@ static int ffs_fs_init_fs_context(struct fs_context *fc)
return 0;
}
-static void ffs_data_reset(struct ffs_data *ffs);
-
static void
ffs_fs_kill_sb(struct super_block *sb)
{
@@ -2150,15 +2144,6 @@ static void ffs_data_get(struct ffs_data *ffs)
refcount_inc(&ffs->ref);
}
-static void ffs_data_opened(struct ffs_data *ffs)
-{
- if (atomic_add_return(1, &ffs->opened) == 1 &&
- ffs->state == FFS_DEACTIVATED) {
- ffs->state = FFS_CLOSING;
- ffs_data_reset(ffs);
- }
-}
-
static void ffs_data_put(struct ffs_data *ffs)
{
if (refcount_dec_and_test(&ffs->ref)) {
@@ -2176,28 +2161,29 @@ static void ffs_data_put(struct ffs_data *ffs)
static void ffs_data_closed(struct ffs_data *ffs)
{
- if (atomic_dec_and_test(&ffs->opened)) {
- if (ffs->no_disconnect) {
- struct ffs_epfile *epfiles;
- unsigned long flags;
-
- ffs->state = FFS_DEACTIVATED;
- spin_lock_irqsave(&ffs->eps_lock, flags);
- epfiles = ffs->epfiles;
- ffs->epfiles = NULL;
- spin_unlock_irqrestore(&ffs->eps_lock,
- flags);
-
- if (epfiles)
- ffs_epfiles_destroy(ffs->sb, epfiles,
- ffs->eps_count);
-
- if (ffs->setup_state == FFS_SETUP_PENDING)
- __ffs_ep0_stall(ffs);
- } else {
- ffs->state = FFS_CLOSING;
- ffs_data_reset(ffs);
- }
+ spin_lock_irq(&ffs->eps_lock);
+ if (--ffs->opened) { // not the last opener?
+ spin_unlock_irq(&ffs->eps_lock);
+ return;
+ }
+ if (ffs->no_disconnect) {
+ struct ffs_epfile *epfiles;
+
+ ffs->state = FFS_DEACTIVATED;
+ epfiles = ffs->epfiles;
+ ffs->epfiles = NULL;
+ spin_unlock_irq(&ffs->eps_lock);
+
+ if (epfiles)
+ ffs_epfiles_destroy(ffs->sb, epfiles,
+ ffs->eps_count);
+
+ if (ffs->setup_state == FFS_SETUP_PENDING)
+ __ffs_ep0_stall(ffs);
+ } else {
+ ffs->state = FFS_CLOSING;
+ spin_unlock_irq(&ffs->eps_lock);
+ ffs_data_reset(ffs);
}
}
@@ -2214,7 +2200,7 @@ static struct ffs_data *ffs_data_new(const char *dev_name)
}
refcount_set(&ffs->ref, 1);
- atomic_set(&ffs->opened, 0);
+ ffs->opened = 0;
ffs->state = FFS_READ_DESCRIPTORS;
mutex_init(&ffs->mutex);
spin_lock_init(&ffs->eps_lock);
@@ -2266,6 +2252,7 @@ static void ffs_data_reset(struct ffs_data *ffs)
{
ffs_data_clear(ffs);
+ spin_lock_irq(&ffs->eps_lock);
ffs->raw_descs_data = NULL;
ffs->raw_descs = NULL;
ffs->raw_strings = NULL;
@@ -2289,6 +2276,7 @@ static void ffs_data_reset(struct ffs_data *ffs)
ffs->ms_os_descs_ext_prop_count = 0;
ffs->ms_os_descs_ext_prop_name_len = 0;
ffs->ms_os_descs_ext_prop_data_len = 0;
+ spin_unlock_irq(&ffs->eps_lock);
}
@@ -3756,6 +3744,7 @@ static int ffs_func_set_alt(struct usb_function *f,
{
struct ffs_function *func = ffs_func_from_usb(f);
struct ffs_data *ffs = func->ffs;
+ unsigned long flags;
int ret = 0, intf;
if (alt > MAX_ALT_SETTINGS)
@@ -3768,12 +3757,15 @@ static int ffs_func_set_alt(struct usb_function *f,
if (ffs->func)
ffs_func_eps_disable(ffs->func);
+ spin_lock_irqsave(&ffs->eps_lock, flags);
if (ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
INIT_WORK(&ffs->reset_work, ffs_reset_work);
schedule_work(&ffs->reset_work);
return -ENODEV;
}
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
if (ffs->state != FFS_ACTIVE)
return -ENODEV;
@@ -3791,16 +3783,20 @@ static void ffs_func_disable(struct usb_function *f)
{
struct ffs_function *func = ffs_func_from_usb(f);
struct ffs_data *ffs = func->ffs;
+ unsigned long flags;
if (ffs->func)
ffs_func_eps_disable(ffs->func);
+ spin_lock_irqsave(&ffs->eps_lock, flags);
if (ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
INIT_WORK(&ffs->reset_work, ffs_reset_work);
schedule_work(&ffs->reset_work);
return;
}
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
if (ffs->state == FFS_ACTIVE) {
ffs->func = NULL;
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index 4b3365f23fd7..6a80182aadd7 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -176,7 +176,7 @@ struct ffs_data {
/* reference counter */
refcount_t ref;
/* how many files are opened (EP0 and others) */
- atomic_t opened;
+ int opened;
/* EP0 state */
enum ffs_state state;
^ permalink raw reply related [flat|nested] 26+ messages in thread
end of thread, other threads:[~2026-02-01 0:09 UTC | newest]
Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20251118051604.3868588-1-viro@zeniv.linux.org.uk>
2026-01-27 0:56 ` [PATCH v4 00/54] tree-in-dcache stuff Samuel Wu
2026-01-27 7:42 ` Greg KH
2026-01-27 18:39 ` Linus Torvalds
2026-01-27 20:14 ` Al Viro
2026-01-28 8:53 ` Greg KH
2026-01-28 2:02 ` Samuel Wu
2026-01-28 4:59 ` Al Viro
2026-01-29 0:58 ` Samuel Wu
2026-01-29 3:23 ` Al Viro
2026-01-29 22:54 ` Al Viro
2026-01-30 1:16 ` Samuel Wu
2026-01-30 7:04 ` Al Viro
2026-01-30 22:31 ` Samuel Wu
2026-01-30 23:57 ` Al Viro
2026-01-31 0:14 ` Linus Torvalds
2026-01-31 1:08 ` Al Viro
2026-01-31 1:11 ` Linus Torvalds
2026-02-01 0:11 ` Al Viro
2026-01-31 0:59 ` Al Viro
2026-01-31 1:05 ` Samuel Wu
2026-01-31 1:18 ` Al Viro
2026-01-31 2:09 ` Samuel Wu
2026-01-31 2:43 ` Al Viro
2026-01-31 19:48 ` Samuel Wu
2026-01-31 14:58 ` Krishna Kurapati PSSNV
2026-01-31 20:02 ` Samuel Wu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox