linux/fs/fscache/cookie.c

596 lines
17 KiB
C
Raw Normal View History

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
// SPDX-License-Identifier: GPL-2.0-or-later
/* netfs cookie management
*
* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* See Documentation/filesystems/caching/netfs-api.rst for more information on
* the netfs API.
*/
#define FSCACHE_DEBUG_LEVEL COOKIE
#include <linux/module.h>
#include <linux/slab.h>
#include "internal.h"
struct kmem_cache *fscache_cookie_jar;
static void fscache_drop_cookie(struct fscache_cookie *cookie);
#define fscache_cookie_hash_shift 15
static struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_shift];
static LIST_HEAD(fscache_cookies);
static DEFINE_RWLOCK(fscache_cookies_lock);
static const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR] = "-LCAFWRD";
void fscache_print_cookie(struct fscache_cookie *cookie, char prefix)
{
const u8 *k;
pr_err("%c-cookie c=%08x [fl=%lx na=%u nA=%u s=%c]\n",
prefix,
cookie->debug_id,
cookie->flags,
atomic_read(&cookie->n_active),
atomic_read(&cookie->n_accesses),
fscache_cookie_states[cookie->state]);
pr_err("%c-cookie V=%08x [%s]\n",
prefix,
cookie->volume->debug_id,
cookie->volume->key);
k = (cookie->key_len <= sizeof(cookie->inline_key)) ?
cookie->inline_key : cookie->key;
pr_err("%c-key=[%u] '%*phN'\n", prefix, cookie->key_len, cookie->key_len, k);
}
static void fscache_free_cookie(struct fscache_cookie *cookie)
{
if (WARN_ON_ONCE(test_bit(FSCACHE_COOKIE_IS_HASHED, &cookie->flags))) {
fscache_print_cookie(cookie, 'F');
return;
}
write_lock(&fscache_cookies_lock);
list_del(&cookie->proc_link);
write_unlock(&fscache_cookies_lock);
if (cookie->aux_len > sizeof(cookie->inline_aux))
kfree(cookie->aux);
if (cookie->key_len > sizeof(cookie->inline_key))
kfree(cookie->key);
fscache_stat_d(&fscache_n_cookies);
kmem_cache_free(fscache_cookie_jar, cookie);
}
fscache: Implement cookie-level access helpers Add a number of helper functions to manage access to a cookie, pinning the cache object in place for the duration to prevent cache withdrawal from removing it: (1) void fscache_init_access_gate(struct fscache_cookie *cookie); This function initialises the access count when a cache binds to a cookie. An extra ref is taken on the access count to prevent wakeups while the cache is active. We're only interested in the wakeup when a cookie is being withdrawn and we're waiting for it to quiesce - at which point the counter will be decremented before the wait. The FSCACHE_COOKIE_NACC_ELEVATED flag is set on the cookie to keep track of the extra ref in order to handle a race between relinquishment and withdrawal both trying to drop the extra ref. (2) bool fscache_begin_cookie_access(struct fscache_cookie *cookie, enum fscache_access_trace why); This function attempts to begin access upon a cookie, pinning it in place if it's cached. If successful, it returns true and leaves a the access count incremented. (3) void fscache_end_cookie_access(struct fscache_cookie *cookie, enum fscache_access_trace why); This function drops the access count obtained by (2), permitting object withdrawal to take place when it reaches zero. A tracepoint is provided to track changes to the access counter on a cookie. Changes ======= 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. 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/163819595085.215744.1706073049250505427.stgit@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906895313.143852.10141619544149102193.stgit@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/163967095980.1823006.1133648159424418877.stgit@warthog.procyon.org.uk/ # v3 Link: https://lore.kernel.org/r/164021503063.640689.8870918985269528670.stgit@warthog.procyon.org.uk/ # v4
2021-10-20 22:53:34 +08:00
/*
* Initialise the access gate on a cookie by setting a flag to prevent the
* state machine from being queued when the access counter transitions to 0.
* We're only interested in this when we withdraw caching services from the
* cookie.
*/
static void fscache_init_access_gate(struct fscache_cookie *cookie)
{
int n_accesses;
n_accesses = atomic_read(&cookie->n_accesses);
trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref),
n_accesses, fscache_access_cache_pin);
set_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->flags);
}
/**
* fscache_end_cookie_access - Unpin a cache at the end of an access.
* @cookie: A data file cookie
* @why: An indication of the circumstances of the access for tracing
*
* Unpin a cache cookie after we've accessed it and bring a deferred
* relinquishment or withdrawal state into effect.
*
* The @why indicator is provided for tracing purposes.
*/
void fscache_end_cookie_access(struct fscache_cookie *cookie,
enum fscache_access_trace why)
{
int n_accesses;
smp_mb__before_atomic();
n_accesses = atomic_dec_return(&cookie->n_accesses);
trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref),
n_accesses, why);
if (n_accesses == 0 &&
!test_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->flags)) {
// PLACEHOLDER: Need to poke the state machine
}
}
EXPORT_SYMBOL(fscache_end_cookie_access);
/*
* Pin the cache behind a cookie so that we can access it.
*/
static void __fscache_begin_cookie_access(struct fscache_cookie *cookie,
enum fscache_access_trace why)
{
int n_accesses;
n_accesses = atomic_inc_return(&cookie->n_accesses);
smp_mb__after_atomic(); /* (Future) read state after is-caching.
* Reread n_accesses after is-caching
*/
trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref),
n_accesses, why);
}
/**
* fscache_begin_cookie_access - Pin a cache so data can be accessed
* @cookie: A data file cookie
* @why: An indication of the circumstances of the access for tracing
*
* Attempt to pin the cache to prevent it from going away whilst we're
* accessing data and returns true if successful. This works as follows:
*
* (1) If the cookie is not being cached (ie. FSCACHE_COOKIE_IS_CACHING is not
* set), we return false to indicate access was not permitted.
*
* (2) If the cookie is being cached, we increment its n_accesses count and
* then recheck the IS_CACHING flag, ending the access if it got cleared.
*
* (3) When we end the access, we decrement the cookie's n_accesses and wake
* up the any waiters if it reaches 0.
*
* (4) Whilst the cookie is actively being cached, its n_accesses is kept
* artificially incremented to prevent wakeups from happening.
*
* (5) When the cache is taken offline or if the cookie is culled, the flag is
* cleared to prevent new accesses, the cookie's n_accesses is decremented
* and we wait for it to become 0.
*
* The @why indicator are merely provided for tracing purposes.
*/
bool fscache_begin_cookie_access(struct fscache_cookie *cookie,
enum fscache_access_trace why)
{
if (!test_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags))
return false;
__fscache_begin_cookie_access(cookie, why);
if (!test_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags) ||
!fscache_cache_is_live(cookie->volume->cache)) {
fscache_end_cookie_access(cookie, fscache_access_unlive);
return false;
}
return true;
}
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
static inline void wake_up_cookie_state(struct fscache_cookie *cookie)
{
/* Use a barrier to ensure that waiters see the state variable
* change, as spin_unlock doesn't guarantee a barrier.
*
* See comments over wake_up_bit() and waitqueue_active().
*/
smp_mb();
wake_up_var(&cookie->state);
}
static void __fscache_set_cookie_state(struct fscache_cookie *cookie,
enum fscache_cookie_state state)
{
cookie->state = state;
}
/*
* Change the state a cookie is at and wake up anyone waiting for that - but
* only if the cookie isn't already marked as being in a cleanup state.
*/
void fscache_set_cookie_state(struct fscache_cookie *cookie,
enum fscache_cookie_state state)
{
bool changed = false;
spin_lock(&cookie->lock);
switch (cookie->state) {
case FSCACHE_COOKIE_STATE_RELINQUISHING:
break;
default:
__fscache_set_cookie_state(cookie, state);
changed = true;
break;
}
spin_unlock(&cookie->lock);
if (changed)
wake_up_cookie_state(cookie);
}
EXPORT_SYMBOL(fscache_set_cookie_state);
/*
* Set the index key in a cookie. The cookie struct has space for a 16-byte
* key plus length and hash, but if that's not big enough, it's instead a
* pointer to a buffer containing 3 bytes of hash, 1 byte of length and then
* the key data.
*/
static int fscache_set_key(struct fscache_cookie *cookie,
const void *index_key, size_t index_key_len)
{
void *buf;
size_t buf_size;
buf_size = round_up(index_key_len, sizeof(__le32));
if (index_key_len > sizeof(cookie->inline_key)) {
buf = kzalloc(buf_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
cookie->key = buf;
} else {
buf = cookie->inline_key;
}
memcpy(buf, index_key, index_key_len);
cookie->key_hash = fscache_hash(cookie->volume->key_hash,
buf, buf_size);
return 0;
}
static bool fscache_cookie_same(const struct fscache_cookie *a,
const struct fscache_cookie *b)
{
const void *ka, *kb;
if (a->key_hash != b->key_hash ||
a->volume != b->volume ||
a->key_len != b->key_len)
return false;
if (a->key_len <= sizeof(a->inline_key)) {
ka = &a->inline_key;
kb = &b->inline_key;
} else {
ka = a->key;
kb = b->key;
}
return memcmp(ka, kb, a->key_len) == 0;
}
static atomic_t fscache_cookie_debug_id = ATOMIC_INIT(1);
/*
* Allocate a cookie.
*/
static struct fscache_cookie *fscache_alloc_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)
{
struct fscache_cookie *cookie;
/* allocate and initialise a cookie */
cookie = kmem_cache_zalloc(fscache_cookie_jar, GFP_KERNEL);
if (!cookie)
return NULL;
fscache_stat(&fscache_n_cookies);
cookie->volume = volume;
cookie->advice = advice;
cookie->key_len = index_key_len;
cookie->aux_len = aux_data_len;
cookie->object_size = object_size;
if (object_size == 0)
__set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags);
if (fscache_set_key(cookie, index_key, index_key_len) < 0)
goto nomem;
if (cookie->aux_len <= sizeof(cookie->inline_aux)) {
memcpy(cookie->inline_aux, aux_data, cookie->aux_len);
} else {
cookie->aux = kmemdup(aux_data, cookie->aux_len, GFP_KERNEL);
if (!cookie->aux)
goto nomem;
}
refcount_set(&cookie->ref, 1);
cookie->debug_id = atomic_inc_return(&fscache_cookie_debug_id);
cookie->state = FSCACHE_COOKIE_STATE_QUIESCENT;
spin_lock_init(&cookie->lock);
INIT_LIST_HEAD(&cookie->commit_link);
INIT_WORK(&cookie->work, NULL /* PLACEHOLDER */);
write_lock(&fscache_cookies_lock);
list_add_tail(&cookie->proc_link, &fscache_cookies);
write_unlock(&fscache_cookies_lock);
fscache_see_cookie(cookie, fscache_cookie_new_acquire);
return cookie;
nomem:
fscache_free_cookie(cookie);
return NULL;
}
static void fscache_wait_on_collision(struct fscache_cookie *candidate,
struct fscache_cookie *wait_for)
{
enum fscache_cookie_state *statep = &wait_for->state;
wait_var_event_timeout(statep, READ_ONCE(*statep) == FSCACHE_COOKIE_STATE_DROPPED,
20 * HZ);
if (READ_ONCE(*statep) != FSCACHE_COOKIE_STATE_DROPPED) {
pr_notice("Potential collision c=%08x old: c=%08x",
candidate->debug_id, wait_for->debug_id);
wait_var_event(statep, READ_ONCE(*statep) == FSCACHE_COOKIE_STATE_DROPPED);
}
}
/*
* Attempt to insert the new cookie into the hash. If there's a collision, we
* wait for the old cookie to complete if it's being relinquished and an error
* otherwise.
*/
static bool fscache_hash_cookie(struct fscache_cookie *candidate)
{
struct fscache_cookie *cursor, *wait_for = NULL;
struct hlist_bl_head *h;
struct hlist_bl_node *p;
unsigned int bucket;
bucket = candidate->key_hash & (ARRAY_SIZE(fscache_cookie_hash) - 1);
h = &fscache_cookie_hash[bucket];
hlist_bl_lock(h);
hlist_bl_for_each_entry(cursor, p, h, hash_link) {
if (fscache_cookie_same(candidate, cursor)) {
if (!test_bit(FSCACHE_COOKIE_RELINQUISHED, &cursor->flags))
goto collision;
wait_for = fscache_get_cookie(cursor,
fscache_cookie_get_hash_collision);
break;
}
}
fscache_get_volume(candidate->volume, fscache_volume_get_cookie);
atomic_inc(&candidate->volume->n_cookies);
hlist_bl_add_head(&candidate->hash_link, h);
set_bit(FSCACHE_COOKIE_IS_HASHED, &candidate->flags);
hlist_bl_unlock(h);
if (wait_for) {
fscache_wait_on_collision(candidate, wait_for);
fscache_put_cookie(wait_for, fscache_cookie_put_hash_collision);
}
return true;
collision:
trace_fscache_cookie(cursor->debug_id, refcount_read(&cursor->ref),
fscache_cookie_collision);
pr_err("Duplicate cookie detected\n");
fscache_print_cookie(cursor, 'O');
fscache_print_cookie(candidate, 'N');
hlist_bl_unlock(h);
return false;
}
/*
* Request a cookie to represent a data storage object within a volume.
*
* We never let on to the netfs about errors. We may set a negative cookie
* pointer, but that's okay
*/
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)
{
struct fscache_cookie *cookie;
_enter("V=%x", volume->debug_id);
if (!index_key || !index_key_len || index_key_len > 255 || aux_data_len > 255)
return NULL;
if (!aux_data || !aux_data_len) {
aux_data = NULL;
aux_data_len = 0;
}
fscache_stat(&fscache_n_acquires);
cookie = fscache_alloc_cookie(volume, advice,
index_key, index_key_len,
aux_data, aux_data_len,
object_size);
if (!cookie) {
fscache_stat(&fscache_n_acquires_oom);
return NULL;
}
if (!fscache_hash_cookie(cookie)) {
fscache_see_cookie(cookie, fscache_cookie_discard);
fscache_free_cookie(cookie);
return NULL;
}
trace_fscache_acquire(cookie);
fscache_stat(&fscache_n_acquires_ok);
_leave(" = c=%08x", cookie->debug_id);
return cookie;
}
EXPORT_SYMBOL(__fscache_acquire_cookie);
/*
* Remove a cookie from the hash table.
*/
static void fscache_unhash_cookie(struct fscache_cookie *cookie)
{
struct hlist_bl_head *h;
unsigned int bucket;
bucket = cookie->key_hash & (ARRAY_SIZE(fscache_cookie_hash) - 1);
h = &fscache_cookie_hash[bucket];
hlist_bl_lock(h);
hlist_bl_del(&cookie->hash_link);
clear_bit(FSCACHE_COOKIE_IS_HASHED, &cookie->flags);
hlist_bl_unlock(h);
}
/*
* Finalise a cookie after all its resources have been disposed of.
*/
static void fscache_drop_cookie(struct fscache_cookie *cookie)
{
spin_lock(&cookie->lock);
__fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_DROPPED);
spin_unlock(&cookie->lock);
wake_up_cookie_state(cookie);
fscache_unhash_cookie(cookie);
fscache_stat(&fscache_n_relinquishes_dropped);
}
/*
* Allow the netfs to release a cookie back to the cache.
* - the object will be marked as recyclable on disk if retire is true
*/
void __fscache_relinquish_cookie(struct fscache_cookie *cookie, bool retire)
{
fscache_stat(&fscache_n_relinquishes);
if (retire)
fscache_stat(&fscache_n_relinquishes_retire);
_enter("c=%08x{%d},%d",
cookie->debug_id, atomic_read(&cookie->n_active), retire);
if (WARN(test_and_set_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags),
"Cookie c=%x already relinquished\n", cookie->debug_id))
return;
if (retire)
set_bit(FSCACHE_COOKIE_RETIRED, &cookie->flags);
trace_fscache_relinquish(cookie, retire);
ASSERTCMP(atomic_read(&cookie->n_active), ==, 0);
ASSERTCMP(atomic_read(&cookie->volume->n_cookies), >, 0);
atomic_dec(&cookie->volume->n_cookies);
set_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags);
if (test_bit(FSCACHE_COOKIE_HAS_BEEN_CACHED, &cookie->flags))
; // PLACEHOLDER: Do something here if the cookie was cached
else
fscache_drop_cookie(cookie);
fscache_put_cookie(cookie, fscache_cookie_put_relinquish);
}
EXPORT_SYMBOL(__fscache_relinquish_cookie);
/*
* Drop a reference to a cookie.
*/
void fscache_put_cookie(struct fscache_cookie *cookie,
enum fscache_cookie_trace where)
{
struct fscache_volume *volume = cookie->volume;
unsigned int cookie_debug_id = cookie->debug_id;
bool zero;
int ref;
zero = __refcount_dec_and_test(&cookie->ref, &ref);
trace_fscache_cookie(cookie_debug_id, ref - 1, where);
if (zero) {
fscache_free_cookie(cookie);
fscache_put_volume(volume, fscache_volume_put_cookie);
}
}
EXPORT_SYMBOL(fscache_put_cookie);
/*
* Get a reference to a cookie.
*/
struct fscache_cookie *fscache_get_cookie(struct fscache_cookie *cookie,
enum fscache_cookie_trace where)
{
int ref;
__refcount_inc(&cookie->ref, &ref);
trace_fscache_cookie(cookie->debug_id, ref + 1, where);
return cookie;
}
EXPORT_SYMBOL(fscache_get_cookie);
/*
* Generate a list of extant cookies in /proc/fs/fscache/cookies
*/
static int fscache_cookies_seq_show(struct seq_file *m, void *v)
{
struct fscache_cookie *cookie;
unsigned int keylen = 0, auxlen = 0;
u8 *p;
if (v == &fscache_cookies) {
seq_puts(m,
"COOKIE VOLUME REF ACT ACC S FL DEF \n"
"======== ======== === === === = == ================\n"
);
return 0;
}
cookie = list_entry(v, struct fscache_cookie, proc_link);
seq_printf(m,
"%08x %08x %3d %3d %3d %c %02lx",
cookie->debug_id,
cookie->volume->debug_id,
refcount_read(&cookie->ref),
atomic_read(&cookie->n_active),
atomic_read(&cookie->n_accesses),
fscache_cookie_states[cookie->state],
cookie->flags);
keylen = cookie->key_len;
auxlen = cookie->aux_len;
if (keylen > 0 || auxlen > 0) {
seq_puts(m, " ");
p = keylen <= sizeof(cookie->inline_key) ?
cookie->inline_key : cookie->key;
for (; keylen > 0; keylen--)
seq_printf(m, "%02x", *p++);
if (auxlen > 0) {
seq_puts(m, ", ");
p = auxlen <= sizeof(cookie->inline_aux) ?
cookie->inline_aux : cookie->aux;
for (; auxlen > 0; auxlen--)
seq_printf(m, "%02x", *p++);
}
}
seq_puts(m, "\n");
return 0;
}
static void *fscache_cookies_seq_start(struct seq_file *m, loff_t *_pos)
__acquires(fscache_cookies_lock)
{
read_lock(&fscache_cookies_lock);
return seq_list_start_head(&fscache_cookies, *_pos);
}
static void *fscache_cookies_seq_next(struct seq_file *m, void *v, loff_t *_pos)
{
return seq_list_next(v, &fscache_cookies, _pos);
}
static void fscache_cookies_seq_stop(struct seq_file *m, void *v)
__releases(rcu)
{
read_unlock(&fscache_cookies_lock);
}
const struct seq_operations fscache_cookies_seq_ops = {
.start = fscache_cookies_seq_start,
.next = fscache_cookies_seq_next,
.stop = fscache_cookies_seq_stop,
.show = fscache_cookies_seq_show,
};