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 */ |
