11e1236b8SDavid Howells // SPDX-License-Identifier: GPL-2.0-or-later
21e1236b8SDavid Howells /* General filesystem local caching manager
31e1236b8SDavid Howells *
41e1236b8SDavid Howells * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
51e1236b8SDavid Howells * Written by David Howells (dhowells@redhat.com)
61e1236b8SDavid Howells */
71e1236b8SDavid Howells
81e1236b8SDavid Howells #define FSCACHE_DEBUG_LEVEL CACHE
91e1236b8SDavid Howells #include <linux/module.h>
101e1236b8SDavid Howells #include <linux/init.h>
111e1236b8SDavid Howells #define CREATE_TRACE_POINTS
121e1236b8SDavid Howells #include "internal.h"
131e1236b8SDavid Howells
141e1236b8SDavid Howells MODULE_DESCRIPTION("FS Cache Manager");
151e1236b8SDavid Howells MODULE_AUTHOR("Red Hat, Inc.");
161e1236b8SDavid Howells MODULE_LICENSE("GPL");
171e1236b8SDavid Howells
181e1236b8SDavid Howells unsigned fscache_debug;
191e1236b8SDavid Howells module_param_named(debug, fscache_debug, uint,
201e1236b8SDavid Howells S_IWUSR | S_IRUGO);
211e1236b8SDavid Howells MODULE_PARM_DESC(fscache_debug,
221e1236b8SDavid Howells "FS-Cache debugging mask");
231e1236b8SDavid Howells
2423e12e28SDavid Howells EXPORT_TRACEPOINT_SYMBOL(fscache_access_cache);
25e6acd329SDavid Howells EXPORT_TRACEPOINT_SYMBOL(fscache_access_volume);
26a7733fb6SDavid Howells EXPORT_TRACEPOINT_SYMBOL(fscache_access);
2723e12e28SDavid Howells
281e1236b8SDavid Howells struct workqueue_struct *fscache_wq;
291e1236b8SDavid Howells EXPORT_SYMBOL(fscache_wq);
301e1236b8SDavid Howells
311e1236b8SDavid Howells /*
32e8a07c9dSDavid Howells * Mixing scores (in bits) for (7,20):
33e8a07c9dSDavid Howells * Input delta: 1-bit 2-bit
34e8a07c9dSDavid Howells * 1 round: 330.3 9201.6
35e8a07c9dSDavid Howells * 2 rounds: 1246.4 25475.4
36e8a07c9dSDavid Howells * 3 rounds: 1907.1 31295.1
37e8a07c9dSDavid Howells * 4 rounds: 2042.3 31718.6
38e8a07c9dSDavid Howells * Perfect: 2048 31744
39e8a07c9dSDavid Howells * (32*64) (32*31/2 * 64)
40e8a07c9dSDavid Howells */
41e8a07c9dSDavid Howells #define HASH_MIX(x, y, a) \
42e8a07c9dSDavid Howells ( x ^= (a), \
43e8a07c9dSDavid Howells y ^= x, x = rol32(x, 7),\
44e8a07c9dSDavid Howells x += y, y = rol32(y,20),\
45e8a07c9dSDavid Howells y *= 9 )
46e8a07c9dSDavid Howells
fold_hash(unsigned long x,unsigned long y)47e8a07c9dSDavid Howells static inline unsigned int fold_hash(unsigned long x, unsigned long y)
48e8a07c9dSDavid Howells {
49e8a07c9dSDavid Howells /* Use arch-optimized multiply if one exists */
50e8a07c9dSDavid Howells return __hash_32(y ^ __hash_32(x));
51e8a07c9dSDavid Howells }
52e8a07c9dSDavid Howells
53e8a07c9dSDavid Howells /*
54e8a07c9dSDavid Howells * Generate a hash. This is derived from full_name_hash(), but we want to be
55e8a07c9dSDavid Howells * sure it is arch independent and that it doesn't change as bits of the
56e8a07c9dSDavid Howells * computed hash value might appear on disk. The caller must guarantee that
57e8a07c9dSDavid Howells * the source data is a multiple of four bytes in size.
58e8a07c9dSDavid Howells */
fscache_hash(unsigned int salt,const void * data,size_t len)59e8a07c9dSDavid Howells unsigned int fscache_hash(unsigned int salt, const void *data, size_t len)
60e8a07c9dSDavid Howells {
61e8a07c9dSDavid Howells const __le32 *p = data;
62e8a07c9dSDavid Howells unsigned int a, x = 0, y = salt, n = len / sizeof(__le32);
63e8a07c9dSDavid Howells
64e8a07c9dSDavid Howells for (; n; n--) {
65e8a07c9dSDavid Howells a = le32_to_cpu(*p++);
66e8a07c9dSDavid Howells HASH_MIX(x, y, a);
67e8a07c9dSDavid Howells }
68e8a07c9dSDavid Howells return fold_hash(x, y);
69e8a07c9dSDavid Howells }
70e8a07c9dSDavid Howells
71e8a07c9dSDavid Howells /*
721e1236b8SDavid Howells * initialise the fs caching module
731e1236b8SDavid Howells */
fscache_init(void)741e1236b8SDavid Howells static int __init fscache_init(void)
751e1236b8SDavid Howells {
761e1236b8SDavid Howells int ret = -ENOMEM;
771e1236b8SDavid Howells
781e1236b8SDavid Howells fscache_wq = alloc_workqueue("fscache", WQ_UNBOUND | WQ_FREEZABLE, 0);
791e1236b8SDavid Howells if (!fscache_wq)
801e1236b8SDavid Howells goto error_wq;
811e1236b8SDavid Howells
821e1236b8SDavid Howells ret = fscache_proc_init();
831e1236b8SDavid Howells if (ret < 0)
841e1236b8SDavid Howells goto error_proc;
851e1236b8SDavid Howells
867f3283abSDavid Howells fscache_cookie_jar = kmem_cache_create("fscache_cookie_jar",
877f3283abSDavid Howells sizeof(struct fscache_cookie),
887f3283abSDavid Howells 0, 0, NULL);
897f3283abSDavid Howells if (!fscache_cookie_jar) {
907f3283abSDavid Howells pr_notice("Failed to allocate a cookie jar\n");
917f3283abSDavid Howells ret = -ENOMEM;
927f3283abSDavid Howells goto error_cookie_jar;
937f3283abSDavid Howells }
947f3283abSDavid Howells
951e1236b8SDavid Howells pr_notice("Loaded\n");
961e1236b8SDavid Howells return 0;
971e1236b8SDavid Howells
987f3283abSDavid Howells error_cookie_jar:
997f3283abSDavid Howells fscache_proc_cleanup();
1001e1236b8SDavid Howells error_proc:
1011e1236b8SDavid Howells destroy_workqueue(fscache_wq);
1021e1236b8SDavid Howells error_wq:
1031e1236b8SDavid Howells return ret;
1041e1236b8SDavid Howells }
1051e1236b8SDavid Howells
1061e1236b8SDavid Howells fs_initcall(fscache_init);
1071e1236b8SDavid Howells
1081e1236b8SDavid Howells /*
1091e1236b8SDavid Howells * clean up on module removal
1101e1236b8SDavid Howells */
fscache_exit(void)1111e1236b8SDavid Howells static void __exit fscache_exit(void)
1121e1236b8SDavid Howells {
1131e1236b8SDavid Howells _enter("");
1141e1236b8SDavid Howells
1157f3283abSDavid Howells kmem_cache_destroy(fscache_cookie_jar);
1161e1236b8SDavid Howells fscache_proc_cleanup();
117*e0d72493SBaokun Li timer_shutdown_sync(&fscache_cookie_lru_timer);
1181e1236b8SDavid Howells destroy_workqueue(fscache_wq);
1191e1236b8SDavid Howells pr_notice("Unloaded\n");
1201e1236b8SDavid Howells }
1211e1236b8SDavid Howells
1221e1236b8SDavid Howells module_exit(fscache_exit);
123