diff options
-rw-r--r-- | fs/fscache/Makefile | 1 | ||||
-rw-r--r-- | fs/fscache/internal.h | 11 | ||||
-rw-r--r-- | fs/fscache/io.c | 151 | ||||
-rw-r--r-- | include/linux/fscache-cache.h | 11 | ||||
-rw-r--r-- | include/linux/fscache.h | 49 | ||||
-rw-r--r-- | include/trace/events/fscache.h | 6 |
6 files changed, 229 insertions, 0 deletions
diff --git a/fs/fscache/Makefile b/fs/fscache/Makefile index bcc79615f93a..afb090ea16c4 100644 --- a/fs/fscache/Makefile +++ b/fs/fscache/Makefile @@ -6,6 +6,7 @@ fscache-y := \ cache.o \ cookie.o \ + io.o \ main.o \ volume.o diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index 7fb83d216360..017bf3d346a4 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -71,6 +71,17 @@ static inline void fscache_see_cookie(struct fscache_cookie *cookie, } /* + * io.c + */ +static inline void fscache_end_operation(struct netfs_cache_resources *cres) +{ + const struct netfs_cache_ops *ops = fscache_operation_valid(cres); + + if (ops) + ops->end_operation(cres); +} + +/* * main.c */ extern unsigned fscache_debug; diff --git a/fs/fscache/io.c b/fs/fscache/io.c new file mode 100644 index 000000000000..460a43473019 --- /dev/null +++ b/fs/fscache/io.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Cache data I/O routines + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ +#define FSCACHE_DEBUG_LEVEL OPERATION +#include <linux/fscache-cache.h> +#include <linux/uio.h> +#include <linux/bvec.h> +#include <linux/slab.h> +#include <linux/uio.h> +#include "internal.h" + +/** + * fscache_wait_for_operation - Wait for an object become accessible + * @cres: The cache resources for the operation being performed + * @want_state: The minimum state the object must be at + * + * See if the target cache object is at the specified minimum state of + * accessibility yet, and if not, wait for it. + */ +bool fscache_wait_for_operation(struct netfs_cache_resources *cres, + enum fscache_want_state want_state) +{ + struct fscache_cookie *cookie = fscache_cres_cookie(cres); + enum fscache_cookie_state state; + +again: + if (!fscache_cache_is_live(cookie->volume->cache)) { + _leave(" [broken]"); + return false; + } + + state = fscache_cookie_state(cookie); + _enter("c=%08x{%u},%x", cookie->debug_id, state, want_state); + + switch (state) { + case FSCACHE_COOKIE_STATE_CREATING: + case FSCACHE_COOKIE_STATE_INVALIDATING: + if (want_state == FSCACHE_WANT_PARAMS) + goto ready; /* There can be no content */ + fallthrough; + case FSCACHE_COOKIE_STATE_LOOKING_UP: + case FSCACHE_COOKIE_STATE_LRU_DISCARDING: + wait_var_event(&cookie->state, + fscache_cookie_state(cookie) != state); + goto again; + + case FSCACHE_COOKIE_STATE_ACTIVE: + goto ready; + case FSCACHE_COOKIE_STATE_DROPPED: + case FSCACHE_COOKIE_STATE_RELINQUISHING: + default: + _leave(" [not live]"); + return false; + } + +ready: + if (!cres->cache_priv2) + return cookie->volume->cache->ops->begin_operation(cres, want_state); + return true; +} +EXPORT_SYMBOL(fscache_wait_for_operation); + +/* + * Begin an I/O operation on the cache, waiting till we reach the right state. + * + * Attaches the resources required to the operation resources record. + */ +static int fscache_begin_operation(struct netfs_cache_resources *cres, + struct fscache_cookie *cookie, + enum fscache_want_state want_state, + enum fscache_access_trace why) +{ + enum fscache_cookie_state state; + long timeo; + bool once_only = false; + + cres->ops = NULL; + cres->cache_priv = cookie; + cres->cache_priv2 = NULL; + cres->debug_id = cookie->debug_id; + cres->inval_counter = cookie->inval_counter; + + if (!fscache_begin_cookie_access(cookie, why)) + return -ENOBUFS; + +again: + spin_lock(&cookie->lock); + + state = fscache_cookie_state(cookie); + _enter("c=%08x{%u},%x", cookie->debug_id, state, want_state); + + switch (state) { + case FSCACHE_COOKIE_STATE_LOOKING_UP: + case FSCACHE_COOKIE_STATE_LRU_DISCARDING: + case FSCACHE_COOKIE_STATE_INVALIDATING: + goto wait_for_file_wrangling; + case FSCACHE_COOKIE_STATE_CREATING: + if (want_state == FSCACHE_WANT_PARAMS) + goto ready; /* There can be no content */ + goto wait_for_file_wrangling; + case FSCACHE_COOKIE_STATE_ACTIVE: + goto ready; + case FSCACHE_COOKIE_STATE_DROPPED: + case FSCACHE_COOKIE_STATE_RELINQUISHING: + WARN(1, "Can't use cookie in state %u\n", cookie->state); + goto not_live; + default: + goto not_live; + } + +ready: + spin_unlock(&cookie->lock); + if (!cookie->volume->cache->ops->begin_operation(cres, want_state)) + goto failed; + return 0; + +wait_for_file_wrangling: + spin_unlock(&cookie->lock); + trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref), + atomic_read(&cookie->n_accesses), + fscache_access_io_wait); + timeo = wait_var_event_timeout(&cookie->state, + fscache_cookie_state(cookie) != state, 20 * HZ); + if (timeo <= 1 && !once_only) { + pr_warn("%s: cookie state change wait timed out: cookie->state=%u state=%u", + __func__, fscache_cookie_state(cookie), state); + fscache_print_cookie(cookie, 'O'); + once_only = true; + } + goto again; + +not_live: + spin_unlock(&cookie->lock); +failed: + cres->cache_priv = NULL; + cres->ops = NULL; + fscache_end_cookie_access(cookie, fscache_access_io_not_live); + _leave(" = -ENOBUFS"); + return -ENOBUFS; +} + +int __fscache_begin_read_operation(struct netfs_cache_resources *cres, + struct fscache_cookie *cookie) +{ + return fscache_begin_operation(cres, cookie, FSCACHE_WANT_PARAMS, + fscache_access_io_read); +} +EXPORT_SYMBOL(__fscache_begin_read_operation); diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 1ad56bfd9d72..566497cf5f13 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -67,6 +67,10 @@ struct fscache_cache_ops { /* Invalidate an object */ bool (*invalidate_cookie)(struct fscache_cookie *cookie); + /* Begin an operation for the netfs lib */ + bool (*begin_operation)(struct netfs_cache_resources *cres, + enum fscache_want_state want_state); + /* Prepare to write to a live cache object */ void (*prepare_to_write)(struct fscache_cookie *cookie); }; @@ -101,6 +105,8 @@ extern void fscache_end_cookie_access(struct fscache_cookie *cookie, extern void fscache_cookie_lookup_negative(struct fscache_cookie *cookie); extern void fscache_resume_after_invalidation(struct fscache_cookie *cookie); extern void fscache_caching_failed(struct fscache_cookie *cookie); +extern bool fscache_wait_for_operation(struct netfs_cache_resources *cred, + enum fscache_want_state state); /** * fscache_cookie_state - Read the state of a cookie @@ -129,4 +135,9 @@ static inline void *fscache_get_key(struct fscache_cookie *cookie) return cookie->key; } +static inline struct fscache_cookie *fscache_cres_cookie(struct netfs_cache_resources *cres) +{ + return cres->cache_priv; +} + #endif /* _LINUX_FSCACHE_CACHE_H */ diff --git a/include/linux/fscache.h b/include/linux/fscache.h index 0f36d1fac237..7cdc63c4fe35 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -41,6 +41,12 @@ struct fscache_cookie; #define FSCACHE_INVAL_DIO_WRITE 0x01 /* Invalidate due to DIO write */ +enum fscache_want_state { + FSCACHE_WANT_PARAMS, + FSCACHE_WANT_WRITE, + FSCACHE_WANT_READ, +}; + /* * Data object state. */ @@ -157,6 +163,7 @@ extern void __fscache_use_cookie(struct fscache_cookie *, bool); extern void __fscache_unuse_cookie(struct fscache_cookie *, const void *, const loff_t *); extern void __fscache_relinquish_cookie(struct fscache_cookie *, bool); extern void __fscache_invalidate(struct fscache_cookie *, const void *, loff_t, unsigned int); +extern int __fscache_begin_read_operation(struct netfs_cache_resources *, struct fscache_cookie *); /** * fscache_acquire_volume - Register a volume as desiring caching services @@ -358,4 +365,46 @@ void fscache_invalidate(struct fscache_cookie *cookie, __fscache_invalidate(cookie, aux_data, size, flags); } +/** + * fscache_operation_valid - Return true if operations resources are usable + * @cres: The resources to check. + * + * Returns a pointer to the operations table if usable or NULL if not. + */ +static inline +const struct netfs_cache_ops *fscache_operation_valid(const struct netfs_cache_resources *cres) +{ + return fscache_resources_valid(cres) ? cres->ops : NULL; +} + +/** + * fscache_begin_read_operation - Begin a read operation for the netfs lib + * @cres: The cache resources for the read being performed + * @cookie: The cookie representing the cache object + * + * Begin a read operation on behalf of the netfs helper library. @cres + * indicates the cache resources to which the operation state should be + * attached; @cookie indicates the cache object that will be accessed. + * + * This is intended to be called from the ->begin_cache_operation() netfs lib + * operation as implemented by the network filesystem. + * + * @cres->inval_counter is set from @cookie->inval_counter for comparison at + * the end of the operation. This allows invalidation during the operation to + * be detected by the caller. + * + * Returns: + * * 0 - Success + * * -ENOBUFS - No caching available + * * Other error code from the cache, such as -ENOMEM. + */ +static inline +int fscache_begin_read_operation(struct netfs_cache_resources *cres, + struct fscache_cookie *cookie) +{ + if (fscache_cookie_enabled(cookie)) + return __fscache_begin_read_operation(cres, cookie); + return -ENOBUFS; +} + #endif /* _LINUX_FSCACHE_H */ diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index 294792881434..9f78c903b00a 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -76,6 +76,9 @@ enum fscache_access_trace { fscache_access_cache_unpin, fscache_access_invalidate_cookie, fscache_access_invalidate_cookie_end, + fscache_access_io_not_live, + fscache_access_io_read, + fscache_access_io_wait, fscache_access_lookup_cookie, fscache_access_lookup_cookie_end, fscache_access_lookup_cookie_end_failed, @@ -143,6 +146,9 @@ enum fscache_access_trace { EM(fscache_access_cache_unpin, "UNPIN cache ") \ EM(fscache_access_invalidate_cookie, "BEGIN inval ") \ EM(fscache_access_invalidate_cookie_end,"END inval ") \ + EM(fscache_access_io_not_live, "END io_notl") \ + EM(fscache_access_io_read, "BEGIN io_read") \ + EM(fscache_access_io_wait, "WAIT io ") \ EM(fscache_access_lookup_cookie, "BEGIN lookup ") \ EM(fscache_access_lookup_cookie_end, "END lookup ") \ EM(fscache_access_lookup_cookie_end_failed,"END lookupf") \ |