Skip to content

Commit 9226b5b

Browse files
committed
vfs: avoid non-forwarding large load after small store in path lookup
The performance regression that Josef Bacik reported in the pathname lookup (see commit 99d263d "vfs: fix bad hashing of dentries") made me look at performance stability of the dcache code, just to verify that the problem was actually fixed. That turned up a few other problems in this area. There are a few cases where we exit RCU lookup mode and go to the slow serializing case when we shouldn't, Al has fixed those and they'll come in with the next VFS pull. But my performance verification also shows that link_path_walk() turns out to have a very unfortunate 32-bit store of the length and hash of the name we look up, followed by a 64-bit read of the combined hash_len field. That screws up the processor store to load forwarding, causing an unnecessary hickup in this critical routine. It's caused by the ugly calling convention for the "hash_name()" function, and easily fixed by just making hash_name() fill in the whole 'struct qstr' rather than passing it a pointer to just the hash value. With that, the profile for this function looks much smoother. Signed-off-by: Linus Torvalds <[email protected]>
1 parent 5910cfd commit 9226b5b

File tree

2 files changed

+11
-9
lines changed

2 files changed

+11
-9
lines changed

fs/namei.c

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,13 +1669,14 @@ EXPORT_SYMBOL(full_name_hash);
16691669

16701670
/*
16711671
* Calculate the length and hash of the path component, and
1672-
* return the length of the component;
1672+
* fill in the qstr. return the "len" as the result.
16731673
*/
1674-
static inline unsigned long hash_name(const char *name, unsigned int *hashp)
1674+
static inline unsigned long hash_name(const char *name, struct qstr *res)
16751675
{
16761676
unsigned long a, b, adata, bdata, mask, hash, len;
16771677
const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
16781678

1679+
res->name = name;
16791680
hash = a = 0;
16801681
len = -sizeof(unsigned long);
16811682
do {
@@ -1691,9 +1692,10 @@ static inline unsigned long hash_name(const char *name, unsigned int *hashp)
16911692
mask = create_zero_mask(adata | bdata);
16921693

16931694
hash += a & zero_bytemask(mask);
1694-
*hashp = fold_hash(hash);
1695+
len += find_zero(mask);
1696+
res->hash_len = hashlen_create(fold_hash(hash), len);
16951697

1696-
return len + find_zero(mask);
1698+
return len;
16971699
}
16981700

16991701
#else
@@ -1711,18 +1713,19 @@ EXPORT_SYMBOL(full_name_hash);
17111713
* We know there's a real path component here of at least
17121714
* one character.
17131715
*/
1714-
static inline unsigned long hash_name(const char *name, unsigned int *hashp)
1716+
static inline long hash_name(const char *name, struct qstr *res)
17151717
{
17161718
unsigned long hash = init_name_hash();
17171719
unsigned long len = 0, c;
17181720

1721+
res->name = name;
17191722
c = (unsigned char)*name;
17201723
do {
17211724
len++;
17221725
hash = partial_name_hash(c, hash);
17231726
c = (unsigned char)name[len];
17241727
} while (c && c != '/');
1725-
*hashp = end_name_hash(hash);
1728+
res->hash_len = hashlen_create(end_name_hash(hash), len);
17261729
return len;
17271730
}
17281731

@@ -1756,9 +1759,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
17561759
if (err)
17571760
break;
17581761

1759-
len = hash_name(name, &this.hash);
1760-
this.name = name;
1761-
this.len = len;
1762+
len = hash_name(name, &this);
17621763

17631764
type = LAST_NORM;
17641765
if (name[0] == '.') switch (len) {

include/linux/dcache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct qstr {
5555
#define QSTR_INIT(n,l) { { { .len = l } }, .name = n }
5656
#define hashlen_hash(hashlen) ((u32) (hashlen))
5757
#define hashlen_len(hashlen) ((u32)((hashlen) >> 32))
58+
#define hashlen_create(hash,len) (((u64)(len)<<32)|(u32)(hash))
5859

5960
struct dentry_stat_t {
6061
long nr_dentry;

0 commit comments

Comments
 (0)