diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-06-16 15:00:35 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-07-07 10:27:22 -0700 |
commit | 58b0afa038bb404ff82b358b68db5ffa8f53c404 (patch) | |
tree | 5433288bbb114d2aa5400ce06abbc7bdc4dde33f /fs | |
parent | ba848a77c90800cb686a5c8cf725e9bdfdcccfc2 (diff) |
vfs: link_path_walk: improve may_lookup() code generation
Instead of having separate calls to 'inode_permission()' depending on
whether we're in RCU lookup or not, just share the first call.
Note that the initial "conditional" on LOOKUP_RCU really turns into just
a "convert the LOOKUP_RCU bit in the nameidata into the MAY_NOT_BLOCK
bit in the argument", which is just a trivial bitwise and and shift
operation.
So the initial conditional goes away entirely, and then the likely case
is that it will succeed independently of us being in RCU lookup or not,
and the possible "we may need to fall out of RCU and redo it all" fixups
that are needed afterwards all go in the unlikely path.
[ This also marks 'nd' restrict, because that means that the compiler
can know that there is no other alias, and can cache the LOOKUP_RCU
value over the call to inode_permission(). ]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/namei.c | 29 |
1 files changed, 19 insertions, 10 deletions
diff --git a/fs/namei.c b/fs/namei.c index 928cbc856d5b..6dcb1be1aca9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1712,17 +1712,26 @@ static struct dentry *lookup_slow(const struct qstr *name, } static inline int may_lookup(struct mnt_idmap *idmap, - struct nameidata *nd) + struct nameidata *restrict nd) { - if (nd->flags & LOOKUP_RCU) { - int err = inode_permission(idmap, nd->inode, MAY_EXEC|MAY_NOT_BLOCK); - if (!err) // success, keep going - return 0; - if (!try_to_unlazy(nd)) - return -ECHILD; // redo it all non-lazy - if (err != -ECHILD) // hard error - return err; - } + int err, mask; + + mask = nd->flags & LOOKUP_RCU ? MAY_NOT_BLOCK : 0; + err = inode_permission(idmap, nd->inode, mask | MAY_EXEC); + if (likely(!err)) + return 0; + + // If we failed, and we weren't in LOOKUP_RCU, it's final + if (!(nd->flags & LOOKUP_RCU)) + return err; + + // Drop out of RCU mode to make sure it wasn't transient + if (!try_to_unlazy(nd)) + return -ECHILD; // redo it all non-lazy + + if (err != -ECHILD) // hard error + return err; + return inode_permission(idmap, nd->inode, MAY_EXEC); } |