diff options
author | George Spelvin <linux@sciencehorizons.net> | 2016-05-20 08:41:37 -0400 |
---|---|---|
committer | George Spelvin <linux@sciencehorizons.net> | 2016-05-28 15:42:50 -0400 |
commit | fcfd2fbf22d2587196890103d41e3d554c47da0e (patch) | |
tree | d8e67346881f72e06782c08305b88c8ce167b698 | |
parent | f4bcbe792b8f434e32487cff9d9e30ab45a3ce02 (diff) |
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 <linux@sciencehorizons.net>
-rw-r--r-- | fs/dcache.c | 3 | ||||
-rw-r--r-- | fs/namei.c | 51 | ||||
-rw-r--r-- | include/linux/stringhash.h | 8 |
3 files changed, 53 insertions, 9 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index d5ecc6e477da..19b751806789 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -1653,8 +1653,7 @@ struct dentry *d_alloc_name(struct dentry *parent, const char *name) | |||
1653 | struct qstr q; | 1653 | struct qstr q; |
1654 | 1654 | ||
1655 | q.name = name; | 1655 | q.name = name; |
1656 | q.len = strlen(name); | 1656 | q.hash_len = hashlen_string(name); |
1657 | q.hash = full_name_hash(q.name, q.len); | ||
1658 | return d_alloc(parent, &q); | 1657 | return d_alloc(parent, &q); |
1659 | } | 1658 | } |
1660 | EXPORT_SYMBOL(d_alloc_name); | 1659 | EXPORT_SYMBOL(d_alloc_name); |
diff --git a/fs/namei.c b/fs/namei.c index 42f8ca038254..dd98d43a54f8 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -1822,19 +1822,20 @@ static inline unsigned long mix_hash(unsigned long hash) | |||
1822 | 1822 | ||
1823 | #endif | 1823 | #endif |
1824 | 1824 | ||
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) | ||
1826 | { | 1827 | { |
1827 | unsigned long a, hash = 0; | 1828 | unsigned long a, hash = 0; |
1828 | 1829 | ||
1829 | for (;;) { | 1830 | for (;;) { |
1831 | if (!len) | ||
1832 | goto done; | ||
1830 | a = load_unaligned_zeropad(name); | 1833 | a = load_unaligned_zeropad(name); |
1831 | if (len < sizeof(unsigned long)) | 1834 | if (len < sizeof(unsigned long)) |
1832 | break; | 1835 | break; |
1833 | hash = mix_hash(hash + a); | 1836 | hash = mix_hash(hash + a); |
1834 | name += sizeof(unsigned long); | 1837 | name += sizeof(unsigned long); |
1835 | len -= sizeof(unsigned long); | 1838 | len -= sizeof(unsigned long); |
1836 | if (!len) | ||
1837 | goto done; | ||
1838 | } | 1839 | } |
1839 | hash += a & bytemask_from_count(len); | 1840 | hash += a & bytemask_from_count(len); |
1840 | done: | 1841 | done: |
@@ -1842,6 +1843,29 @@ done: | |||
1842 | } | 1843 | } |
1843 | EXPORT_SYMBOL(full_name_hash); | 1844 | EXPORT_SYMBOL(full_name_hash); |
1844 | 1845 | ||
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 | |||
1845 | /* | 1869 | /* |
1846 | * Calculate the length and hash of the path component, and | 1870 | * Calculate the length and hash of the path component, and |
1847 | * return the "hash_len" as the result. | 1871 | * return the "hash_len" as the result. |
@@ -1872,15 +1896,32 @@ static inline u64 hash_name(const char *name) | |||
1872 | 1896 | ||
1873 | #else | 1897 | #else |
1874 | 1898 | ||
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) | ||
1876 | { | 1901 | { |
1877 | unsigned long hash = init_name_hash(); | 1902 | unsigned long hash = init_name_hash(); |
1878 | while (len--) | 1903 | while (len--) |
1879 | hash = partial_name_hash(*name++, hash); | 1904 | hash = partial_name_hash((unsigned char)*name++, hash); |
1880 | return end_name_hash(hash); | 1905 | return end_name_hash(hash); |
1881 | } | 1906 | } |
1882 | EXPORT_SYMBOL(full_name_hash); | 1907 | EXPORT_SYMBOL(full_name_hash); |
1883 | 1908 | ||
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 | |||
1884 | /* | 1925 | /* |
1885 | * We know there's a real path component here of at least | 1926 | * We know there's a real path component here of at least |
1886 | * one character. | 1927 | * one character. |
diff --git a/include/linux/stringhash.h b/include/linux/stringhash.h index 2eaaaf6d2776..451771d9b9c0 100644 --- a/include/linux/stringhash.h +++ b/include/linux/stringhash.h | |||
@@ -1,7 +1,8 @@ | |||
1 | #ifndef __LINUX_STRINGHASH_H | 1 | #ifndef __LINUX_STRINGHASH_H |
2 | #define __LINUX_STRINGHASH_H | 2 | #define __LINUX_STRINGHASH_H |
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/compiler.h> /* For __pure */ |
5 | #include <linux/types.h> /* For u32, u64 */ | ||
5 | 6 | ||
6 | /* | 7 | /* |
7 | * Routines for hashing strings of bytes to a 32-bit hash value. | 8 | * 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) | |||
59 | * | 60 | * |
60 | * If not set, this falls back to a wrapper around the preceding. | 61 | * If not set, this falls back to a wrapper around the preceding. |
61 | */ | 62 | */ |
62 | extern unsigned int full_name_hash(const unsigned char *, unsigned int); | 63 | extern unsigned int __pure full_name_hash(const char *, unsigned int); |
63 | 64 | ||
64 | /* | 65 | /* |
65 | * A hash_len is a u64 with the hash of a string in the low | 66 | * 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); | |||
69 | #define hashlen_len(hashlen) ((u32)((hashlen) >> 32)) | 70 | #define hashlen_len(hashlen) ((u32)((hashlen) >> 32)) |
70 | #define hashlen_create(hash, len) ((u64)(len)<<32 | (u32)(hash)) | 71 | #define hashlen_create(hash, len) ((u64)(len)<<32 | (u32)(hash)) |
71 | 72 | ||
73 | /* Return the "hash_len" (hash and length) of a null-terminated string */ | ||
74 | extern u64 __pure hashlen_string(const char *name); | ||
75 | |||
72 | #endif /* __LINUX_STRINGHASH_H */ | 76 | #endif /* __LINUX_STRINGHASH_H */ |