diff options
author | George Spelvin | 2016-05-20 08:41:37 -0400 |
---|---|---|
committer | George Spelvin | 2016-05-28 15:42:50 -0400 |
commit | fcfd2fbf22d2587196890103d41e3d554c47da0e (patch) | |
tree | d8e67346881f72e06782c08305b88c8ce167b698 /fs | |
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/dcache.c | 3 | ||||
-rw-r--r-- | fs/namei.c | 51 |
2 files changed, 47 insertions, 7 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) struct qstr q; q.name = name; - q.len = strlen(name); - q.hash = full_name_hash(q.name, q.len); + q.hash_len = hashlen_string(name); return d_alloc(parent, &q); } 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) #endif -unsigned int full_name_hash(const unsigned char *name, unsigned int len) +/* Return the hash of a string of known length */ +unsigned int full_name_hash(const char *name, unsigned int len) { unsigned long a, hash = 0; for (;;) { + if (!len) + goto done; a = load_unaligned_zeropad(name); if (len < sizeof(unsigned long)) break; hash = mix_hash(hash + a); name += sizeof(unsigned long); len -= sizeof(unsigned long); - if (!len) - goto done; } hash += a & bytemask_from_count(len); done: @@ -1842,6 +1843,29 @@ done: } EXPORT_SYMBOL(full_name_hash); +/* Return the "hash_len" (hash and length) of a null-terminated string */ +u64 hashlen_string(const char *name) +{ + unsigned long a, adata, mask, hash, len; + const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; + + hash = a = 0; + len = -sizeof(unsigned long); + do { + hash = mix_hash(hash + a); + len += sizeof(unsigned long); + a = load_unaligned_zeropad(name+len); + } while (!has_zero(a, &adata, &constants)); + + adata = prep_zero_mask(a, adata, &constants); + mask = create_zero_mask(adata); + hash += a & zero_bytemask(mask); + len += find_zero(mask); + + return hashlen_create(fold_hash(hash), len); +} +EXPORT_SYMBOL(hashlen_string); + /* * Calculate the length and hash of the path component, and * return the "hash_len" as the result. @@ -1872,15 +1896,32 @@ static inline u64 hash_name(const char *name) #else -unsigned int full_name_hash(const unsigned char *name, unsigned int len) +/* Return the hash of a string of known length */ +unsigned int full_name_hash(const char *name, unsigned int len) { unsigned long hash = init_name_hash(); while (len--) - hash = partial_name_hash(*name++, hash); + hash = partial_name_hash((unsigned char)*name++, hash); return end_name_hash(hash); } EXPORT_SYMBOL(full_name_hash); +/* Return the "hash_len" (hash and length) of a null-terminated string */ +u64 hash_string(const char *name) +{ + unsigned long hash = init_name_hash(); + unsigned long len = 0, c; + + c = (unsigned char)*name; + do { + len++; + hash = partial_name_hash(c, hash); + c = (unsigned char)name[len]; + } while (c); + return hashlen_create(end_name_hash(hash), len); +} +EXPORT_SYMBOL(hash_string); + /* * We know there's a real path component here of at least * one character. |