Skip to content

Commit 5e04975

Browse files
committed
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
2 parents 1654c37 + 13694f0 commit 5e04975

File tree

1 file changed

+94
-47
lines changed

1 file changed

+94
-47
lines changed

fs/namei.c

Lines changed: 94 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,17 +1712,26 @@ static struct dentry *lookup_slow(const struct qstr *name,
17121712
}
17131713

17141714
static inline int may_lookup(struct mnt_idmap *idmap,
1715-
struct nameidata *nd)
1715+
struct nameidata *restrict nd)
17161716
{
1717-
if (nd->flags & LOOKUP_RCU) {
1718-
int err = inode_permission(idmap, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
1719-
if (!err) // success, keep going
1720-
return 0;
1721-
if (!try_to_unlazy(nd))
1722-
return -ECHILD; // redo it all non-lazy
1723-
if (err != -ECHILD) // hard error
1724-
return err;
1725-
}
1717+
int err, mask;
1718+
1719+
mask = nd->flags & LOOKUP_RCU ? MAY_NOT_BLOCK : 0;
1720+
err = inode_permission(idmap, nd->inode, mask | MAY_EXEC);
1721+
if (likely(!err))
1722+
return 0;
1723+
1724+
// If we failed, and we weren't in LOOKUP_RCU, it's final
1725+
if (!(nd->flags & LOOKUP_RCU))
1726+
return err;
1727+
1728+
// Drop out of RCU mode to make sure it wasn't transient
1729+
if (!try_to_unlazy(nd))
1730+
return -ECHILD; // redo it all non-lazy
1731+
1732+
if (err != -ECHILD) // hard error
1733+
return err;
1734+
17261735
return inode_permission(idmap, nd->inode, MAY_EXEC);
17271736
}
17281737

@@ -2163,33 +2172,65 @@ EXPORT_SYMBOL(hashlen_string);
21632172

21642173
/*
21652174
* Calculate the length and hash of the path component, and
2166-
* return the "hash_len" as the result.
2175+
* return the length as the result.
21672176
*/
2168-
static inline u64 hash_name(const void *salt, const char *name)
2177+
static inline const char *hash_name(struct nameidata *nd,
2178+
const char *name,
2179+
unsigned long *lastword)
21692180
{
2170-
unsigned long a = 0, b, x = 0, y = (unsigned long)salt;
2181+
unsigned long a, b, x, y = (unsigned long)nd->path.dentry;
21712182
unsigned long adata, bdata, mask, len;
21722183
const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
21732184

2174-
len = 0;
2175-
goto inside;
2185+
/*
2186+
* The first iteration is special, because it can result in
2187+
* '.' and '..' and has no mixing other than the final fold.
2188+
*/
2189+
a = load_unaligned_zeropad(name);
2190+
b = a ^ REPEAT_BYTE('/');
2191+
if (has_zero(a, &adata, &constants) | has_zero(b, &bdata, &constants)) {
2192+
adata = prep_zero_mask(a, adata, &constants);
2193+
bdata = prep_zero_mask(b, bdata, &constants);
2194+
mask = create_zero_mask(adata | bdata);
2195+
a &= zero_bytemask(mask);
2196+
*lastword = a;
2197+
len = find_zero(mask);
2198+
nd->last.hash = fold_hash(a, y);
2199+
nd->last.len = len;
2200+
return name + len;
2201+
}
21762202

2203+
len = 0;
2204+
x = 0;
21772205
do {
21782206
HASH_MIX(x, y, a);
21792207
len += sizeof(unsigned long);
2180-
inside:
21812208
a = load_unaligned_zeropad(name+len);
21822209
b = a ^ REPEAT_BYTE('/');
21832210
} while (!(has_zero(a, &adata, &constants) | has_zero(b, &bdata, &constants)));
21842211

21852212
adata = prep_zero_mask(a, adata, &constants);
21862213
bdata = prep_zero_mask(b, bdata, &constants);
21872214
mask = create_zero_mask(adata | bdata);
2188-
x ^= a & zero_bytemask(mask);
2215+
a &= zero_bytemask(mask);
2216+
x ^= a;
2217+
len += find_zero(mask);
2218+
*lastword = 0; // Multi-word components cannot be DOT or DOTDOT
21892219

2190-
return hashlen_create(fold_hash(x, y), len + find_zero(mask));
2220+
nd->last.hash = fold_hash(x, y);
2221+
nd->last.len = len;
2222+
return name + len;
21912223
}
21922224

2225+
/*
2226+
* Note that the 'last' word is always zero-masked, but
2227+
* was loaded as a possibly big-endian word.
2228+
*/
2229+
#ifdef __BIG_ENDIAN
2230+
#define LAST_WORD_IS_DOT (0x2eul << (BITS_PER_LONG-8))
2231+
#define LAST_WORD_IS_DOTDOT (0x2e2eul << (BITS_PER_LONG-16))
2232+
#endif
2233+
21932234
#else /* !CONFIG_DCACHE_WORD_ACCESS: Slow, byte-at-a-time version */
21942235

21952236
/* Return the hash of a string of known length */
@@ -2222,22 +2263,35 @@ EXPORT_SYMBOL(hashlen_string);
22222263
* We know there's a real path component here of at least
22232264
* one character.
22242265
*/
2225-
static inline u64 hash_name(const void *salt, const char *name)
2266+
static inline const char *hash_name(struct nameidata *nd, const char *name, unsigned long *lastword)
22262267
{
2227-
unsigned long hash = init_name_hash(salt);
2228-
unsigned long len = 0, c;
2268+
unsigned long hash = init_name_hash(nd->path.dentry);
2269+
unsigned long len = 0, c, last = 0;
22292270

22302271
c = (unsigned char)*name;
22312272
do {
2273+
last = (last << 8) + c;
22322274
len++;
22332275
hash = partial_name_hash(c, hash);
22342276
c = (unsigned char)name[len];
22352277
} while (c && c != '/');
2236-
return hashlen_create(end_name_hash(hash), len);
2278+
2279+
// This is reliable for DOT or DOTDOT, since the component
2280+
// cannot contain NUL characters - top bits being zero means
2281+
// we cannot have had any other pathnames.
2282+
*lastword = last;
2283+
nd->last.hash = end_name_hash(hash);
2284+
nd->last.len = len;
2285+
return name + len;
22372286
}
22382287

22392288
#endif
22402289

2290+
#ifndef LAST_WORD_IS_DOT
2291+
#define LAST_WORD_IS_DOT 0x2e
2292+
#define LAST_WORD_IS_DOTDOT 0x2e2e
2293+
#endif
2294+
22412295
/*
22422296
* Name resolution.
22432297
* 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)
22662320
for(;;) {
22672321
struct mnt_idmap *idmap;
22682322
const char *link;
2269-
u64 hash_len;
2270-
int type;
2323+
unsigned long lastword;
22712324

22722325
idmap = mnt_idmap(nd->path.mnt);
22732326
err = may_lookup(idmap, nd);
22742327
if (err)
22752328
return err;
22762329

2277-
hash_len = hash_name(nd->path.dentry, name);
2330+
nd->last.name = name;
2331+
name = hash_name(nd, name, &lastword);
22782332

2279-
type = LAST_NORM;
2280-
if (name[0] == '.') switch (hashlen_len(hash_len)) {
2281-
case 2:
2282-
if (name[1] == '.') {
2283-
type = LAST_DOTDOT;
2284-
nd->state |= ND_JUMPED;
2285-
}
2286-
break;
2287-
case 1:
2288-
type = LAST_DOT;
2289-
}
2290-
if (likely(type == LAST_NORM)) {
2291-
struct dentry *parent = nd->path.dentry;
2333+
switch(lastword) {
2334+
case LAST_WORD_IS_DOTDOT:
2335+
nd->last_type = LAST_DOTDOT;
2336+
nd->state |= ND_JUMPED;
2337+
break;
2338+
2339+
case LAST_WORD_IS_DOT:
2340+
nd->last_type = LAST_DOT;
2341+
break;
2342+
2343+
default:
2344+
nd->last_type = LAST_NORM;
22922345
nd->state &= ~ND_JUMPED;
2346+
2347+
struct dentry *parent = nd->path.dentry;
22932348
if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
2294-
struct qstr this = { { .hash_len = hash_len }, .name = name };
2295-
err = parent->d_op->d_hash(parent, &this);
2349+
err = parent->d_op->d_hash(parent, &nd->last);
22962350
if (err < 0)
22972351
return err;
2298-
hash_len = this.hash_len;
2299-
name = this.name;
23002352
}
23012353
}
23022354

2303-
nd->last.hash_len = hash_len;
2304-
nd->last.name = name;
2305-
nd->last_type = type;
2306-
2307-
name += hashlen_len(hash_len);
23082355
if (!*name)
23092356
goto OK;
23102357
/*

0 commit comments

Comments
 (0)