fscache: Implement cookie invalidation
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 function to invalidate the cache behind a cookie:

void fscache_invalidate(struct fscache_cookie *cookie,
const void *aux_data,
loff_t size,
unsigned int flags)

This causes any cached data for the specified cookie to be discarded.  If
the cookie is marked as being in use, a new cache object will be created if
possible and future I/O will use that instead.  In-flight I/O should be
abandoned (writes) or reconsidered (reads).  Each time it is called
cookie->inval_counter is incremented and this can be used to detect
invalidation at the end of an I/O operation.

The coherency data attached to the cookie can be updated and the cookie
size should be reset.  One flag is available, FSCACHE_INVAL_DIO_WRITE,
which should be used to indicate invalidation due to a DIO write on a
file.  This will temporarily disable caching for this cookie.

Changes
=======
ver #2:
 - Should only change to inval state if can get access to cache.

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/163819602231.215744.11206598147269491575.stgit@warthog.procyon.org.uk/
Link: https://lore.kernel.org/r/163906909707.143852.18056070560477964891.stgit@warthog.procyon.org.uk/
Link: https://lore.kernel.org/r/163967107447.1823006.5945029409592119962.stgit@warthog.procyon.org.uk/
Link: https://lore.kernel.org/r/164021512640.640689.11418616313147754172.stgit@warthog.procyon.org.uk/
fs/fscache/cookie.c
fs/fscache/internal.h
fs/fscache/stats.c
include/linux/fscache-cache.h
include/linux/fscache.h
include/linux/netfs.h
include/trace/events/fscache.h

index 2f5ee71..a7ea7d1 100644 (file)
@@ -19,6 +19,7 @@ static void fscache_cookie_lru_timed_out(struct timer_list *timer);
 static void fscache_cookie_lru_worker(struct work_struct *work);
 static void fscache_cookie_worker(struct work_struct *work);
 static void fscache_unhash_cookie(struct fscache_cookie *cookie);
+static void fscache_perform_invalidation(struct fscache_cookie *cookie);
 
 #define fscache_cookie_hash_shift 15
 static struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_shift];
@@ -28,7 +29,7 @@ static LIST_HEAD(fscache_cookie_lru);
 static DEFINE_SPINLOCK(fscache_cookie_lru_lock);
 DEFINE_TIMER(fscache_cookie_lru_timer, fscache_cookie_lru_timed_out);
 static DECLARE_WORK(fscache_cookie_lru_work, fscache_cookie_lru_worker);
-static const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR] = "-LCAFUWRD";
+static const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR] = "-LCAIFUWRD";
 unsigned int fscache_lru_cookie_timeout = 10 * HZ;
 
 void fscache_print_cookie(struct fscache_cookie *cookie, char prefix)
@@ -237,6 +238,19 @@ void fscache_cookie_lookup_negative(struct fscache_cookie *cookie)
 EXPORT_SYMBOL(fscache_cookie_lookup_negative);
 
 /**
+ * fscache_resume_after_invalidation - Allow I/O to resume after invalidation
+ * @cookie: The cookie that was invalidated
+ *
+ * Tell fscache that invalidation is sufficiently complete that I/O can be
+ * allowed again.
+ */
+void fscache_resume_after_invalidation(struct fscache_cookie *cookie)
+{
+       fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_ACTIVE);
+}
+EXPORT_SYMBOL(fscache_resume_after_invalidation);
+
+/**
  * fscache_caching_failed - Report that a failure stopped caching on a cookie
  * @cookie: The cookie that was affected
  *
@@ -566,6 +580,7 @@ again:
                        set_bit(FSCACHE_COOKIE_LOCAL_WRITE, &cookie->flags);
                break;
        case FSCACHE_COOKIE_STATE_ACTIVE:
+       case FSCACHE_COOKIE_STATE_INVALIDATING:
                if (will_modify &&
                    !test_and_set_bit(FSCACHE_COOKIE_LOCAL_WRITE, &cookie->flags)) {
                        set_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags);
@@ -671,6 +686,11 @@ again_locked:
                fscache_perform_lookup(cookie);
                goto again;
 
+       case FSCACHE_COOKIE_STATE_INVALIDATING:
+               spin_unlock(&cookie->lock);
+               fscache_perform_invalidation(cookie);
+               goto again;
+
        case FSCACHE_COOKIE_STATE_ACTIVE:
                if (test_and_clear_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags)) {
                        spin_unlock(&cookie->lock);
@@ -963,6 +983,72 @@ struct fscache_cookie *fscache_get_cookie(struct fscache_cookie *cookie,
 EXPORT_SYMBOL(fscache_get_cookie);
 
 /*
+ * Ask the cache to effect invalidation of a cookie.
+ */
+static void fscache_perform_invalidation(struct fscache_cookie *cookie)
+{
+       if (!cookie->volume->cache->ops->invalidate_cookie(cookie))
+               fscache_caching_failed(cookie);
+       fscache_end_cookie_access(cookie, fscache_access_invalidate_cookie_end);
+}
+
+/*
+ * Invalidate an object.
+ */
+void __fscache_invalidate(struct fscache_cookie *cookie,
+                         const void *aux_data, loff_t new_size,
+                         unsigned int flags)
+{
+       bool is_caching;
+
+       _enter("c=%x", cookie->debug_id);
+
+       fscache_stat(&fscache_n_invalidates);
+
+       if (WARN(test_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags),
+                "Trying to invalidate relinquished cookie\n"))
+               return;
+
+       if ((flags & FSCACHE_INVAL_DIO_WRITE) &&
+           test_and_set_bit(FSCACHE_COOKIE_DISABLED, &cookie->flags))
+               return;
+
+       spin_lock(&cookie->lock);
+       set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags);
+       fscache_update_aux(cookie, aux_data, &new_size);
+       cookie->inval_counter++;
+       trace_fscache_invalidate(cookie, new_size);
+
+       switch (cookie->state) {
+       case FSCACHE_COOKIE_STATE_INVALIDATING: /* is_still_valid will catch it */
+       default:
+               spin_unlock(&cookie->lock);
+               _leave(" [no %u]", cookie->state);
+               return;
+
+       case FSCACHE_COOKIE_STATE_LOOKING_UP:
+       case FSCACHE_COOKIE_STATE_CREATING:
+               spin_unlock(&cookie->lock);
+               _leave(" [look %x]", cookie->inval_counter);
+               return;
+
+       case FSCACHE_COOKIE_STATE_ACTIVE:
+               is_caching = fscache_begin_cookie_access(
+                       cookie, fscache_access_invalidate_cookie);
+               if (is_caching)
+                       __fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_INVALIDATING);
+               spin_unlock(&cookie->lock);
+               wake_up_cookie_state(cookie);
+
+               if (is_caching)
+                       fscache_queue_cookie(cookie, fscache_cookie_get_inval_work);
+               _leave(" [inv]");
+               return;
+       }
+}
+EXPORT_SYMBOL(__fscache_invalidate);
+
+/*
  * Generate a list of extant cookies in /proc/fs/fscache/cookies
  */
 static int fscache_cookies_seq_show(struct seq_file *m, void *v)
index ca938e0..7fb83d2 100644 (file)
@@ -105,6 +105,8 @@ extern atomic_t fscache_n_acquires;
 extern atomic_t fscache_n_acquires_ok;
 extern atomic_t fscache_n_acquires_oom;
 
+extern atomic_t fscache_n_invalidates;
+
 extern atomic_t fscache_n_relinquishes;
 extern atomic_t fscache_n_relinquishes_retire;
 extern atomic_t fscache_n_relinquishes_dropped;
index 5aa4bd9..cdbb672 100644 (file)
@@ -26,6 +26,8 @@ atomic_t fscache_n_acquires;
 atomic_t fscache_n_acquires_ok;
 atomic_t fscache_n_acquires_oom;
 
+atomic_t fscache_n_invalidates;
+
 atomic_t fscache_n_updates;
 EXPORT_SYMBOL(fscache_n_updates);
 
@@ -59,6 +61,9 @@ int fscache_stats_show(struct seq_file *m, void *v)
                   timer_pending(&fscache_cookie_lru_timer) ?
                   fscache_cookie_lru_timer.expires - jiffies : 0);
 
+       seq_printf(m, "Invals : n=%u\n",
+                  atomic_read(&fscache_n_invalidates));
+
        seq_printf(m, "Updates: n=%u\n",
                   atomic_read(&fscache_n_updates));
 
index ae6a759..1ad56bf 100644 (file)
@@ -64,6 +64,9 @@ struct fscache_cache_ops {
        /* Withdraw an object without any cookie access counts held */
        void (*withdraw_cookie)(struct fscache_cookie *cookie);
 
+       /* Invalidate an object */
+       bool (*invalidate_cookie)(struct fscache_cookie *cookie);
+
        /* Prepare to write to a live cache object */
        void (*prepare_to_write)(struct fscache_cookie *cookie);
 };
@@ -96,6 +99,7 @@ extern void fscache_put_cookie(struct fscache_cookie *cookie,
 extern void fscache_end_cookie_access(struct fscache_cookie *cookie,
                                      enum fscache_access_trace why);
 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);
 
 /**
index e6c321e..0f36d1f 100644 (file)
@@ -39,6 +39,8 @@ struct fscache_cookie;
 #define FSCACHE_ADV_WRITE_CACHE                0x00 /* Do cache if written to locally */
 #define FSCACHE_ADV_WRITE_NOCACHE      0x02 /* Don't cache if written to locally */
 
+#define FSCACHE_INVAL_DIO_WRITE                0x01 /* Invalidate due to DIO write */
+
 /*
  * Data object state.
  */
@@ -47,6 +49,7 @@ enum fscache_cookie_state {
        FSCACHE_COOKIE_STATE_LOOKING_UP,        /* The cache object is being looked up */
        FSCACHE_COOKIE_STATE_CREATING,          /* The cache object is being created */
        FSCACHE_COOKIE_STATE_ACTIVE,            /* The cache is active, readable and writable */
+       FSCACHE_COOKIE_STATE_INVALIDATING,      /* The cache is being invalidated */
        FSCACHE_COOKIE_STATE_FAILED,            /* The cache failed, withdraw to clear */
        FSCACHE_COOKIE_STATE_LRU_DISCARDING,    /* The cookie is being discarded by the LRU */
        FSCACHE_COOKIE_STATE_WITHDRAWING,       /* The cookie is being withdrawn */
@@ -153,6 +156,7 @@ extern struct fscache_cookie *__fscache_acquire_cookie(
 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);
 
 /**
  * fscache_acquire_volume - Register a volume as desiring caching services
@@ -327,4 +331,31 @@ void __fscache_update_cookie(struct fscache_cookie *cookie, const void *aux_data
        set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &cookie->flags);
 }
 
+/**
+ * fscache_invalidate - Notify cache that an object needs invalidation
+ * @cookie: The cookie representing the cache object
+ * @aux_data: The updated auxiliary data for the cookie (may be NULL)
+ * @size: The revised size of the object.
+ * @flags: Invalidation flags (FSCACHE_INVAL_*)
+ *
+ * Notify the cache that an object is needs to be invalidated and that it
+ * should abort any retrievals or stores it is doing on the cache.  This
+ * increments inval_counter on the cookie which can be used by the caller to
+ * reconsider I/O requests as they complete.
+ *
+ * If @flags has FSCACHE_INVAL_DIO_WRITE set, this indicates that this is due
+ * to a direct I/O write and will cause caching to be disabled on this cookie
+ * until it is completely unused.
+ *
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
+ * description.
+ */
+static inline
+void fscache_invalidate(struct fscache_cookie *cookie,
+                       const void *aux_data, loff_t size, unsigned int flags)
+{
+       if (fscache_cookie_enabled(cookie))
+               __fscache_invalidate(cookie, aux_data, size, flags);
+}
+
 #endif /* _LINUX_FSCACHE_H */
index 1ea22fc..5a46fde 100644 (file)
@@ -124,6 +124,7 @@ struct netfs_cache_resources {
        void                            *cache_priv;
        void                            *cache_priv2;
        unsigned int                    debug_id;       /* Cookie debug ID */
+       unsigned int                    inval_counter;  /* object->inval_counter at begin_op */
 };
 
 /*
index b0409b1..2947928 100644 (file)
@@ -51,6 +51,7 @@ enum fscache_cookie_trace {
        fscache_cookie_discard,
        fscache_cookie_get_end_access,
        fscache_cookie_get_hash_collision,
+       fscache_cookie_get_inval_work,
        fscache_cookie_get_lru,
        fscache_cookie_get_use_work,
        fscache_cookie_new_acquire,
@@ -73,6 +74,8 @@ enum fscache_access_trace {
        fscache_access_acquire_volume_end,
        fscache_access_cache_pin,
        fscache_access_cache_unpin,
+       fscache_access_invalidate_cookie,
+       fscache_access_invalidate_cookie_end,
        fscache_access_lookup_cookie,
        fscache_access_lookup_cookie_end,
        fscache_access_lookup_cookie_end_failed,
@@ -116,6 +119,7 @@ enum fscache_access_trace {
        EM(fscache_cookie_discard,              "DISCARD  ")            \
        EM(fscache_cookie_get_hash_collision,   "GET hcoll")            \
        EM(fscache_cookie_get_end_access,       "GQ  endac")            \
+       EM(fscache_cookie_get_inval_work,       "GQ  inval")            \
        EM(fscache_cookie_get_lru,              "GET lru  ")            \
        EM(fscache_cookie_get_use_work,         "GQ  use  ")            \
        EM(fscache_cookie_new_acquire,          "NEW acq  ")            \
@@ -137,6 +141,8 @@ enum fscache_access_trace {
        EM(fscache_access_acquire_volume_end,   "END   acq_vol")        \
        EM(fscache_access_cache_pin,            "PIN   cache  ")        \
        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_lookup_cookie,        "BEGIN lookup ")        \
        EM(fscache_access_lookup_cookie_end,    "END   lookup ")        \
        EM(fscache_access_lookup_cookie_end_failed,"END   lookupf")     \
@@ -385,6 +391,25 @@ TRACE_EVENT(fscache_relinquish,
                      __entry->n_active, __entry->flags, __entry->retire)
            );
 
+TRACE_EVENT(fscache_invalidate,
+           TP_PROTO(struct fscache_cookie *cookie, loff_t new_size),
+
+           TP_ARGS(cookie, new_size),
+
+           TP_STRUCT__entry(
+                   __field(unsigned int,               cookie          )
+                   __field(loff_t,                     new_size        )
+                            ),
+
+           TP_fast_assign(
+                   __entry->cookie     = cookie->debug_id;
+                   __entry->new_size   = new_size;
+                          ),
+
+           TP_printk("c=%08x sz=%llx",
+                     __entry->cookie, __entry->new_size)
+           );
+
 #endif /* _TRACE_FSCACHE_H */
 
 /* This part must be outside protection */