fscache: Implement cookie-level access helpers
authorDavid Howells <dhowells@redhat.com>
Wed, 20 Oct 2021 14:53:34 +0000 (15:53 +0100)
committerDavid Howells <dhowells@redhat.com>
Fri, 7 Jan 2022 09:22:19 +0000 (09:22 +0000)
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/
Link: https://lore.kernel.org/r/163906895313.143852.10141619544149102193.stgit@warthog.procyon.org.uk/
Link: https://lore.kernel.org/r/163967095980.1823006.1133648159424418877.stgit@warthog.procyon.org.uk/
Link: https://lore.kernel.org/r/164021503063.640689.8870918985269528670.stgit@warthog.procyon.org.uk/
fs/fscache/cookie.c
fs/fscache/internal.h
fs/fscache/main.c
include/linux/fscache-cache.h
include/trace/events/fscache.h

index 438b0098aa735620b41df1b4c3ba5d9ee31122fd..04d2127bd354f1838dd68e6683dc028a83d0e87f 100644 (file)
@@ -62,6 +62,104 @@ static void fscache_free_cookie(struct fscache_cookie *cookie)
        kmem_cache_free(fscache_cookie_jar, cookie);
 }
 
+/*
+ * 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;
+}
+
 static inline void wake_up_cookie_state(struct fscache_cookie *cookie)
 {
        /* Use a barrier to ensure that waiters see the state variable
index 91a4ea08ec0b0517c89a4e6d8c337de4cab0375d..e0d8ef212e827e7223b65c7b9b5fb211b05878bd 100644 (file)
@@ -59,6 +59,9 @@ extern struct kmem_cache *fscache_cookie_jar;
 extern const struct seq_operations fscache_cookies_seq_ops;
 
 extern void fscache_print_cookie(struct fscache_cookie *cookie, char prefix);
+extern bool fscache_begin_cookie_access(struct fscache_cookie *cookie,
+                                       enum fscache_access_trace why);
+
 static inline void fscache_see_cookie(struct fscache_cookie *cookie,
                                      enum fscache_cookie_trace where)
 {
index 6cab5d99ba4c85f85977026178cbf025d1ae173b..dad85fd84f6f9f9245112b7bdcea4305313c8950 100644 (file)
@@ -23,6 +23,7 @@ MODULE_PARM_DESC(fscache_debug,
 
 EXPORT_TRACEPOINT_SYMBOL(fscache_access_cache);
 EXPORT_TRACEPOINT_SYMBOL(fscache_access_volume);
+EXPORT_TRACEPOINT_SYMBOL(fscache_access);
 
 struct workqueue_struct *fscache_wq;
 EXPORT_SYMBOL(fscache_wq);
index fbbd8a2afe12b02a161be261919f39a544bff5f0..66624407ba8437d174ecb2e7c58f89c09bfc6f55 100644 (file)
@@ -61,6 +61,8 @@ extern struct fscache_cookie *fscache_get_cookie(struct fscache_cookie *cookie,
                                                 enum fscache_cookie_trace where);
 extern void fscache_put_cookie(struct fscache_cookie *cookie,
                               enum fscache_cookie_trace where);
+extern void fscache_end_cookie_access(struct fscache_cookie *cookie,
+                                     enum fscache_access_trace why);
 extern void fscache_set_cookie_state(struct fscache_cookie *cookie,
                                     enum fscache_cookie_state state);
 
index 4f40cfa52469258028fb658a768cc6ed89f13620..b1a962adfd167ece44a4f5a830bd9b1f36fbebb3 100644 (file)
@@ -279,6 +279,35 @@ TRACE_EVENT(fscache_access_volume,
                      __entry->n_accesses)
            );
 
+TRACE_EVENT(fscache_access,
+           TP_PROTO(unsigned int cookie_debug_id,
+                    int ref,
+                    int n_accesses,
+                    enum fscache_access_trace why),
+
+           TP_ARGS(cookie_debug_id, ref, n_accesses, why),
+
+           TP_STRUCT__entry(
+                   __field(unsigned int,               cookie          )
+                   __field(int,                        ref             )
+                   __field(int,                        n_accesses      )
+                   __field(enum fscache_access_trace,  why             )
+                            ),
+
+           TP_fast_assign(
+                   __entry->cookie     = cookie_debug_id;
+                   __entry->ref        = ref;
+                   __entry->n_accesses = n_accesses;
+                   __entry->why        = why;
+                          ),
+
+           TP_printk("c=%08x %s r=%d a=%d",
+                     __entry->cookie,
+                     __print_symbolic(__entry->why, fscache_access_traces),
+                     __entry->ref,
+                     __entry->n_accesses)
+           );
+
 TRACE_EVENT(fscache_acquire,
            TP_PROTO(struct fscache_cookie *cookie),