162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* General filesystem local caching manager
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define FSCACHE_DEBUG_LEVEL CACHE
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
1262306a36Sopenharmony_ci#include "internal.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ciMODULE_DESCRIPTION("FS Cache Manager");
1562306a36Sopenharmony_ciMODULE_AUTHOR("Red Hat, Inc.");
1662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciunsigned fscache_debug;
1962306a36Sopenharmony_cimodule_param_named(debug, fscache_debug, uint,
2062306a36Sopenharmony_ci		   S_IWUSR | S_IRUGO);
2162306a36Sopenharmony_ciMODULE_PARM_DESC(fscache_debug,
2262306a36Sopenharmony_ci		 "FS-Cache debugging mask");
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL(fscache_access_cache);
2562306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL(fscache_access_volume);
2662306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL(fscache_access);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct workqueue_struct *fscache_wq;
2962306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_wq);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/*
3262306a36Sopenharmony_ci * Mixing scores (in bits) for (7,20):
3362306a36Sopenharmony_ci * Input delta: 1-bit      2-bit
3462306a36Sopenharmony_ci * 1 round:     330.3     9201.6
3562306a36Sopenharmony_ci * 2 rounds:   1246.4    25475.4
3662306a36Sopenharmony_ci * 3 rounds:   1907.1    31295.1
3762306a36Sopenharmony_ci * 4 rounds:   2042.3    31718.6
3862306a36Sopenharmony_ci * Perfect:    2048      31744
3962306a36Sopenharmony_ci *            (32*64)   (32*31/2 * 64)
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_ci#define HASH_MIX(x, y, a)	\
4262306a36Sopenharmony_ci	(	x ^= (a),	\
4362306a36Sopenharmony_ci	y ^= x,	x = rol32(x, 7),\
4462306a36Sopenharmony_ci	x += y,	y = rol32(y,20),\
4562306a36Sopenharmony_ci	y *= 9			)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic inline unsigned int fold_hash(unsigned long x, unsigned long y)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	/* Use arch-optimized multiply if one exists */
5062306a36Sopenharmony_ci	return __hash_32(y ^ __hash_32(x));
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/*
5462306a36Sopenharmony_ci * Generate a hash.  This is derived from full_name_hash(), but we want to be
5562306a36Sopenharmony_ci * sure it is arch independent and that it doesn't change as bits of the
5662306a36Sopenharmony_ci * computed hash value might appear on disk.  The caller must guarantee that
5762306a36Sopenharmony_ci * the source data is a multiple of four bytes in size.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ciunsigned int fscache_hash(unsigned int salt, const void *data, size_t len)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	const __le32 *p = data;
6262306a36Sopenharmony_ci	unsigned int a, x = 0, y = salt, n = len / sizeof(__le32);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	for (; n; n--) {
6562306a36Sopenharmony_ci		a = le32_to_cpu(*p++);
6662306a36Sopenharmony_ci		HASH_MIX(x, y, a);
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci	return fold_hash(x, y);
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/*
7262306a36Sopenharmony_ci * initialise the fs caching module
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cistatic int __init fscache_init(void)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int ret = -ENOMEM;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	fscache_wq = alloc_workqueue("fscache", WQ_UNBOUND | WQ_FREEZABLE, 0);
7962306a36Sopenharmony_ci	if (!fscache_wq)
8062306a36Sopenharmony_ci		goto error_wq;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	ret = fscache_proc_init();
8362306a36Sopenharmony_ci	if (ret < 0)
8462306a36Sopenharmony_ci		goto error_proc;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	fscache_cookie_jar = kmem_cache_create("fscache_cookie_jar",
8762306a36Sopenharmony_ci					       sizeof(struct fscache_cookie),
8862306a36Sopenharmony_ci					       0, 0, NULL);
8962306a36Sopenharmony_ci	if (!fscache_cookie_jar) {
9062306a36Sopenharmony_ci		pr_notice("Failed to allocate a cookie jar\n");
9162306a36Sopenharmony_ci		ret = -ENOMEM;
9262306a36Sopenharmony_ci		goto error_cookie_jar;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	pr_notice("Loaded\n");
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cierror_cookie_jar:
9962306a36Sopenharmony_ci	fscache_proc_cleanup();
10062306a36Sopenharmony_cierror_proc:
10162306a36Sopenharmony_ci	destroy_workqueue(fscache_wq);
10262306a36Sopenharmony_cierror_wq:
10362306a36Sopenharmony_ci	return ret;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cifs_initcall(fscache_init);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ci * clean up on module removal
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistatic void __exit fscache_exit(void)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	_enter("");
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	kmem_cache_destroy(fscache_cookie_jar);
11662306a36Sopenharmony_ci	fscache_proc_cleanup();
11762306a36Sopenharmony_ci	destroy_workqueue(fscache_wq);
11862306a36Sopenharmony_ci	pr_notice("Unloaded\n");
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cimodule_exit(fscache_exit);
122