Commit 5e049755 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'link_path_walk'

This is the last - for now - of the "look, we generated some
questionable code for basic pathname lookup operations" set of
branches.

This is mainly just re-organizing the name hashing code in
link_path_walk(), mostly by improving the calling conventions to
the inlined helper functions and moving some of the code around
to allow for more straightforward code generation.

The profiles - and the generated code - look much more palatable
to me now.

* link_path_walk:
  vfs: link_path_walk: move more of the name hashing into hash_name()
  vfs: link_path_walk: improve may_lookup() code generation
  vfs: link_path_walk: do '.' and '..' detection while hashing
  vfs: link_path_walk: clarify and improve name hashing interface
  vfs: link_path_walk: simplify name hash flow
parents 1654c37d 13694f0d
...@@ -1712,17 +1712,26 @@ static struct dentry *lookup_slow(const struct qstr *name, ...@@ -1712,17 +1712,26 @@ static struct dentry *lookup_slow(const struct qstr *name,
} }
static inline int may_lookup(struct mnt_idmap *idmap, static inline int may_lookup(struct mnt_idmap *idmap,
struct nameidata *nd) struct nameidata *restrict nd)
{ {
if (nd->flags & LOOKUP_RCU) { int err, mask;
int err = inode_permission(idmap, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
if (!err) // success, keep going mask = nd->flags & LOOKUP_RCU ? MAY_NOT_BLOCK : 0;
err = inode_permission(idmap, nd->inode, mask | MAY_EXEC);
if (likely(!err))
return 0; 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)) if (!try_to_unlazy(nd))
return -ECHILD; // redo it all non-lazy return -ECHILD; // redo it all non-lazy
if (err != -ECHILD) // hard error if (err != -ECHILD) // hard error
return err; return err;
}
return inode_permission(idmap, nd->inode, MAY_EXEC); return inode_permission(idmap, nd->inode, MAY_EXEC);
} }
...@@ -2163,21 +2172,39 @@ EXPORT_SYMBOL(hashlen_string); ...@@ -2163,21 +2172,39 @@ EXPORT_SYMBOL(hashlen_string);
/* /*
* Calculate the length and hash of the path component, and * Calculate the length and hash of the path component, and
* return the "hash_len" as the result. * return the length as the result.
*/ */
static inline u64 hash_name(const void *salt, const char *name) static inline const char *hash_name(struct nameidata *nd,
const char *name,
unsigned long *lastword)
{ {
unsigned long a = 0, b, x = 0, y = (unsigned long)salt; unsigned long a, b, x, y = (unsigned long)nd->path.dentry;
unsigned long adata, bdata, mask, len; unsigned long adata, bdata, mask, len;
const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
len = 0; /*
goto inside; * The first iteration is special, because it can result in
* '.' and '..' and has no mixing other than the final fold.
*/
a = load_unaligned_zeropad(name);
b = a ^ REPEAT_BYTE('/');
if (has_zero(a, &adata, &constants) | has_zero(b, &bdata, &constants)) {
adata = prep_zero_mask(a, adata, &constants);
bdata = prep_zero_mask(b, bdata, &constants);
mask = create_zero_mask(adata | bdata);
a &= zero_bytemask(mask);
*lastword = a;
len = find_zero(mask);
nd->last.hash = fold_hash(a, y);
nd->last.len = len;
return name + len;
}
len = 0;
x = 0;
do { do {
HASH_MIX(x, y, a); HASH_MIX(x, y, a);
len += sizeof(unsigned long); len += sizeof(unsigned long);
inside:
a = load_unaligned_zeropad(name+len); a = load_unaligned_zeropad(name+len);
b = a ^ REPEAT_BYTE('/'); b = a ^ REPEAT_BYTE('/');
} while (!(has_zero(a, &adata, &constants) | has_zero(b, &bdata, &constants))); } while (!(has_zero(a, &adata, &constants) | has_zero(b, &bdata, &constants)));
...@@ -2185,11 +2212,25 @@ static inline u64 hash_name(const void *salt, const char *name) ...@@ -2185,11 +2212,25 @@ static inline u64 hash_name(const void *salt, const char *name)
adata = prep_zero_mask(a, adata, &constants); adata = prep_zero_mask(a, adata, &constants);
bdata = prep_zero_mask(b, bdata, &constants); bdata = prep_zero_mask(b, bdata, &constants);
mask = create_zero_mask(adata | bdata); mask = create_zero_mask(adata | bdata);
x ^= a & zero_bytemask(mask); a &= zero_bytemask(mask);
x ^= a;
len += find_zero(mask);
*lastword = 0; // Multi-word components cannot be DOT or DOTDOT
return hashlen_create(fold_hash(x, y), len + find_zero(mask)); nd->last.hash = fold_hash(x, y);
nd->last.len = len;
return name + len;
} }
/*
* Note that the 'last' word is always zero-masked, but
* was loaded as a possibly big-endian word.
*/
#ifdef __BIG_ENDIAN
#define LAST_WORD_IS_DOT (0x2eul << (BITS_PER_LONG-8))
#define LAST_WORD_IS_DOTDOT (0x2e2eul << (BITS_PER_LONG-16))
#endif
#else /* !CONFIG_DCACHE_WORD_ACCESS: Slow, byte-at-a-time version */ #else /* !CONFIG_DCACHE_WORD_ACCESS: Slow, byte-at-a-time version */
/* Return the hash of a string of known length */ /* Return the hash of a string of known length */
...@@ -2222,22 +2263,35 @@ EXPORT_SYMBOL(hashlen_string); ...@@ -2222,22 +2263,35 @@ EXPORT_SYMBOL(hashlen_string);
* We know there's a real path component here of at least * We know there's a real path component here of at least
* one character. * one character.
*/ */
static inline u64 hash_name(const void *salt, const char *name) static inline const char *hash_name(struct nameidata *nd, const char *name, unsigned long *lastword)
{ {
unsigned long hash = init_name_hash(salt); unsigned long hash = init_name_hash(nd->path.dentry);
unsigned long len = 0, c; unsigned long len = 0, c, last = 0;
c = (unsigned char)*name; c = (unsigned char)*name;
do { do {
last = (last << 8) + c;
len++; len++;
hash = partial_name_hash(c, hash); hash = partial_name_hash(c, hash);
c = (unsigned char)name[len]; c = (unsigned char)name[len];
} while (c && c != '/'); } while (c && c != '/');
return hashlen_create(end_name_hash(hash), len);
// This is reliable for DOT or DOTDOT, since the component
// cannot contain NUL characters - top bits being zero means
// we cannot have had any other pathnames.
*lastword = last;
nd->last.hash = end_name_hash(hash);
nd->last.len = len;
return name + len;
} }
#endif #endif
#ifndef LAST_WORD_IS_DOT
#define LAST_WORD_IS_DOT 0x2e
#define LAST_WORD_IS_DOTDOT 0x2e2e
#endif
/* /*
* Name resolution. * Name resolution.
* This is the basic name resolution function, turning a pathname into * This is the basic name resolution function, turning a pathname into
...@@ -2266,45 +2320,38 @@ static int link_path_walk(const char *name, struct nameidata *nd) ...@@ -2266,45 +2320,38 @@ static int link_path_walk(const char *name, struct nameidata *nd)
for(;;) { for(;;) {
struct mnt_idmap *idmap; struct mnt_idmap *idmap;
const char *link; const char *link;
u64 hash_len; unsigned long lastword;
int type;
idmap = mnt_idmap(nd->path.mnt); idmap = mnt_idmap(nd->path.mnt);
err = may_lookup(idmap, nd); err = may_lookup(idmap, nd);
if (err) if (err)
return err; return err;
hash_len = hash_name(nd->path.dentry, name); nd->last.name = name;
name = hash_name(nd, name, &lastword);
type = LAST_NORM; switch(lastword) {
if (name[0] == '.') switch (hashlen_len(hash_len)) { case LAST_WORD_IS_DOTDOT:
case 2: nd->last_type = LAST_DOTDOT;
if (name[1] == '.') {
type = LAST_DOTDOT;
nd->state |= ND_JUMPED; nd->state |= ND_JUMPED;
}
break; break;
case 1:
type = LAST_DOT; case LAST_WORD_IS_DOT:
} nd->last_type = LAST_DOT;
if (likely(type == LAST_NORM)) { break;
struct dentry *parent = nd->path.dentry;
default:
nd->last_type = LAST_NORM;
nd->state &= ~ND_JUMPED; nd->state &= ~ND_JUMPED;
struct dentry *parent = nd->path.dentry;
if (unlikely(parent->d_flags & DCACHE_OP_HASH)) { if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
struct qstr this = { { .hash_len = hash_len }, .name = name }; err = parent->d_op->d_hash(parent, &nd->last);
err = parent->d_op->d_hash(parent, &this);
if (err < 0) if (err < 0)
return err; return err;
hash_len = this.hash_len;
name = this.name;
} }
} }
nd->last.hash_len = hash_len;
nd->last.name = name;
nd->last_type = type;
name += hashlen_len(hash_len);
if (!*name) if (!*name)
goto OK; goto OK;
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment