aboutsummaryrefslogtreecommitdiffstats
path: root/fs/namei.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-09-14 20:28:32 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-09-14 20:28:32 -0400
commit9226b5b440f2b4fbb3b797f3cb74a9a627220660 (patch)
tree2b9ff475c498e19606031d5e7fde74cdba30dee8 /fs/namei.c
parent5910cfdce307d6e5c55d747809e3c670c9e8a9a7 (diff)
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 99d263d4c5b2 "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 <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/namei.c')
-rw-r--r--fs/namei.c19
1 files changed, 10 insertions, 9 deletions
diff --git a/fs/namei.c b/fs/namei.c
index 229235862e50..2be5120b81b3 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1669,13 +1669,14 @@ EXPORT_SYMBOL(full_name_hash);
1669 1669
1670/* 1670/*
1671 * Calculate the length and hash of the path component, and 1671 * 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.
1673 */ 1673 */
1674static inline unsigned long hash_name(const char *name, unsigned int *hashp) 1674static inline unsigned long hash_name(const char *name, struct qstr *res)
1675{ 1675{
1676 unsigned long a, b, adata, bdata, mask, hash, len; 1676 unsigned long a, b, adata, bdata, mask, hash, len;
1677 const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; 1677 const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
1678 1678
1679 res->name = name;
1679 hash = a = 0; 1680 hash = a = 0;
1680 len = -sizeof(unsigned long); 1681 len = -sizeof(unsigned long);
1681 do { 1682 do {
@@ -1691,9 +1692,10 @@ static inline unsigned long hash_name(const char *name, unsigned int *hashp)
1691 mask = create_zero_mask(adata | bdata); 1692 mask = create_zero_mask(adata | bdata);
1692 1693
1693 hash += a & zero_bytemask(mask); 1694 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);
1695 1697
1696 return len + find_zero(mask); 1698 return len;
1697} 1699}
1698 1700
1699#else 1701#else
@@ -1711,18 +1713,19 @@ EXPORT_SYMBOL(full_name_hash);
1711 * We know there's a real path component here of at least 1713 * We know there's a real path component here of at least
1712 * one character. 1714 * one character.
1713 */ 1715 */
1714static inline unsigned long hash_name(const char *name, unsigned int *hashp) 1716static inline long hash_name(const char *name, struct qstr *res)
1715{ 1717{
1716 unsigned long hash = init_name_hash(); 1718 unsigned long hash = init_name_hash();
1717 unsigned long len = 0, c; 1719 unsigned long len = 0, c;
1718 1720
1721 res->name = name;
1719 c = (unsigned char)*name; 1722 c = (unsigned char)*name;
1720 do { 1723 do {
1721 len++; 1724 len++;
1722 hash = partial_name_hash(c, hash); 1725 hash = partial_name_hash(c, hash);
1723 c = (unsigned char)name[len]; 1726 c = (unsigned char)name[len];
1724 } while (c && c != '/'); 1727 } while (c && c != '/');
1725 *hashp = end_name_hash(hash); 1728 res->hash_len = hashlen_create(end_name_hash(hash), len);
1726 return len; 1729 return len;
1727} 1730}
1728 1731
@@ -1756,9 +1759,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
1756 if (err) 1759 if (err)
1757 break; 1760 break;
1758 1761
1759 len = hash_name(name, &this.hash); 1762 len = hash_name(name, &this);
1760 this.name = name;
1761 this.len = len;
1762 1763
1763 type = LAST_NORM; 1764 type = LAST_NORM;
1764 if (name[0] == '.') switch (len) { 1765 if (name[0] == '.') switch (len) {