Skip to content

Commit fcfd2fb

Browse files
author
George Spelvin
committed
fs/namei.c: Add hashlen_string() function
We'd like to make more use of the highly-optimized dcache hash functions throughout the kernel, rather than have every subsystem create its own, and a function that hashes basic null-terminated strings is required for that. (The name is to emphasize that it returns both hash and length.) It's actually useful in the dcache itself, specifically d_alloc_name(). Other uses in the next patch. full_name_hash() is also tweaked to make it more generally useful: 1) Take a "char *" rather than "unsigned char *" argument, to be consistent with hash_name(). 2) Handle zero-length inputs. If we want more callers, we don't want to make them worry about corner cases. Signed-off-by: George Spelvin <[email protected]>
1 parent f4bcbe7 commit fcfd2fb

File tree

3 files changed

+53
-9
lines changed

3 files changed

+53
-9
lines changed

fs/dcache.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,8 +1653,7 @@ struct dentry *d_alloc_name(struct dentry *parent, const char *name)
16531653
struct qstr q;
16541654

16551655
q.name = name;
1656-
q.len = strlen(name);
1657-
q.hash = full_name_hash(q.name, q.len);
1656+
q.hash_len = hashlen_string(name);
16581657
return d_alloc(parent, &q);
16591658
}
16601659
EXPORT_SYMBOL(d_alloc_name);

fs/namei.c

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1822,26 +1822,50 @@ static inline unsigned long mix_hash(unsigned long hash)
18221822

18231823
#endif
18241824

1825-
unsigned int full_name_hash(const unsigned char *name, unsigned int len)
1825+
/* Return the hash of a string of known length */
1826+
unsigned int full_name_hash(const char *name, unsigned int len)
18261827
{
18271828
unsigned long a, hash = 0;
18281829

18291830
for (;;) {
1831+
if (!len)
1832+
goto done;
18301833
a = load_unaligned_zeropad(name);
18311834
if (len < sizeof(unsigned long))
18321835
break;
18331836
hash = mix_hash(hash + a);
18341837
name += sizeof(unsigned long);
18351838
len -= sizeof(unsigned long);
1836-
if (!len)
1837-
goto done;
18381839
}
18391840
hash += a & bytemask_from_count(len);
18401841
done:
18411842
return fold_hash(hash);
18421843
}
18431844
EXPORT_SYMBOL(full_name_hash);
18441845

1846+
/* Return the "hash_len" (hash and length) of a null-terminated string */
1847+
u64 hashlen_string(const char *name)
1848+
{
1849+
unsigned long a, adata, mask, hash, len;
1850+
const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
1851+
1852+
hash = a = 0;
1853+
len = -sizeof(unsigned long);
1854+
do {
1855+
hash = mix_hash(hash + a);
1856+
len += sizeof(unsigned long);
1857+
a = load_unaligned_zeropad(name+len);
1858+
} while (!has_zero(a, &adata, &constants));
1859+
1860+
adata = prep_zero_mask(a, adata, &constants);
1861+
mask = create_zero_mask(adata);
1862+
hash += a & zero_bytemask(mask);
1863+
len += find_zero(mask);
1864+
1865+
return hashlen_create(fold_hash(hash), len);
1866+
}
1867+
EXPORT_SYMBOL(hashlen_string);
1868+
18451869
/*
18461870
* Calculate the length and hash of the path component, and
18471871
* return the "hash_len" as the result.
@@ -1872,15 +1896,32 @@ static inline u64 hash_name(const char *name)
18721896

18731897
#else
18741898

1875-
unsigned int full_name_hash(const unsigned char *name, unsigned int len)
1899+
/* Return the hash of a string of known length */
1900+
unsigned int full_name_hash(const char *name, unsigned int len)
18761901
{
18771902
unsigned long hash = init_name_hash();
18781903
while (len--)
1879-
hash = partial_name_hash(*name++, hash);
1904+
hash = partial_name_hash((unsigned char)*name++, hash);
18801905
return end_name_hash(hash);
18811906
}
18821907
EXPORT_SYMBOL(full_name_hash);
18831908

1909+
/* Return the "hash_len" (hash and length) of a null-terminated string */
1910+
u64 hash_string(const char *name)
1911+
{
1912+
unsigned long hash = init_name_hash();
1913+
unsigned long len = 0, c;
1914+
1915+
c = (unsigned char)*name;
1916+
do {
1917+
len++;
1918+
hash = partial_name_hash(c, hash);
1919+
c = (unsigned char)name[len];
1920+
} while (c);
1921+
return hashlen_create(end_name_hash(hash), len);
1922+
}
1923+
EXPORT_SYMBOL(hash_string);
1924+
18841925
/*
18851926
* We know there's a real path component here of at least
18861927
* one character.

include/linux/stringhash.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#ifndef __LINUX_STRINGHASH_H
22
#define __LINUX_STRINGHASH_H
33

4-
#include <linux/types.h>
4+
#include <linux/compiler.h> /* For __pure */
5+
#include <linux/types.h> /* For u32, u64 */
56

67
/*
78
* Routines for hashing strings of bytes to a 32-bit hash value.
@@ -59,7 +60,7 @@ static inline unsigned long end_name_hash(unsigned long hash)
5960
*
6061
* If not set, this falls back to a wrapper around the preceding.
6162
*/
62-
extern unsigned int full_name_hash(const unsigned char *, unsigned int);
63+
extern unsigned int __pure full_name_hash(const char *, unsigned int);
6364

6465
/*
6566
* A hash_len is a u64 with the hash of a string in the low
@@ -69,4 +70,7 @@ extern unsigned int full_name_hash(const unsigned char *, unsigned int);
6970
#define hashlen_len(hashlen) ((u32)((hashlen) >> 32))
7071
#define hashlen_create(hash, len) ((u64)(len)<<32 | (u32)(hash))
7172

73+
/* Return the "hash_len" (hash and length) of a null-terminated string */
74+
extern u64 __pure hashlen_string(const char *name);
75+
7276
#endif /* __LINUX_STRINGHASH_H */

0 commit comments

Comments
 (0)