2021-10-20 21:34:41 +08:00
|
|
|
// 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>
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("FS Cache Manager");
|
|
|
|
MODULE_AUTHOR("Red Hat, Inc.");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
unsigned fscache_debug;
|
|
|
|
module_param_named(debug, fscache_debug, uint,
|
|
|
|
S_IWUSR | S_IRUGO);
|
|
|
|
MODULE_PARM_DESC(fscache_debug,
|
|
|
|
"FS-Cache debugging mask");
|
|
|
|
|
2021-10-20 22:00:26 +08:00
|
|
|
EXPORT_TRACEPOINT_SYMBOL(fscache_access_cache);
|
fscache: Implement volume-level access helpers
Add a pair of helper functions to manage access to a volume, pinning the
volume in place for the duration to prevent cache withdrawal from removing
it:
bool fscache_begin_volume_access(struct fscache_volume *volume,
enum fscache_access_trace why);
void fscache_end_volume_access(struct fscache_volume *volume,
enum fscache_access_trace why);
The way the access gate on the volume works/will work is:
(1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTIVE),
then we return false to indicate access was not permitted.
(2) If the cache tests as live, then we increment the volume's n_accesses
count and then recheck the cache liveness, ending the access if it
ceased to be live.
(3) When we end the access, we decrement the volume's n_accesses and wake
up the any waiters if it reaches 0.
(4) Whilst the cache is caching, the volume's n_accesses is kept
artificially incremented to prevent wakeups from happening.
(5) When the cache is taken offline, the state is changed to prevent new
accesses, the volume's n_accesses is decremented and we wait for it to
become 0.
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
cc: linux-cachefs@redhat.com
Link: https://lore.kernel.org/r/163819594158.215744.8285859817391683254.stgit@warthog.procyon.org.uk/ # v1
Link: https://lore.kernel.org/r/163906894315.143852.5454793807544710479.stgit@warthog.procyon.org.uk/ # v2
Link: https://lore.kernel.org/r/163967095028.1823006.9173132503876627466.stgit@warthog.procyon.org.uk/ # v3
Link: https://lore.kernel.org/r/164021501546.640689.9631510472149608443.stgit@warthog.procyon.org.uk/ # v4
2021-10-20 22:26:17 +08:00
|
|
|
EXPORT_TRACEPOINT_SYMBOL(fscache_access_volume);
|
2021-10-20 22:53:34 +08:00
|
|
|
EXPORT_TRACEPOINT_SYMBOL(fscache_access);
|
2021-10-20 22:00:26 +08:00
|
|
|
|
2021-10-20 21:34:41 +08:00
|
|
|
struct workqueue_struct *fscache_wq;
|
|
|
|
EXPORT_SYMBOL(fscache_wq);
|
|
|
|
|
2021-10-20 22:45:28 +08:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2021-10-20 21:34:41 +08:00
|
|
|
/*
|
|
|
|
* initialise the fs caching module
|
|
|
|
*/
|
|
|
|
static 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: Implement cookie registration
Add functions to the fscache API to allow data file cookies to be acquired
and relinquished by the network filesystem. It is intended that the
filesystem will create such cookies per-inode under a volume.
To request a cookie, the filesystem should call:
struct fscache_cookie *
fscache_acquire_cookie(struct fscache_volume *volume,
u8 advice,
const void *index_key,
size_t index_key_len,
const void *aux_data,
size_t aux_data_len,
loff_t object_size)
The filesystem must first have created a volume cookie, which is passed in
here. If it passes in NULL then the function will just return a NULL
cookie.
A binary key should be passed in index_key and is of size index_key_len.
This is saved in the cookie and is used to locate the associated data in
the cache.
A coherency data buffer of size aux_data_len will be allocated and
initialised from the buffer pointed to by aux_data. This is used to
validate cache objects when they're opened and is stored on disk with them
when they're committed. The data is stored in the cookie and will be
updateable by various functions in later patches.
The object_size must also be given. This is also used to perform a
coherency check and to size the backing storage appropriately.
This function disallows a cookie from being acquired twice in parallel,
though it will cause the second user to wait if the first is busy
relinquishing its cookie.
When a network filesystem has finished with a cookie, it should call:
void
fscache_relinquish_cookie(struct fscache_volume *volume,
bool retire)
If retire is true, any backing data will be discarded immediately.
Changes
=======
ver #3:
- fscache_hash()'s size parameter is now in bytes. Use __le32 as the unit
to round up to.
- When comparing cookies, simply see if the attributes are the same rather
than subtracting them to produce a strcmp-style return[1].
- Add a check to see if the cookie is still hashed at the point of
freeing.
ver #2:
- Don't hold n_accesses elevated whilst cache is bound to a cookie, but
rather add a flag that prevents the state machine from being queued when
n_accesses reaches 0.
- Remove the unused cookie pointer field from the fscache_acquire
tracepoint.
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
cc: linux-cachefs@redhat.com
Link: https://lore.kernel.org/r/CAHk-=whtkzB446+hX0zdLsdcUJsJ=8_-0S1mE_R+YurThfUbLA@mail.gmail.com/ [1]
Link: https://lore.kernel.org/r/163819590658.215744.14934902514281054323.stgit@warthog.procyon.org.uk/ # v1
Link: https://lore.kernel.org/r/163906891983.143852.6219772337558577395.stgit@warthog.procyon.org.uk/ # v2
Link: https://lore.kernel.org/r/163967088507.1823006.12659006350221417165.stgit@warthog.procyon.org.uk/ # v3
Link: https://lore.kernel.org/r/164021498432.640689.12743483856927722772.stgit@warthog.procyon.org.uk/ # v4
2021-10-20 22:53:34 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-10-20 21:34:41 +08:00
|
|
|
pr_notice("Loaded\n");
|
|
|
|
return 0;
|
|
|
|
|
fscache: Implement cookie registration
Add functions to the fscache API to allow data file cookies to be acquired
and relinquished by the network filesystem. It is intended that the
filesystem will create such cookies per-inode under a volume.
To request a cookie, the filesystem should call:
struct fscache_cookie *
fscache_acquire_cookie(struct fscache_volume *volume,
u8 advice,
const void *index_key,
size_t index_key_len,
const void *aux_data,
size_t aux_data_len,
loff_t object_size)
The filesystem must first have created a volume cookie, which is passed in
here. If it passes in NULL then the function will just return a NULL
cookie.
A binary key should be passed in index_key and is of size index_key_len.
This is saved in the cookie and is used to locate the associated data in
the cache.
A coherency data buffer of size aux_data_len will be allocated and
initialised from the buffer pointed to by aux_data. This is used to
validate cache objects when they're opened and is stored on disk with them
when they're committed. The data is stored in the cookie and will be
updateable by various functions in later patches.
The object_size must also be given. This is also used to perform a
coherency check and to size the backing storage appropriately.
This function disallows a cookie from being acquired twice in parallel,
though it will cause the second user to wait if the first is busy
relinquishing its cookie.
When a network filesystem has finished with a cookie, it should call:
void
fscache_relinquish_cookie(struct fscache_volume *volume,
bool retire)
If retire is true, any backing data will be discarded immediately.
Changes
=======
ver #3:
- fscache_hash()'s size parameter is now in bytes. Use __le32 as the unit
to round up to.
- When comparing cookies, simply see if the attributes are the same rather
than subtracting them to produce a strcmp-style return[1].
- Add a check to see if the cookie is still hashed at the point of
freeing.
ver #2:
- Don't hold n_accesses elevated whilst cache is bound to a cookie, but
rather add a flag that prevents the state machine from being queued when
n_accesses reaches 0.
- Remove the unused cookie pointer field from the fscache_acquire
tracepoint.
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
cc: linux-cachefs@redhat.com
Link: https://lore.kernel.org/r/CAHk-=whtkzB446+hX0zdLsdcUJsJ=8_-0S1mE_R+YurThfUbLA@mail.gmail.com/ [1]
Link: https://lore.kernel.org/r/163819590658.215744.14934902514281054323.stgit@warthog.procyon.org.uk/ # v1
Link: https://lore.kernel.org/r/163906891983.143852.6219772337558577395.stgit@warthog.procyon.org.uk/ # v2
Link: https://lore.kernel.org/r/163967088507.1823006.12659006350221417165.stgit@warthog.procyon.org.uk/ # v3
Link: https://lore.kernel.org/r/164021498432.640689.12743483856927722772.stgit@warthog.procyon.org.uk/ # v4
2021-10-20 22:53:34 +08:00
|
|
|
error_cookie_jar:
|
|
|
|
fscache_proc_cleanup();
|
2021-10-20 21:34:41 +08:00
|
|
|
error_proc:
|
|
|
|
destroy_workqueue(fscache_wq);
|
|
|
|
error_wq:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
fs_initcall(fscache_init);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* clean up on module removal
|
|
|
|
*/
|
|
|
|
static void __exit fscache_exit(void)
|
|
|
|
{
|
|
|
|
_enter("");
|
|
|
|
|
fscache: Implement cookie registration
Add functions to the fscache API to allow data file cookies to be acquired
and relinquished by the network filesystem. It is intended that the
filesystem will create such cookies per-inode under a volume.
To request a cookie, the filesystem should call:
struct fscache_cookie *
fscache_acquire_cookie(struct fscache_volume *volume,
u8 advice,
const void *index_key,
size_t index_key_len,
const void *aux_data,
size_t aux_data_len,
loff_t object_size)
The filesystem must first have created a volume cookie, which is passed in
here. If it passes in NULL then the function will just return a NULL
cookie.
A binary key should be passed in index_key and is of size index_key_len.
This is saved in the cookie and is used to locate the associated data in
the cache.
A coherency data buffer of size aux_data_len will be allocated and
initialised from the buffer pointed to by aux_data. This is used to
validate cache objects when they're opened and is stored on disk with them
when they're committed. The data is stored in the cookie and will be
updateable by various functions in later patches.
The object_size must also be given. This is also used to perform a
coherency check and to size the backing storage appropriately.
This function disallows a cookie from being acquired twice in parallel,
though it will cause the second user to wait if the first is busy
relinquishing its cookie.
When a network filesystem has finished with a cookie, it should call:
void
fscache_relinquish_cookie(struct fscache_volume *volume,
bool retire)
If retire is true, any backing data will be discarded immediately.
Changes
=======
ver #3:
- fscache_hash()'s size parameter is now in bytes. Use __le32 as the unit
to round up to.
- When comparing cookies, simply see if the attributes are the same rather
than subtracting them to produce a strcmp-style return[1].
- Add a check to see if the cookie is still hashed at the point of
freeing.
ver #2:
- Don't hold n_accesses elevated whilst cache is bound to a cookie, but
rather add a flag that prevents the state machine from being queued when
n_accesses reaches 0.
- Remove the unused cookie pointer field from the fscache_acquire
tracepoint.
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
cc: linux-cachefs@redhat.com
Link: https://lore.kernel.org/r/CAHk-=whtkzB446+hX0zdLsdcUJsJ=8_-0S1mE_R+YurThfUbLA@mail.gmail.com/ [1]
Link: https://lore.kernel.org/r/163819590658.215744.14934902514281054323.stgit@warthog.procyon.org.uk/ # v1
Link: https://lore.kernel.org/r/163906891983.143852.6219772337558577395.stgit@warthog.procyon.org.uk/ # v2
Link: https://lore.kernel.org/r/163967088507.1823006.12659006350221417165.stgit@warthog.procyon.org.uk/ # v3
Link: https://lore.kernel.org/r/164021498432.640689.12743483856927722772.stgit@warthog.procyon.org.uk/ # v4
2021-10-20 22:53:34 +08:00
|
|
|
kmem_cache_destroy(fscache_cookie_jar);
|
2021-10-20 21:34:41 +08:00
|
|
|
fscache_proc_cleanup();
|
|
|
|
destroy_workqueue(fscache_wq);
|
|
|
|
pr_notice("Unloaded\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
module_exit(fscache_exit);
|