netfs, fscache: Move fs/fscache/* into fs/netfs/
[linux-block.git] / fs / fscache / volume.c
diff --git a/fs/fscache/volume.c b/fs/fscache/volume.c
deleted file mode 100644 (file)
index cdf991b..0000000
+++ /dev/null
@@ -1,520 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* Volume-level cache cookie handling.
- *
- * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- */
-
-#define FSCACHE_DEBUG_LEVEL COOKIE
-#include <linux/export.h>
-#include <linux/slab.h>
-#include "internal.h"
-
-#define fscache_volume_hash_shift 10
-static struct hlist_bl_head fscache_volume_hash[1 << fscache_volume_hash_shift];
-static atomic_t fscache_volume_debug_id;
-static LIST_HEAD(fscache_volumes);
-
-static void fscache_create_volume_work(struct work_struct *work);
-
-struct fscache_volume *fscache_get_volume(struct fscache_volume *volume,
-                                         enum fscache_volume_trace where)
-{
-       int ref;
-
-       __refcount_inc(&volume->ref, &ref);
-       trace_fscache_volume(volume->debug_id, ref + 1, where);
-       return volume;
-}
-
-static void fscache_see_volume(struct fscache_volume *volume,
-                              enum fscache_volume_trace where)
-{
-       int ref = refcount_read(&volume->ref);
-
-       trace_fscache_volume(volume->debug_id, ref, where);
-}
-
-/*
- * Pin the cache behind a volume so that we can access it.
- */
-static void __fscache_begin_volume_access(struct fscache_volume *volume,
-                                         struct fscache_cookie *cookie,
-                                         enum fscache_access_trace why)
-{
-       int n_accesses;
-
-       n_accesses = atomic_inc_return(&volume->n_accesses);
-       smp_mb__after_atomic();
-       trace_fscache_access_volume(volume->debug_id, cookie ? cookie->debug_id : 0,
-                                   refcount_read(&volume->ref),
-                                   n_accesses, why);
-}
-
-/**
- * fscache_begin_volume_access - Pin a cache so a volume can be accessed
- * @volume: The volume cookie
- * @cookie: A datafile cookie for a tracing reference (or NULL)
- * @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 a volume and returns true if successful.  This works as follows:
- *
- *  (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.
- *
- * The datafile @cookie and the @why indicator are merely provided for tracing
- * purposes.
- */
-bool fscache_begin_volume_access(struct fscache_volume *volume,
-                                struct fscache_cookie *cookie,
-                                enum fscache_access_trace why)
-{
-       if (!fscache_cache_is_live(volume->cache))
-               return false;
-       __fscache_begin_volume_access(volume, cookie, why);
-       if (!fscache_cache_is_live(volume->cache)) {
-               fscache_end_volume_access(volume, cookie, fscache_access_unlive);
-               return false;
-       }
-       return true;
-}
-
-/**
- * fscache_end_volume_access - Unpin a cache at the end of an access.
- * @volume: The volume cookie
- * @cookie: A datafile cookie for a tracing reference (or NULL)
- * @why: An indication of the circumstances of the access for tracing
- *
- * Unpin a cache volume after we've accessed it.  The datafile @cookie and the
- * @why indicator are merely provided for tracing purposes.
- */
-void fscache_end_volume_access(struct fscache_volume *volume,
-                              struct fscache_cookie *cookie,
-                              enum fscache_access_trace why)
-{
-       int n_accesses;
-
-       smp_mb__before_atomic();
-       n_accesses = atomic_dec_return(&volume->n_accesses);
-       trace_fscache_access_volume(volume->debug_id, cookie ? cookie->debug_id : 0,
-                                   refcount_read(&volume->ref),
-                                   n_accesses, why);
-       if (n_accesses == 0)
-               wake_up_var(&volume->n_accesses);
-}
-EXPORT_SYMBOL(fscache_end_volume_access);
-
-static bool fscache_volume_same(const struct fscache_volume *a,
-                               const struct fscache_volume *b)
-{
-       size_t klen;
-
-       if (a->key_hash != b->key_hash ||
-           a->cache    != b->cache ||
-           a->key[0]   != b->key[0])
-               return false;
-
-       klen = round_up(a->key[0] + 1, sizeof(__le32));
-       return memcmp(a->key, b->key, klen) == 0;
-}
-
-static bool fscache_is_acquire_pending(struct fscache_volume *volume)
-{
-       return test_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &volume->flags);
-}
-
-static void fscache_wait_on_volume_collision(struct fscache_volume *candidate,
-                                            unsigned int collidee_debug_id)
-{
-       wait_on_bit_timeout(&candidate->flags, FSCACHE_VOLUME_ACQUIRE_PENDING,
-                           TASK_UNINTERRUPTIBLE, 20 * HZ);
-       if (fscache_is_acquire_pending(candidate)) {
-               pr_notice("Potential volume collision new=%08x old=%08x",
-                         candidate->debug_id, collidee_debug_id);
-               fscache_stat(&fscache_n_volumes_collision);
-               wait_on_bit(&candidate->flags, FSCACHE_VOLUME_ACQUIRE_PENDING,
-                           TASK_UNINTERRUPTIBLE);
-       }
-}
-
-/*
- * Attempt to insert the new volume into the hash.  If there's a collision, we
- * wait for the old volume to complete if it's being relinquished and an error
- * otherwise.
- */
-static bool fscache_hash_volume(struct fscache_volume *candidate)
-{
-       struct fscache_volume *cursor;
-       struct hlist_bl_head *h;
-       struct hlist_bl_node *p;
-       unsigned int bucket, collidee_debug_id = 0;
-
-       bucket = candidate->key_hash & (ARRAY_SIZE(fscache_volume_hash) - 1);
-       h = &fscache_volume_hash[bucket];
-
-       hlist_bl_lock(h);
-       hlist_bl_for_each_entry(cursor, p, h, hash_link) {
-               if (fscache_volume_same(candidate, cursor)) {
-                       if (!test_bit(FSCACHE_VOLUME_RELINQUISHED, &cursor->flags))
-                               goto collision;
-                       fscache_see_volume(cursor, fscache_volume_get_hash_collision);
-                       set_bit(FSCACHE_VOLUME_COLLIDED_WITH, &cursor->flags);
-                       set_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &candidate->flags);
-                       collidee_debug_id = cursor->debug_id;
-                       break;
-               }
-       }
-
-       hlist_bl_add_head(&candidate->hash_link, h);
-       hlist_bl_unlock(h);
-
-       if (fscache_is_acquire_pending(candidate))
-               fscache_wait_on_volume_collision(candidate, collidee_debug_id);
-       return true;
-
-collision:
-       fscache_see_volume(cursor, fscache_volume_collision);
-       hlist_bl_unlock(h);
-       return false;
-}
-
-/*
- * Allocate and initialise a volume representation cookie.
- */
-static struct fscache_volume *fscache_alloc_volume(const char *volume_key,
-                                                  const char *cache_name,
-                                                  const void *coherency_data,
-                                                  size_t coherency_len)
-{
-       struct fscache_volume *volume;
-       struct fscache_cache *cache;
-       size_t klen, hlen;
-       u8 *key;
-
-       klen = strlen(volume_key);
-       if (klen > NAME_MAX)
-               return NULL;
-
-       if (!coherency_data)
-               coherency_len = 0;
-
-       cache = fscache_lookup_cache(cache_name, false);
-       if (IS_ERR(cache))
-               return NULL;
-
-       volume = kzalloc(struct_size(volume, coherency, coherency_len),
-                        GFP_KERNEL);
-       if (!volume)
-               goto err_cache;
-
-       volume->cache = cache;
-       volume->coherency_len = coherency_len;
-       if (coherency_data)
-               memcpy(volume->coherency, coherency_data, coherency_len);
-       INIT_LIST_HEAD(&volume->proc_link);
-       INIT_WORK(&volume->work, fscache_create_volume_work);
-       refcount_set(&volume->ref, 1);
-       spin_lock_init(&volume->lock);
-
-       /* Stick the length on the front of the key and pad it out to make
-        * hashing easier.
-        */
-       hlen = round_up(1 + klen + 1, sizeof(__le32));
-       key = kzalloc(hlen, GFP_KERNEL);
-       if (!key)
-               goto err_vol;
-       key[0] = klen;
-       memcpy(key + 1, volume_key, klen);
-
-       volume->key = key;
-       volume->key_hash = fscache_hash(0, key, hlen);
-
-       volume->debug_id = atomic_inc_return(&fscache_volume_debug_id);
-       down_write(&fscache_addremove_sem);
-       atomic_inc(&cache->n_volumes);
-       list_add_tail(&volume->proc_link, &fscache_volumes);
-       fscache_see_volume(volume, fscache_volume_new_acquire);
-       fscache_stat(&fscache_n_volumes);
-       up_write(&fscache_addremove_sem);
-       _leave(" = v=%x", volume->debug_id);
-       return volume;
-
-err_vol:
-       kfree(volume);
-err_cache:
-       fscache_put_cache(cache, fscache_cache_put_alloc_volume);
-       fscache_stat(&fscache_n_volumes_nomem);
-       return NULL;
-}
-
-/*
- * Create a volume's representation on disk.  Have a volume ref and a cache
- * access we have to release.
- */
-static void fscache_create_volume_work(struct work_struct *work)
-{
-       const struct fscache_cache_ops *ops;
-       struct fscache_volume *volume =
-               container_of(work, struct fscache_volume, work);
-
-       fscache_see_volume(volume, fscache_volume_see_create_work);
-
-       ops = volume->cache->ops;
-       if (ops->acquire_volume)
-               ops->acquire_volume(volume);
-       fscache_end_cache_access(volume->cache,
-                                fscache_access_acquire_volume_end);
-
-       clear_and_wake_up_bit(FSCACHE_VOLUME_CREATING, &volume->flags);
-       fscache_put_volume(volume, fscache_volume_put_create_work);
-}
-
-/*
- * Dispatch a worker thread to create a volume's representation on disk.
- */
-void fscache_create_volume(struct fscache_volume *volume, bool wait)
-{
-       if (test_and_set_bit(FSCACHE_VOLUME_CREATING, &volume->flags))
-               goto maybe_wait;
-       if (volume->cache_priv)
-               goto no_wait; /* We raced */
-       if (!fscache_begin_cache_access(volume->cache,
-                                       fscache_access_acquire_volume))
-               goto no_wait;
-
-       fscache_get_volume(volume, fscache_volume_get_create_work);
-       if (!schedule_work(&volume->work))
-               fscache_put_volume(volume, fscache_volume_put_create_work);
-
-maybe_wait:
-       if (wait) {
-               fscache_see_volume(volume, fscache_volume_wait_create_work);
-               wait_on_bit(&volume->flags, FSCACHE_VOLUME_CREATING,
-                           TASK_UNINTERRUPTIBLE);
-       }
-       return;
-no_wait:
-       clear_bit_unlock(FSCACHE_VOLUME_CREATING, &volume->flags);
-       wake_up_bit(&volume->flags, FSCACHE_VOLUME_CREATING);
-}
-
-/*
- * Acquire a volume representation cookie and link it to a (proposed) cache.
- */
-struct fscache_volume *__fscache_acquire_volume(const char *volume_key,
-                                               const char *cache_name,
-                                               const void *coherency_data,
-                                               size_t coherency_len)
-{
-       struct fscache_volume *volume;
-
-       volume = fscache_alloc_volume(volume_key, cache_name,
-                                     coherency_data, coherency_len);
-       if (!volume)
-               return ERR_PTR(-ENOMEM);
-
-       if (!fscache_hash_volume(volume)) {
-               fscache_put_volume(volume, fscache_volume_put_hash_collision);
-               return ERR_PTR(-EBUSY);
-       }
-
-       fscache_create_volume(volume, false);
-       return volume;
-}
-EXPORT_SYMBOL(__fscache_acquire_volume);
-
-static void fscache_wake_pending_volume(struct fscache_volume *volume,
-                                       struct hlist_bl_head *h)
-{
-       struct fscache_volume *cursor;
-       struct hlist_bl_node *p;
-
-       hlist_bl_for_each_entry(cursor, p, h, hash_link) {
-               if (fscache_volume_same(cursor, volume)) {
-                       fscache_see_volume(cursor, fscache_volume_see_hash_wake);
-                       clear_and_wake_up_bit(FSCACHE_VOLUME_ACQUIRE_PENDING,
-                                             &cursor->flags);
-                       return;
-               }
-       }
-}
-
-/*
- * Remove a volume cookie from the hash table.
- */
-static void fscache_unhash_volume(struct fscache_volume *volume)
-{
-       struct hlist_bl_head *h;
-       unsigned int bucket;
-
-       bucket = volume->key_hash & (ARRAY_SIZE(fscache_volume_hash) - 1);
-       h = &fscache_volume_hash[bucket];
-
-       hlist_bl_lock(h);
-       hlist_bl_del(&volume->hash_link);
-       if (test_bit(FSCACHE_VOLUME_COLLIDED_WITH, &volume->flags))
-               fscache_wake_pending_volume(volume, h);
-       hlist_bl_unlock(h);
-}
-
-/*
- * Drop a cache's volume attachments.
- */
-static void fscache_free_volume(struct fscache_volume *volume)
-{
-       struct fscache_cache *cache = volume->cache;
-
-       if (volume->cache_priv) {
-               __fscache_begin_volume_access(volume, NULL,
-                                             fscache_access_relinquish_volume);
-               if (volume->cache_priv)
-                       cache->ops->free_volume(volume);
-               fscache_end_volume_access(volume, NULL,
-                                         fscache_access_relinquish_volume_end);
-       }
-
-       down_write(&fscache_addremove_sem);
-       list_del_init(&volume->proc_link);
-       atomic_dec(&volume->cache->n_volumes);
-       up_write(&fscache_addremove_sem);
-
-       if (!hlist_bl_unhashed(&volume->hash_link))
-               fscache_unhash_volume(volume);
-
-       trace_fscache_volume(volume->debug_id, 0, fscache_volume_free);
-       kfree(volume->key);
-       kfree(volume);
-       fscache_stat_d(&fscache_n_volumes);
-       fscache_put_cache(cache, fscache_cache_put_volume);
-}
-
-/*
- * Drop a reference to a volume cookie.
- */
-void fscache_put_volume(struct fscache_volume *volume,
-                       enum fscache_volume_trace where)
-{
-       if (volume) {
-               unsigned int debug_id = volume->debug_id;
-               bool zero;
-               int ref;
-
-               zero = __refcount_dec_and_test(&volume->ref, &ref);
-               trace_fscache_volume(debug_id, ref - 1, where);
-               if (zero)
-                       fscache_free_volume(volume);
-       }
-}
-
-/*
- * Relinquish a volume representation cookie.
- */
-void __fscache_relinquish_volume(struct fscache_volume *volume,
-                                const void *coherency_data,
-                                bool invalidate)
-{
-       if (WARN_ON(test_and_set_bit(FSCACHE_VOLUME_RELINQUISHED, &volume->flags)))
-               return;
-
-       if (invalidate) {
-               set_bit(FSCACHE_VOLUME_INVALIDATE, &volume->flags);
-       } else if (coherency_data) {
-               memcpy(volume->coherency, coherency_data, volume->coherency_len);
-       }
-
-       fscache_put_volume(volume, fscache_volume_put_relinquish);
-}
-EXPORT_SYMBOL(__fscache_relinquish_volume);
-
-/**
- * fscache_withdraw_volume - Withdraw a volume from being cached
- * @volume: Volume cookie
- *
- * Withdraw a cache volume from service, waiting for all accesses to complete
- * before returning.
- */
-void fscache_withdraw_volume(struct fscache_volume *volume)
-{
-       int n_accesses;
-
-       _debug("withdraw V=%x", volume->debug_id);
-
-       /* Allow wakeups on dec-to-0 */
-       n_accesses = atomic_dec_return(&volume->n_accesses);
-       trace_fscache_access_volume(volume->debug_id, 0,
-                                   refcount_read(&volume->ref),
-                                   n_accesses, fscache_access_cache_unpin);
-
-       wait_var_event(&volume->n_accesses,
-                      atomic_read(&volume->n_accesses) == 0);
-}
-EXPORT_SYMBOL(fscache_withdraw_volume);
-
-#ifdef CONFIG_PROC_FS
-/*
- * Generate a list of volumes in /proc/fs/fscache/volumes
- */
-static int fscache_volumes_seq_show(struct seq_file *m, void *v)
-{
-       struct fscache_volume *volume;
-
-       if (v == &fscache_volumes) {
-               seq_puts(m,
-                        "VOLUME   REF   nCOOK ACC FL CACHE           KEY\n"
-                        "======== ===== ===== === == =============== ================\n");
-               return 0;
-       }
-
-       volume = list_entry(v, struct fscache_volume, proc_link);
-       seq_printf(m,
-                  "%08x %5d %5d %3d %02lx %-15.15s %s\n",
-                  volume->debug_id,
-                  refcount_read(&volume->ref),
-                  atomic_read(&volume->n_cookies),
-                  atomic_read(&volume->n_accesses),
-                  volume->flags,
-                  volume->cache->name ?: "-",
-                  volume->key + 1);
-       return 0;
-}
-
-static void *fscache_volumes_seq_start(struct seq_file *m, loff_t *_pos)
-       __acquires(&fscache_addremove_sem)
-{
-       down_read(&fscache_addremove_sem);
-       return seq_list_start_head(&fscache_volumes, *_pos);
-}
-
-static void *fscache_volumes_seq_next(struct seq_file *m, void *v, loff_t *_pos)
-{
-       return seq_list_next(v, &fscache_volumes, _pos);
-}
-
-static void fscache_volumes_seq_stop(struct seq_file *m, void *v)
-       __releases(&fscache_addremove_sem)
-{
-       up_read(&fscache_addremove_sem);
-}
-
-const struct seq_operations fscache_volumes_seq_ops = {
-       .start  = fscache_volumes_seq_start,
-       .next   = fscache_volumes_seq_next,
-       .stop   = fscache_volumes_seq_stop,
-       .show   = fscache_volumes_seq_show,
-};
-#endif /* CONFIG_PROC_FS */