Skip to content

Commit 11253d7

Browse files
jtlaytonopsiff
authored andcommitted
fs: try an opportunistic lookup for O_CREAT opens too
Today, when opening a file we'll typically do a fast lookup, but if O_CREAT is set, the kernel always takes the exclusive inode lock. I assume this was done with the expectation that O_CREAT means that we always expect to do the create, but that's often not the case. Many programs set O_CREAT even in scenarios where the file already exists. This patch rearranges the pathwalk-for-open code to also attempt a fast_lookup in certain O_CREAT cases. If a positive dentry is found, the inode_lock can be avoided altogether, and if auditing isn't enabled, it can stay in rcuwalk mode for the last step_into. One notable exception that is hopefully temporary: if we're doing an rcuwalk and auditing is enabled, skip the lookup_fast. Legitimizing the dentry in that case is more expensive than taking the i_rwsem for now. Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://lore.kernel.org/r/20240807-openfast-v3-1-040d132d2559@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Christian Brauner <brauner@kernel.org> (cherry picked from commit e747e15)
1 parent 642fb17 commit 11253d7

1 file changed

Lines changed: 64 additions & 10 deletions

File tree

fs/namei.c

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3501,6 +3501,49 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
35013501
return ERR_PTR(error);
35023502
}
35033503

3504+
static inline bool trailing_slashes(struct nameidata *nd)
3505+
{
3506+
return (bool)nd->last.name[nd->last.len];
3507+
}
3508+
3509+
static struct dentry *lookup_fast_for_open(struct nameidata *nd, int open_flag)
3510+
{
3511+
struct dentry *dentry;
3512+
3513+
if (open_flag & O_CREAT) {
3514+
/* Don't bother on an O_EXCL create */
3515+
if (open_flag & O_EXCL)
3516+
return NULL;
3517+
3518+
/*
3519+
* FIXME: If auditing is enabled, then we'll have to unlazy to
3520+
* use the dentry. For now, don't do this, since it shifts
3521+
* contention from parent's i_rwsem to its d_lockref spinlock.
3522+
* Reconsider this once dentry refcounting handles heavy
3523+
* contention better.
3524+
*/
3525+
if ((nd->flags & LOOKUP_RCU) && !audit_dummy_context())
3526+
return NULL;
3527+
}
3528+
3529+
if (trailing_slashes(nd))
3530+
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
3531+
3532+
dentry = lookup_fast(nd);
3533+
if (IS_ERR_OR_NULL(dentry))
3534+
return dentry;
3535+
3536+
if (open_flag & O_CREAT) {
3537+
/* Discard negative dentries. Need inode_lock to do the create */
3538+
if (!dentry->d_inode) {
3539+
if (!(nd->flags & LOOKUP_RCU))
3540+
dput(dentry);
3541+
dentry = NULL;
3542+
}
3543+
}
3544+
return dentry;
3545+
}
3546+
35043547
static const char *open_last_lookups(struct nameidata *nd,
35053548
struct file *file, const struct open_flags *op)
35063549
{
@@ -3518,27 +3561,38 @@ static const char *open_last_lookups(struct nameidata *nd,
35183561
return handle_dots(nd, nd->last_type);
35193562
}
35203563

3564+
/* We _can_ be in RCU mode here */
3565+
dentry = lookup_fast_for_open(nd, open_flag);
3566+
if (IS_ERR(dentry))
3567+
return ERR_CAST(dentry);
3568+
35213569
if (!(open_flag & O_CREAT)) {
3522-
if (nd->last.name[nd->last.len])
3523-
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
3524-
/* we _can_ be in RCU mode here */
3525-
dentry = lookup_fast(nd);
3526-
if (IS_ERR(dentry))
3527-
return ERR_CAST(dentry);
35283570
if (likely(dentry))
35293571
goto finish_lookup;
35303572

35313573
BUG_ON(nd->flags & LOOKUP_RCU);
35323574
} else {
3533-
/* create side of things */
35343575
if (nd->flags & LOOKUP_RCU) {
3535-
if (!try_to_unlazy(nd))
3576+
bool unlazied;
3577+
3578+
/* can stay in rcuwalk if not auditing */
3579+
if (dentry && audit_dummy_context()) {
3580+
if (trailing_slashes(nd))
3581+
return ERR_PTR(-EISDIR);
3582+
goto finish_lookup;
3583+
}
3584+
unlazied = dentry ? try_to_unlazy_next(nd, dentry) :
3585+
try_to_unlazy(nd);
3586+
if (!unlazied)
35363587
return ERR_PTR(-ECHILD);
35373588
}
35383589
audit_inode(nd->name, dir, AUDIT_INODE_PARENT);
3539-
/* trailing slashes? */
3540-
if (unlikely(nd->last.name[nd->last.len]))
3590+
if (trailing_slashes(nd)) {
3591+
dput(dentry);
35413592
return ERR_PTR(-EISDIR);
3593+
}
3594+
if (dentry)
3595+
goto finish_lookup;
35423596
}
35433597

35443598
if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {

0 commit comments

Comments
 (0)