mirror of
https://github.com/torvalds/linux.git
synced 2026-10-24 16:55:02 +02:00
VFS: enhance d_splice_alias() to handle hashed dentries
We currently have three interfaces for attaching existing inodes to normal filesystems(*). - d_add() requires an unhashed or in-lookup dentry and doesn't handle splicing in case a directory already has dentry - d_instantiate() requires a hashed dentry, and also doesn't handle splicing. - d_splice_alias() requires unhashed or in-lookup and does handle splicing, and can return an alternate dentry. So there is no interface that supports both hashed and in-lookup, which is what ->atomic_open needs to deal with. Some filesystems check for in-lookup in their atomic_open and if found, perform a ->lookup and can subsequently use d_instantiate() if the dentry is still negative. Others d_drop() the dentry so they can use d_splice_alias(). This last will cause a problem for proposed changes to locking which require the dentry to remain hashed while an operation proceeds on it. There is also no interface which splices a directory (which might already have a dentry) to a hashed dentry. Filesystems which need to do this d_drop() first. Some filesystems (NFS) skip ->lookup processing for LOOKUP_CREATE|LOOKUP_EXCL which includes mknod, link, symlink etc. So these inode operations might get an unhashed or a hashed-negative dentry. There is no interface for instantiating these so again they need to unhash first (nfs_link) So with this patch d_splice_alias() can handle hashed, unhashed, or in-lookup dentries. This makes it suitable for ->lookup, ->atomic_open, and ->mkdir as well as others. As a side effect d_add() will also now handle hashed dentries, but I have plans to remove d_add() as there is no benefit having it as well as the others. Once updated to handle nr_dentry_negative as is required for hashed dentrties, __d_add() contains code that is identical to __d_instantiate(), so the former is changed to call the later so now: - d_add() calls __d_add() which hashes and might call __d_instantiate. - d_instantiate() calls __d_instantiate() with appropriate locks. It was suggested by Al Viro https://lore.kernel.org/all/20250813050717.GD222315@ZenIV/ that rather than allow d_splice_alias() to handle both hashed and unhashed, we should have a new d_splice_alias_hashed(). I chose not to follow this path because, as noted above, there are several cases where the filesystem has no a priori knowledge of the state of the dentry, and so would need if (d_unhashed(dentry)) alias = d_splice_alias(inode, dentry); else alias = d_splice_alias_hashed(inode, dentry); which is clumsy. Also I hope to minimise the distinction between hashed and in-lookup (they will both be hashed, just with different DCACHE_ENTRY_TYPE) and reduce the use of unhashed dentries. Unhashed dentries would only be created by d_alloc_name() and all of those are passed to d_make_persistent() (though configfs passes some to d_add(dentry, NULL) first!). So the use-case for d_splice_alias() on unhashed dentries would disappear. Note that d_make_persistent() already handles both hashed and unhashed dentries - just not in-lookup. * There is also d_make_persistent() for filesystems which are dcache-based and don't support mkdir, create etc, and d_instantiate_new() for newly created inodes that are still locked. Signed-off-by: NeilBrown <neil@brown.name> Link: https://patch.msgid.link/20260904215142.1060510-3-neilb@ownmail.net Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
parent
f8550827bf
commit
f1ac607f78
|
|
@ -507,8 +507,8 @@ otherwise noted.
|
|||
dentry before the first mkdir returns.
|
||||
|
||||
If there is any chance this could happen, then the new inode
|
||||
should be d_drop()ed and attached with d_splice_alias(). The
|
||||
returned dentry (if any) should be returned by ->mkdir().
|
||||
should be attached with d_splice_alias(). The returned
|
||||
dentry (if any) should be returned by ->mkdir().
|
||||
|
||||
``rmdir``
|
||||
called by the rmdir(2) system call. Only required if you want
|
||||
|
|
|
|||
30
fs/dcache.c
30
fs/dcache.c
|
|
@ -2168,7 +2168,6 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
|
|||
* (or otherwise set) by the caller to indicate that it is now
|
||||
* in use by the dcache.
|
||||
*/
|
||||
|
||||
void d_instantiate(struct dentry *entry, struct inode * inode)
|
||||
{
|
||||
BUG_ON(d_really_is_positive(entry));
|
||||
|
|
@ -2931,15 +2930,10 @@ static inline void __d_add(struct dentry *dentry, struct inode *inode,
|
|||
}
|
||||
if (unlikely(ops))
|
||||
d_set_d_op(dentry, ops);
|
||||
if (inode) {
|
||||
unsigned add_flags = d_flags_for_inode(inode);
|
||||
hlist_add_head(&dentry->d_alias, &inode->i_dentry);
|
||||
raw_write_seqcount_begin(&dentry->d_seq);
|
||||
__d_set_inode_and_type(dentry, inode, add_flags);
|
||||
raw_write_seqcount_end(&dentry->d_seq);
|
||||
fsnotify_update_flags(dentry);
|
||||
}
|
||||
__d_rehash(dentry);
|
||||
if (inode)
|
||||
__d_instantiate(dentry, inode);
|
||||
if (d_unhashed(dentry))
|
||||
__d_rehash(dentry);
|
||||
if (dir) {
|
||||
end_dir_add(dir, n);
|
||||
__d_wake_in_lookup_waiters(dentry);
|
||||
|
|
@ -3241,7 +3235,7 @@ struct dentry *d_splice_alias_ops(struct inode *inode, struct dentry *dentry,
|
|||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
|
||||
BUG_ON(!d_unhashed(dentry));
|
||||
BUG_ON(d_really_is_positive(dentry));
|
||||
|
||||
if (!inode)
|
||||
goto out;
|
||||
|
|
@ -3297,6 +3291,8 @@ struct dentry *d_splice_alias_ops(struct inode *inode, struct dentry *dentry,
|
|||
* @inode: the inode which may have a disconnected dentry
|
||||
* @dentry: a negative dentry which we want to point to the inode.
|
||||
*
|
||||
* @dentry must be negative and may be in-lookup or unhashed or hashed.
|
||||
*
|
||||
* If inode is a directory and has an IS_ROOT alias, then d_move that in
|
||||
* place of the given dentry and return it, else simply d_add the inode
|
||||
* to the dentry and return NULL.
|
||||
|
|
@ -3304,16 +3300,14 @@ struct dentry *d_splice_alias_ops(struct inode *inode, struct dentry *dentry,
|
|||
* If a non-IS_ROOT directory is found, the filesystem is corrupt, and
|
||||
* we should error out: directories can't have multiple aliases.
|
||||
*
|
||||
* This is needed in the lookup routine of any filesystem that is exportable
|
||||
* (via knfsd) so that we can build dcache paths to directories effectively.
|
||||
* This should be used to return the result of ->lookup() and to
|
||||
* instantiate the result of ->mkdir(), is often useful for
|
||||
* ->atomic_open, and may be used to instantiate other objects.
|
||||
*
|
||||
* If a dentry was found and moved, then it is returned. Otherwise NULL
|
||||
* is returned. This matches the expected return value of ->lookup.
|
||||
* is returned. This matches the expected return value of ->lookup and
|
||||
* ->mkdir.
|
||||
*
|
||||
* Cluster filesystems may call this function with a negative, hashed dentry.
|
||||
* In that case, we know that the inode will be a regular file, and also this
|
||||
* will only occur during atomic_open. So we need to check for the dentry
|
||||
* being already hashed only in the final case.
|
||||
*/
|
||||
struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user