@@ -15,6 +15,8 @@ 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)
{
@@ -179,7 +181,7 @@ static struct fscache_volume *fscache_alloc_volume(const char *volume_key,
volume->cache = cache;
volume->coherency = coherency_data;
INIT_LIST_HEAD(&volume->proc_link);
- INIT_WORK(&volume->work, NULL /* PLACEHOLDER */);
+ INIT_WORK(&volume->work, fscache_create_volume_work);
refcount_set(&volume->ref, 1);
spin_lock_init(&volume->lock);
@@ -216,6 +218,58 @@ static struct fscache_volume *fscache_alloc_volume(const char *volume_key,
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_bit_unlock(FSCACHE_VOLUME_CREATING, &volume->flags);
+ wake_up_bit(&volume->flags, FSCACHE_VOLUME_CREATING);
+ 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.
*/
@@ -234,7 +288,7 @@ struct fscache_volume *__fscache_acquire_volume(const char *volume_key,
return NULL;
}
- // PLACEHOLDER: Create the volume if we have a cache available
+ fscache_create_volume(volume, false);
return volume;
}
EXPORT_SYMBOL(__fscache_acquire_volume);
@@ -281,7 +335,13 @@ static void fscache_free_volume(struct fscache_volume *volume)
struct fscache_cache *cache = volume->cache;
if (volume->cache_priv) {
- // PLACEHOLDER: Detach any attached cache
+ __fscache_begin_volume_access(volume, fscache_access_relinquish_volume);
+ if (volume->cache_priv) {
+ const struct fscache_cache_ops *ops = cache->ops;
+ if (ops->free_volume)
+ ops->free_volume(volume);
+ }
+ fscache_end_volume_access(volume, fscache_access_relinquish_volume_end);
}
down_write(&fscache_addremove_sem);
@@ -53,6 +53,12 @@ struct fscache_cache {
struct fscache_cache_ops {
/* name of cache provider */
const char *name;
+
+ /* Acquire a volume */
+ void (*acquire_volume)(struct fscache_volume *volume);
+
+ /* Free the cache's data attached to a volume */
+ void (*free_volume)(struct fscache_volume *volume);
};
static inline enum fscache_cache_state fscache_cache_state(const struct fscache_cache *cache)
@@ -63,8 +63,12 @@ enum fscache_cookie_trace {
};
enum fscache_access_trace {
+ fscache_access_acquire_volume,
+ fscache_access_acquire_volume_end,
fscache_access_cache_pin,
fscache_access_cache_unpin,
+ fscache_access_relinquish_volume,
+ fscache_access_relinquish_volume_end,
fscache_access_unlive,
};
@@ -94,7 +98,8 @@ enum fscache_access_trace {
EM(fscache_volume_put_hash_collision, "PUT hcoll") \
EM(fscache_volume_put_relinquish, "PUT relnq") \
EM(fscache_volume_see_create_work, "SEE creat") \
- E_(fscache_volume_see_hash_wake, "SEE hwake")
+ EM(fscache_volume_see_hash_wake, "SEE hwake") \
+ E_(fscache_volume_wait_create_work, "WAIT crea")
#define fscache_cookie_traces \
EM(fscache_cookie_collision, "*COLLIDE*") \
@@ -113,8 +118,12 @@ enum fscache_access_trace {
E_(fscache_cookie_see_work, "- work ")
#define fscache_access_traces \
+ EM(fscache_access_acquire_volume, "BEGIN acq_vol") \
+ 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_relinquish_volume, "BEGIN rlq_vol") \
+ EM(fscache_access_relinquish_volume_end,"END rlq_vol") \
E_(fscache_access_unlive, "END unlive ")
/*
Add cache methods to lookup, create and remove a volume. Looking up or creating the volume requires the cache pinning for access; freeing the volume requires the volume pinning for access. Signed-off-by: David Howells <dhowells@redhat.com> cc: linux-cachefs@redhat.com --- fs/fscache/volume.c | 66 ++++++++++++++++++++++++++++++++++++++-- include/linux/fscache-cache.h | 6 ++++ include/trace/events/fscache.h | 11 ++++++- 3 files changed, 79 insertions(+), 4 deletions(-)