Merge tag 'pull-dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 12 Jan 2024 04:11:35 +0000 (20:11 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 12 Jan 2024 04:11:35 +0000 (20:11 -0800)
Pull dcache updates from Al Viro:
 "Change of locking rules for __dentry_kill(), regularized refcounting
  rules in that area, assorted cleanups and removal of weird corner
  cases (e.g. now ->d_iput() on child is always called before the parent
  might hit __dentry_kill(), etc)"

* tag 'pull-dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (40 commits)
  dcache: remove unnecessary NULL check in dget_dlock()
  kill DCACHE_MAY_FREE
  __d_unalias() doesn't use inode argument
  d_alloc_parallel(): in-lookup hash insertion doesn't need an RCU variant
  get rid of DCACHE_GENOCIDE
  d_genocide(): move the extern into fs/internal.h
  simple_fill_super(): don't bother with d_genocide() on failure
  nsfs: use d_make_root()
  d_alloc_pseudo(): move setting ->d_op there from the (sole) caller
  kill d_instantate_anon(), fold __d_instantiate_anon() into remaining caller
  retain_dentry(): introduce a trimmed-down lockless variant
  __dentry_kill(): new locking scheme
  d_prune_aliases(): use a shrink list
  switch select_collect{,2}() to use of to_shrink_list()
  to_shrink_list(): call only if refcount is 0
  fold dentry_kill() into dput()
  don't try to cut corners in shrink_lock_dentry()
  fold the call of retain_dentry() into fast_dput()
  Call retain_dentry() with refcount 0
  dentry_kill(): don't bother with retain_dentry() on slow path
  ...

1  2 
Documentation/filesystems/porting.rst
fs/afs/dynroot.c
fs/dcache.c
fs/file_table.c
fs/internal.h
fs/libfs.c
fs/nfsd/nfsctl.c
fs/overlayfs/export.c
fs/tracefs/inode.c

index c549fb2fc3ba7adb85edbdb573f2b801215372b1,d890ef07a9fdff726fa357c3efb9d7a8dda16bb4..1be76ef117b3f80051713c8dc24f0f2568248075
@@@ -1066,37 -1066,32 +1066,71 @@@ generic_encode_ino32_fh() explicitly
  
  **mandatory**
  
 ---
 +If ->rename() update of .. on cross-directory move needs an exclusion with
 +directory modifications, do *not* lock the subdirectory in question in your
 +->rename() - it's done by the caller now [that item should've been added in
 +28eceeda130f "fs: Lock moved directories"].
 +
 +---
 +
 +**mandatory**
 +
 +On same-directory ->rename() the (tautological) update of .. is not protected
 +by any locks; just don't do it if the old parent is the same as the new one.
 +We really can't lock two subdirectories in same-directory rename - not without
 +deadlocks.
 +
 +---
 +
 +**mandatory**
 +
 +lock_rename() and lock_rename_child() may fail in cross-directory case, if
 +their arguments do not have a common ancestor.  In that case ERR_PTR(-EXDEV)
 +is returned, with no locks taken.  In-tree users updated; out-of-tree ones
 +would need to do so.
 +
 +---
 +
++**mandatory**
++
+ The list of children anchored in parent dentry got turned into hlist now.
+ Field names got changed (->d_children/->d_sib instead of ->d_subdirs/->d_child
+ for anchor/entries resp.), so any affected places will be immediately caught
+ by compiler.
+ ---
+ **mandatory**
+ ->d_delete() instances are now called for dentries with ->d_lock held
+ and refcount equal to 0.  They are not permitted to drop/regain ->d_lock.
+ None of in-tree instances did anything of that sort.  Make sure yours do not...
++---
+ **mandatory**
+ ->d_prune() instances are now called without ->d_lock held on the parent.
+ ->d_lock on dentry itself is still held; if you need per-parent exclusions (none
+ of the in-tree instances did), use your own spinlock.
+ ->d_iput() and ->d_release() are called with victim dentry still in the
+ list of parent's children.  It is still unhashed, marked killed, etc., just not
+ removed from parent's ->d_children yet.
+ Anyone iterating through the list of children needs to be aware of the
+ half-killed dentries that might be seen there; taking ->d_lock on those will
+ see them negative, unhashed and with negative refcount, which means that most
+ of the in-kernel users would've done the right thing anyway without any adjustment.
++
++---
++
 +**recommended**
 +
 +Block device freezing and thawing have been moved to holder operations.
 +
 +Before this change, get_active_super() would only be able to find the
 +superblock of the main block device, i.e., the one stored in sb->s_bdev. Block
 +device freezing now works for any block device owned by a given superblock, not
 +just the main block device. The get_active_super() helper and bd_fsfreeze_sb
 +pointer are gone.
Simple merge
diff --cc fs/dcache.c
Simple merge
diff --cc fs/file_table.c
Simple merge
diff --cc fs/internal.h
Simple merge
diff --cc fs/libfs.c
Simple merge
Simple merge
Simple merge
index bc86ffdb103bc526846884a8b78ba9f1bfb7a5d4,61ca5fcf10f9160d9016a280c494a489ceec7589..ad20e6af938d9b68df7b27e08d44a5351f0d977e
@@@ -207,28 -206,14 +206,25 @@@ static void set_gid(struct dentry *pare
  
        change_gid(this_parent, gid);
  repeat:
-       next = this_parent->d_subdirs.next;
+       dentry = d_first_child(this_parent);
  resume:
-       while (next != &this_parent->d_subdirs) {
+       hlist_for_each_entry_from(dentry, d_sib) {
 +              struct tracefs_inode *ti;
-               struct list_head *tmp = next;
-               struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
-               next = tmp->next;
 +
 +              /* Note, getdents() can add a cursor dentry with no inode */
 +              if (!dentry->d_inode)
 +                      continue;
 +
                spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
  
                change_gid(dentry, gid);
  
-               if (!list_empty(&dentry->d_subdirs)) {
 +              /* If this is the events directory, update that too */
 +              ti = get_tracefs(dentry->d_inode);
 +              if (ti && (ti->flags & TRACEFS_EVENT_INODE))
 +                      eventfs_update_gid(dentry, gid);
 +
+               if (!hlist_empty(&dentry->d_children)) {
                        spin_unlock(&this_parent->d_lock);
                        spin_release(&dentry->d_lock.dep_map, _RET_IP_);
                        this_parent = dentry;