Skip to content

Commit 1f08c92

Browse files
committed
cachefiles: Implement backing file wrangling
Implement the wrangling of backing files, including the following pieces: (1) Lookup and creation of a file on disk, using a tmpfile if the file isn't yet present. The file is then opened, sized for DIO and the file handle is attached to the cachefiles_object struct. The inode is marked to indicate that it's in use by a kernel service. (2) Invalidation of an object, creating a tmpfile and switching the file pointer in the cachefiles object. (3) Committing a file to disk, including setting the coherency xattr on it and, if necessary, creating a hard link to it. Note that this would be a good place to use Omar Sandoval's vfs_link() with AT_LINK_REPLACE[1] as I may have to unlink an old file before I can link a tmpfile into place. (4) Withdrawal of open objects when a cache is being withdrawn or a cookie is relinquished. This involves committing or discarding the file. Changes ======= ver #2: - Fix logging of wrong error[1]. 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/20211203094950.GA2480@kili/ [1] Link: https://lore.kernel.org/r/163819644097.215744.4505389616742411239.stgit@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906949512.143852.14222856795032602080.stgit@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/163967158526.1823006.17482695321424642675.stgit@warthog.procyon.org.uk/ # v3 Link: https://lore.kernel.org/r/164021557060.640689.16373541458119269871.stgit@warthog.procyon.org.uk/ # v4
1 parent 07a90e9 commit 1f08c92

File tree

5 files changed

+619
-1
lines changed

5 files changed

+619
-1
lines changed

fs/cachefiles/cache.c

+31-1
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,36 @@ int cachefiles_has_space(struct cachefiles_cache *cache,
262262
return ret;
263263
}
264264

265+
/*
266+
* Mark all the objects as being out of service and queue them all for cleanup.
267+
*/
268+
static void cachefiles_withdraw_objects(struct cachefiles_cache *cache)
269+
{
270+
struct cachefiles_object *object;
271+
unsigned int count = 0;
272+
273+
_enter("");
274+
275+
spin_lock(&cache->object_list_lock);
276+
277+
while (!list_empty(&cache->object_list)) {
278+
object = list_first_entry(&cache->object_list,
279+
struct cachefiles_object, cache_link);
280+
cachefiles_see_object(object, cachefiles_obj_see_withdrawal);
281+
list_del_init(&object->cache_link);
282+
fscache_withdraw_cookie(object->cookie);
283+
count++;
284+
if ((count & 63) == 0) {
285+
spin_unlock(&cache->object_list_lock);
286+
cond_resched();
287+
spin_lock(&cache->object_list_lock);
288+
}
289+
}
290+
291+
spin_unlock(&cache->object_list_lock);
292+
_leave(" [%u objs]", count);
293+
}
294+
265295
/*
266296
* Withdraw volumes.
267297
*/
@@ -326,7 +356,7 @@ void cachefiles_withdraw_cache(struct cachefiles_cache *cache)
326356
/* we now have to destroy all the active objects pertaining to this
327357
* cache - which we do by passing them off to thread pool to be
328358
* disposed of */
329-
// PLACEHOLDER: Withdraw objects
359+
cachefiles_withdraw_objects(cache);
330360
fscache_wait_for_objects(fscache);
331361

332362
cachefiles_withdraw_volumes(cache);

fs/cachefiles/daemon.c

+1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ static int cachefiles_daemon_open(struct inode *inode, struct file *file)
106106
mutex_init(&cache->daemon_mutex);
107107
init_waitqueue_head(&cache->daemon_pollwq);
108108
INIT_LIST_HEAD(&cache->volumes);
109+
INIT_LIST_HEAD(&cache->object_list);
109110
spin_lock_init(&cache->object_list_lock);
110111

111112
/* set default caching limits

fs/cachefiles/interface.c

+260
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,268 @@ void cachefiles_put_object(struct cachefiles_object *object,
9999
_leave("");
100100
}
101101

102+
/*
103+
* Adjust the size of a cache file if necessary to match the DIO size. We keep
104+
* the EOF marker a multiple of DIO blocks so that we don't fall back to doing
105+
* non-DIO for a partial block straddling the EOF, but we also have to be
106+
* careful of someone expanding the file and accidentally accreting the
107+
* padding.
108+
*/
109+
static int cachefiles_adjust_size(struct cachefiles_object *object)
110+
{
111+
struct iattr newattrs;
112+
struct file *file = object->file;
113+
uint64_t ni_size;
114+
loff_t oi_size;
115+
int ret;
116+
117+
ni_size = object->cookie->object_size;
118+
ni_size = round_up(ni_size, CACHEFILES_DIO_BLOCK_SIZE);
119+
120+
_enter("{OBJ%x},[%llu]",
121+
object->debug_id, (unsigned long long) ni_size);
122+
123+
if (!file)
124+
return -ENOBUFS;
125+
126+
oi_size = i_size_read(file_inode(file));
127+
if (oi_size == ni_size)
128+
return 0;
129+
130+
inode_lock(file_inode(file));
131+
132+
/* if there's an extension to a partial page at the end of the backing
133+
* file, we need to discard the partial page so that we pick up new
134+
* data after it */
135+
if (oi_size & ~PAGE_MASK && ni_size > oi_size) {
136+
_debug("discard tail %llx", oi_size);
137+
newattrs.ia_valid = ATTR_SIZE;
138+
newattrs.ia_size = oi_size & PAGE_MASK;
139+
ret = cachefiles_inject_remove_error();
140+
if (ret == 0)
141+
ret = notify_change(&init_user_ns, file->f_path.dentry,
142+
&newattrs, NULL);
143+
if (ret < 0)
144+
goto truncate_failed;
145+
}
146+
147+
newattrs.ia_valid = ATTR_SIZE;
148+
newattrs.ia_size = ni_size;
149+
ret = cachefiles_inject_write_error();
150+
if (ret == 0)
151+
ret = notify_change(&init_user_ns, file->f_path.dentry,
152+
&newattrs, NULL);
153+
154+
truncate_failed:
155+
inode_unlock(file_inode(file));
156+
157+
if (ret < 0)
158+
trace_cachefiles_io_error(NULL, file_inode(file), ret,
159+
cachefiles_trace_notify_change_error);
160+
if (ret == -EIO) {
161+
cachefiles_io_error_obj(object, "Size set failed");
162+
ret = -ENOBUFS;
163+
}
164+
165+
_leave(" = %d", ret);
166+
return ret;
167+
}
168+
169+
/*
170+
* Attempt to look up the nominated node in this cache
171+
*/
172+
static bool cachefiles_lookup_cookie(struct fscache_cookie *cookie)
173+
{
174+
struct cachefiles_object *object;
175+
struct cachefiles_cache *cache = cookie->volume->cache->cache_priv;
176+
const struct cred *saved_cred;
177+
bool success;
178+
179+
object = cachefiles_alloc_object(cookie);
180+
if (!object)
181+
goto fail;
182+
183+
_enter("{OBJ%x}", object->debug_id);
184+
185+
if (!cachefiles_cook_key(object))
186+
goto fail_put;
187+
188+
cookie->cache_priv = object;
189+
190+
cachefiles_begin_secure(cache, &saved_cred);
191+
192+
success = cachefiles_look_up_object(object);
193+
if (!success)
194+
goto fail_withdraw;
195+
196+
cachefiles_see_object(object, cachefiles_obj_see_lookup_cookie);
197+
198+
spin_lock(&cache->object_list_lock);
199+
list_add(&object->cache_link, &cache->object_list);
200+
spin_unlock(&cache->object_list_lock);
201+
cachefiles_adjust_size(object);
202+
203+
cachefiles_end_secure(cache, saved_cred);
204+
_leave(" = t");
205+
return true;
206+
207+
fail_withdraw:
208+
cachefiles_end_secure(cache, saved_cred);
209+
cachefiles_see_object(object, cachefiles_obj_see_lookup_failed);
210+
fscache_caching_failed(cookie);
211+
_debug("failed c=%08x o=%08x", cookie->debug_id, object->debug_id);
212+
/* The caller holds an access count on the cookie, so we need them to
213+
* drop it before we can withdraw the object.
214+
*/
215+
return false;
216+
217+
fail_put:
218+
cachefiles_put_object(object, cachefiles_obj_put_alloc_fail);
219+
fail:
220+
return false;
221+
}
222+
223+
/*
224+
* Commit changes to the object as we drop it.
225+
*/
226+
static void cachefiles_commit_object(struct cachefiles_object *object,
227+
struct cachefiles_cache *cache)
228+
{
229+
bool update = false;
230+
231+
if (test_and_clear_bit(FSCACHE_COOKIE_LOCAL_WRITE, &object->cookie->flags))
232+
update = true;
233+
if (test_and_clear_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &object->cookie->flags))
234+
update = true;
235+
if (update)
236+
cachefiles_set_object_xattr(object);
237+
238+
if (test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags))
239+
cachefiles_commit_tmpfile(cache, object);
240+
}
241+
242+
/*
243+
* Finalise and object and close the VFS structs that we have.
244+
*/
245+
static void cachefiles_clean_up_object(struct cachefiles_object *object,
246+
struct cachefiles_cache *cache)
247+
{
248+
if (test_bit(FSCACHE_COOKIE_RETIRED, &object->cookie->flags)) {
249+
if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) {
250+
cachefiles_see_object(object, cachefiles_obj_see_clean_delete);
251+
_debug("- inval object OBJ%x", object->debug_id);
252+
cachefiles_delete_object(object, FSCACHE_OBJECT_WAS_RETIRED);
253+
} else {
254+
cachefiles_see_object(object, cachefiles_obj_see_clean_drop_tmp);
255+
_debug("- inval object OBJ%x tmpfile", object->debug_id);
256+
}
257+
} else {
258+
cachefiles_see_object(object, cachefiles_obj_see_clean_commit);
259+
cachefiles_commit_object(object, cache);
260+
}
261+
262+
cachefiles_unmark_inode_in_use(object, object->file);
263+
if (object->file) {
264+
fput(object->file);
265+
object->file = NULL;
266+
}
267+
}
268+
269+
/*
270+
* Withdraw caching for a cookie.
271+
*/
272+
static void cachefiles_withdraw_cookie(struct fscache_cookie *cookie)
273+
{
274+
struct cachefiles_object *object = cookie->cache_priv;
275+
struct cachefiles_cache *cache = object->volume->cache;
276+
const struct cred *saved_cred;
277+
278+
_enter("o=%x", object->debug_id);
279+
cachefiles_see_object(object, cachefiles_obj_see_withdraw_cookie);
280+
281+
if (!list_empty(&object->cache_link)) {
282+
spin_lock(&cache->object_list_lock);
283+
cachefiles_see_object(object, cachefiles_obj_see_withdrawal);
284+
list_del_init(&object->cache_link);
285+
spin_unlock(&cache->object_list_lock);
286+
}
287+
288+
if (object->file) {
289+
cachefiles_begin_secure(cache, &saved_cred);
290+
cachefiles_clean_up_object(object, cache);
291+
cachefiles_end_secure(cache, saved_cred);
292+
}
293+
294+
cookie->cache_priv = NULL;
295+
cachefiles_put_object(object, cachefiles_obj_put_detach);
296+
}
297+
298+
/*
299+
* Invalidate the storage associated with a cookie.
300+
*/
301+
static bool cachefiles_invalidate_cookie(struct fscache_cookie *cookie)
302+
{
303+
struct cachefiles_object *object = cookie->cache_priv;
304+
struct file *new_file, *old_file;
305+
bool old_tmpfile;
306+
307+
_enter("o=%x,[%llu]", object->debug_id, object->cookie->object_size);
308+
309+
old_tmpfile = test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags);
310+
311+
if (!object->file) {
312+
fscache_resume_after_invalidation(cookie);
313+
_leave(" = t [light]");
314+
return true;
315+
}
316+
317+
new_file = cachefiles_create_tmpfile(object);
318+
if (IS_ERR(new_file))
319+
goto failed;
320+
321+
/* Substitute the VFS target */
322+
_debug("sub");
323+
spin_lock(&object->lock);
324+
325+
old_file = object->file;
326+
object->file = new_file;
327+
object->content_info = CACHEFILES_CONTENT_NO_DATA;
328+
set_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags);
329+
set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &object->cookie->flags);
330+
331+
spin_unlock(&object->lock);
332+
_debug("subbed");
333+
334+
/* Allow I/O to take place again */
335+
fscache_resume_after_invalidation(cookie);
336+
337+
if (old_file) {
338+
if (!old_tmpfile) {
339+
struct cachefiles_volume *volume = object->volume;
340+
struct dentry *fan = volume->fanout[(u8)cookie->key_hash];
341+
342+
inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
343+
cachefiles_bury_object(volume->cache, object, fan,
344+
old_file->f_path.dentry,
345+
FSCACHE_OBJECT_INVALIDATED);
346+
}
347+
fput(old_file);
348+
}
349+
350+
_leave(" = t");
351+
return true;
352+
353+
failed:
354+
_leave(" = f");
355+
return false;
356+
}
357+
102358
const struct fscache_cache_ops cachefiles_cache_ops = {
103359
.name = "cachefiles",
104360
.acquire_volume = cachefiles_acquire_volume,
105361
.free_volume = cachefiles_free_volume,
362+
.lookup_cookie = cachefiles_lookup_cookie,
363+
.withdraw_cookie = cachefiles_withdraw_cookie,
364+
.invalidate_cookie = cachefiles_invalidate_cookie,
365+
.prepare_to_write = cachefiles_prepare_to_write,
106366
};

fs/cachefiles/internal.h

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include <linux/cred.h>
1717
#include <linux/security.h>
1818

19+
#define CACHEFILES_DIO_BLOCK_SIZE 4096
20+
1921
struct cachefiles_cache;
2022
struct cachefiles_object;
2123

@@ -68,6 +70,7 @@ struct cachefiles_cache {
6870
struct dentry *graveyard; /* directory into which dead objects go */
6971
struct file *cachefilesd; /* manager daemon handle */
7072
struct list_head volumes; /* List of volume objects */
73+
struct list_head object_list; /* List of active objects */
7174
spinlock_t object_list_lock; /* Lock for volumes and object_list */
7275
const struct cred *cache_cred; /* security override for accessing cache */
7376
struct mutex daemon_mutex; /* command serialisation mutex */
@@ -194,6 +197,9 @@ extern int cachefiles_bury_object(struct cachefiles_cache *cache,
194197
struct dentry *dir,
195198
struct dentry *rep,
196199
enum fscache_why_object_killed why);
200+
extern int cachefiles_delete_object(struct cachefiles_object *object,
201+
enum fscache_why_object_killed why);
202+
extern bool cachefiles_look_up_object(struct cachefiles_object *object);
197203
extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
198204
struct dentry *dir,
199205
const char *name,
@@ -205,6 +211,9 @@ extern int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir,
205211

206212
extern int cachefiles_check_in_use(struct cachefiles_cache *cache,
207213
struct dentry *dir, char *filename);
214+
extern struct file *cachefiles_create_tmpfile(struct cachefiles_object *object);
215+
extern bool cachefiles_commit_tmpfile(struct cachefiles_cache *cache,
216+
struct cachefiles_object *object);
208217

209218
/*
210219
* security.c

0 commit comments

Comments
 (0)