From: Linus Torvalds Date: Fri, 12 Jan 2024 04:11:35 +0000 (-0800) Subject: Merge tag 'pull-dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=499aa1ca4eb6602df38afaecb88fc14edf50cdbb;p=linux.git Merge tag 'pull-dcache' of git://git./linux/kernel/git/viro/vfs 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 ... --- 499aa1ca4eb6602df38afaecb88fc14edf50cdbb diff --cc Documentation/filesystems/porting.rst index c549fb2fc3ba7,d890ef07a9fdf..1be76ef117b3f --- a/Documentation/filesystems/porting.rst +++ b/Documentation/filesystems/porting.rst @@@ -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. diff --cc fs/tracefs/inode.c index bc86ffdb103bc,61ca5fcf10f91..ad20e6af938d9 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@@ -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 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 (!list_empty(&dentry->d_subdirs)) { + if (!hlist_empty(&dentry->d_children)) { spin_unlock(&this_parent->d_lock); spin_release(&dentry->d_lock.dep_map, _RET_IP_); this_parent = dentry;