|  | // SPDX-License-Identifier: GPL-2.0-or-later | 
|  | /* General filesystem local caching manager | 
|  | * | 
|  | * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. | 
|  | * Written by David Howells (dhowells@redhat.com) | 
|  | */ | 
|  |  | 
|  | #define FSCACHE_DEBUG_LEVEL CACHE | 
|  | #include <linux/module.h> | 
|  | #include <linux/init.h> | 
|  | #include "internal.h" | 
|  | #define CREATE_TRACE_POINTS | 
|  | #include <trace/events/fscache.h> | 
|  |  | 
|  | EXPORT_TRACEPOINT_SYMBOL(fscache_access_cache); | 
|  | EXPORT_TRACEPOINT_SYMBOL(fscache_access_volume); | 
|  | EXPORT_TRACEPOINT_SYMBOL(fscache_access); | 
|  |  | 
|  | struct workqueue_struct *fscache_wq; | 
|  | EXPORT_SYMBOL(fscache_wq); | 
|  |  | 
|  | /* | 
|  | * Mixing scores (in bits) for (7,20): | 
|  | * Input delta: 1-bit      2-bit | 
|  | * 1 round:     330.3     9201.6 | 
|  | * 2 rounds:   1246.4    25475.4 | 
|  | * 3 rounds:   1907.1    31295.1 | 
|  | * 4 rounds:   2042.3    31718.6 | 
|  | * Perfect:    2048      31744 | 
|  | *            (32*64)   (32*31/2 * 64) | 
|  | */ | 
|  | #define HASH_MIX(x, y, a)	\ | 
|  | (	x ^= (a),	\ | 
|  | y ^= x,	x = rol32(x, 7),\ | 
|  | x += y,	y = rol32(y,20),\ | 
|  | y *= 9			) | 
|  |  | 
|  | static inline unsigned int fold_hash(unsigned long x, unsigned long y) | 
|  | { | 
|  | /* Use arch-optimized multiply if one exists */ | 
|  | return __hash_32(y ^ __hash_32(x)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Generate a hash.  This is derived from full_name_hash(), but we want to be | 
|  | * sure it is arch independent and that it doesn't change as bits of the | 
|  | * computed hash value might appear on disk.  The caller must guarantee that | 
|  | * the source data is a multiple of four bytes in size. | 
|  | */ | 
|  | unsigned int fscache_hash(unsigned int salt, const void *data, size_t len) | 
|  | { | 
|  | const __le32 *p = data; | 
|  | unsigned int a, x = 0, y = salt, n = len / sizeof(__le32); | 
|  |  | 
|  | for (; n; n--) { | 
|  | a = le32_to_cpu(*p++); | 
|  | HASH_MIX(x, y, a); | 
|  | } | 
|  | return fold_hash(x, y); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * initialise the fs caching module | 
|  | */ | 
|  | int __init fscache_init(void) | 
|  | { | 
|  | int ret = -ENOMEM; | 
|  |  | 
|  | fscache_wq = alloc_workqueue("fscache", WQ_UNBOUND | WQ_FREEZABLE, 0); | 
|  | if (!fscache_wq) | 
|  | goto error_wq; | 
|  |  | 
|  | ret = fscache_proc_init(); | 
|  | if (ret < 0) | 
|  | goto error_proc; | 
|  |  | 
|  | fscache_cookie_jar = kmem_cache_create("fscache_cookie_jar", | 
|  | sizeof(struct fscache_cookie), | 
|  | 0, 0, NULL); | 
|  | if (!fscache_cookie_jar) { | 
|  | pr_notice("Failed to allocate a cookie jar\n"); | 
|  | ret = -ENOMEM; | 
|  | goto error_cookie_jar; | 
|  | } | 
|  |  | 
|  | pr_notice("FS-Cache loaded\n"); | 
|  | return 0; | 
|  |  | 
|  | error_cookie_jar: | 
|  | fscache_proc_cleanup(); | 
|  | error_proc: | 
|  | destroy_workqueue(fscache_wq); | 
|  | error_wq: | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * clean up on module removal | 
|  | */ | 
|  | void __exit fscache_exit(void) | 
|  | { | 
|  | _enter(""); | 
|  |  | 
|  | kmem_cache_destroy(fscache_cookie_jar); | 
|  | fscache_proc_cleanup(); | 
|  | timer_shutdown_sync(&fscache_cookie_lru_timer); | 
|  | destroy_workqueue(fscache_wq); | 
|  | pr_notice("FS-Cache unloaded\n"); | 
|  | } |