1fe2140e2SDavid Howells // SPDX-License-Identifier: GPL-2.0-or-later
2fe2140e2SDavid Howells /* Volume handling.
3fe2140e2SDavid Howells *
4fe2140e2SDavid Howells * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
5fe2140e2SDavid Howells * Written by David Howells (dhowells@redhat.com)
6fe2140e2SDavid Howells */
7fe2140e2SDavid Howells
8fe2140e2SDavid Howells #include <linux/fs.h>
9fe2140e2SDavid Howells #include <linux/slab.h>
10fe2140e2SDavid Howells #include "internal.h"
11fe2140e2SDavid Howells #include <trace/events/fscache.h>
12fe2140e2SDavid Howells
13fe2140e2SDavid Howells /*
14fe2140e2SDavid Howells * Allocate and set up a volume representation. We make sure all the fanout
15fe2140e2SDavid Howells * directories are created and pinned.
16fe2140e2SDavid Howells */
cachefiles_acquire_volume(struct fscache_volume * vcookie)17fe2140e2SDavid Howells void cachefiles_acquire_volume(struct fscache_volume *vcookie)
18fe2140e2SDavid Howells {
19fe2140e2SDavid Howells struct cachefiles_volume *volume;
20fe2140e2SDavid Howells struct cachefiles_cache *cache = vcookie->cache->cache_priv;
21fe2140e2SDavid Howells const struct cred *saved_cred;
22fe2140e2SDavid Howells struct dentry *vdentry, *fan;
23fe2140e2SDavid Howells size_t len;
24fe2140e2SDavid Howells char *name;
25*32e15003SDavid Howells bool is_new = false;
26*32e15003SDavid Howells int ret, n_accesses, i;
27fe2140e2SDavid Howells
28fe2140e2SDavid Howells _enter("");
29fe2140e2SDavid Howells
30fe2140e2SDavid Howells volume = kzalloc(sizeof(struct cachefiles_volume), GFP_KERNEL);
31fe2140e2SDavid Howells if (!volume)
32fe2140e2SDavid Howells return;
33fe2140e2SDavid Howells volume->vcookie = vcookie;
34fe2140e2SDavid Howells volume->cache = cache;
35fe2140e2SDavid Howells INIT_LIST_HEAD(&volume->cache_link);
36fe2140e2SDavid Howells
37fe2140e2SDavid Howells cachefiles_begin_secure(cache, &saved_cred);
38fe2140e2SDavid Howells
39fe2140e2SDavid Howells len = vcookie->key[0];
40fe2140e2SDavid Howells name = kmalloc(len + 3, GFP_NOFS);
41fe2140e2SDavid Howells if (!name)
42fe2140e2SDavid Howells goto error_vol;
43fe2140e2SDavid Howells name[0] = 'I';
44fe2140e2SDavid Howells memcpy(name + 1, vcookie->key + 1, len);
45fe2140e2SDavid Howells name[len + 1] = 0;
46fe2140e2SDavid Howells
47*32e15003SDavid Howells retry:
48*32e15003SDavid Howells vdentry = cachefiles_get_directory(cache, cache->store, name, &is_new);
49fe2140e2SDavid Howells if (IS_ERR(vdentry))
50fe2140e2SDavid Howells goto error_name;
51fe2140e2SDavid Howells volume->dentry = vdentry;
52fe2140e2SDavid Howells
53*32e15003SDavid Howells if (is_new) {
54*32e15003SDavid Howells if (!cachefiles_set_volume_xattr(volume))
55*32e15003SDavid Howells goto error_dir;
56*32e15003SDavid Howells } else {
57*32e15003SDavid Howells ret = cachefiles_check_volume_xattr(volume);
58*32e15003SDavid Howells if (ret < 0) {
59*32e15003SDavid Howells if (ret != -ESTALE)
60*32e15003SDavid Howells goto error_dir;
61*32e15003SDavid Howells inode_lock_nested(d_inode(cache->store), I_MUTEX_PARENT);
62*32e15003SDavid Howells cachefiles_bury_object(cache, NULL, cache->store, vdentry,
63*32e15003SDavid Howells FSCACHE_VOLUME_IS_WEIRD);
64*32e15003SDavid Howells cachefiles_put_directory(volume->dentry);
65*32e15003SDavid Howells cond_resched();
66*32e15003SDavid Howells goto retry;
67*32e15003SDavid Howells }
68*32e15003SDavid Howells }
69*32e15003SDavid Howells
70fe2140e2SDavid Howells for (i = 0; i < 256; i++) {
71fe2140e2SDavid Howells sprintf(name, "@%02x", i);
72fe2140e2SDavid Howells fan = cachefiles_get_directory(cache, vdentry, name, NULL);
73fe2140e2SDavid Howells if (IS_ERR(fan))
74fe2140e2SDavid Howells goto error_fan;
75fe2140e2SDavid Howells volume->fanout[i] = fan;
76fe2140e2SDavid Howells }
77fe2140e2SDavid Howells
78fe2140e2SDavid Howells cachefiles_end_secure(cache, saved_cred);
79fe2140e2SDavid Howells
80fe2140e2SDavid Howells vcookie->cache_priv = volume;
81fe2140e2SDavid Howells n_accesses = atomic_inc_return(&vcookie->n_accesses); /* Stop wakeups on dec-to-0 */
82fe2140e2SDavid Howells trace_fscache_access_volume(vcookie->debug_id, 0,
83fe2140e2SDavid Howells refcount_read(&vcookie->ref),
84fe2140e2SDavid Howells n_accesses, fscache_access_cache_pin);
85fe2140e2SDavid Howells
86fe2140e2SDavid Howells spin_lock(&cache->object_list_lock);
87fe2140e2SDavid Howells list_add(&volume->cache_link, &volume->cache->volumes);
88fe2140e2SDavid Howells spin_unlock(&cache->object_list_lock);
89fe2140e2SDavid Howells
90fe2140e2SDavid Howells kfree(name);
91fe2140e2SDavid Howells return;
92fe2140e2SDavid Howells
93fe2140e2SDavid Howells error_fan:
94fe2140e2SDavid Howells for (i = 0; i < 256; i++)
95fe2140e2SDavid Howells cachefiles_put_directory(volume->fanout[i]);
96*32e15003SDavid Howells error_dir:
97fe2140e2SDavid Howells cachefiles_put_directory(volume->dentry);
98fe2140e2SDavid Howells error_name:
99fe2140e2SDavid Howells kfree(name);
100fe2140e2SDavid Howells error_vol:
101fe2140e2SDavid Howells kfree(volume);
102fe2140e2SDavid Howells cachefiles_end_secure(cache, saved_cred);
103fe2140e2SDavid Howells }
104fe2140e2SDavid Howells
105fe2140e2SDavid Howells /*
106fe2140e2SDavid Howells * Release a volume representation.
107fe2140e2SDavid Howells */
__cachefiles_free_volume(struct cachefiles_volume * volume)108fe2140e2SDavid Howells static void __cachefiles_free_volume(struct cachefiles_volume *volume)
109fe2140e2SDavid Howells {
110fe2140e2SDavid Howells int i;
111fe2140e2SDavid Howells
112fe2140e2SDavid Howells _enter("");
113fe2140e2SDavid Howells
114fe2140e2SDavid Howells volume->vcookie->cache_priv = NULL;
115fe2140e2SDavid Howells
116fe2140e2SDavid Howells for (i = 0; i < 256; i++)
117fe2140e2SDavid Howells cachefiles_put_directory(volume->fanout[i]);
118fe2140e2SDavid Howells cachefiles_put_directory(volume->dentry);
119fe2140e2SDavid Howells kfree(volume);
120fe2140e2SDavid Howells }
121fe2140e2SDavid Howells
cachefiles_free_volume(struct fscache_volume * vcookie)122fe2140e2SDavid Howells void cachefiles_free_volume(struct fscache_volume *vcookie)
123fe2140e2SDavid Howells {
124fe2140e2SDavid Howells struct cachefiles_volume *volume = vcookie->cache_priv;
125fe2140e2SDavid Howells
126fe2140e2SDavid Howells if (volume) {
127fe2140e2SDavid Howells spin_lock(&volume->cache->object_list_lock);
128fe2140e2SDavid Howells list_del_init(&volume->cache_link);
129fe2140e2SDavid Howells spin_unlock(&volume->cache->object_list_lock);
130fe2140e2SDavid Howells __cachefiles_free_volume(volume);
131fe2140e2SDavid Howells }
132fe2140e2SDavid Howells }
133fe2140e2SDavid Howells
cachefiles_withdraw_volume(struct cachefiles_volume * volume)134fe2140e2SDavid Howells void cachefiles_withdraw_volume(struct cachefiles_volume *volume)
135fe2140e2SDavid Howells {
136*32e15003SDavid Howells cachefiles_set_volume_xattr(volume);
137fe2140e2SDavid Howells __cachefiles_free_volume(volume);
138fe2140e2SDavid Howells }
139