1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* FS-Cache interface to CacheFiles |
3 | * |
4 | * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. |
5 | * Written by David Howells (dhowells@redhat.com) |
6 | */ |
7 | |
8 | #include <linux/slab.h> |
9 | #include <linux/mount.h> |
10 | #include <linux/xattr.h> |
11 | #include <linux/file.h> |
12 | #include <linux/falloc.h> |
13 | #include <trace/events/fscache.h> |
14 | #include "internal.h" |
15 | |
16 | static atomic_t cachefiles_object_debug_id; |
17 | |
18 | /* |
19 | * Allocate a cache object record. |
20 | */ |
21 | static |
22 | struct cachefiles_object *cachefiles_alloc_object(struct fscache_cookie *cookie) |
23 | { |
24 | struct fscache_volume *vcookie = cookie->volume; |
25 | struct cachefiles_volume *volume = vcookie->cache_priv; |
26 | struct cachefiles_object *object; |
27 | |
28 | _enter("{%s},%x," , vcookie->key, cookie->debug_id); |
29 | |
30 | object = kmem_cache_zalloc(k: cachefiles_object_jar, GFP_KERNEL); |
31 | if (!object) |
32 | return NULL; |
33 | |
34 | refcount_set(r: &object->ref, n: 1); |
35 | |
36 | spin_lock_init(&object->lock); |
37 | INIT_LIST_HEAD(list: &object->cache_link); |
38 | object->volume = volume; |
39 | object->debug_id = atomic_inc_return(v: &cachefiles_object_debug_id); |
40 | object->cookie = fscache_get_cookie(cookie, where: fscache_cookie_get_attach_object); |
41 | |
42 | fscache_count_object(cache: vcookie->cache); |
43 | trace_cachefiles_ref(object_debug_id: object->debug_id, cookie_debug_id: cookie->debug_id, usage: 1, |
44 | why: cachefiles_obj_new); |
45 | return object; |
46 | } |
47 | |
48 | /* |
49 | * Note that an object has been seen. |
50 | */ |
51 | void cachefiles_see_object(struct cachefiles_object *object, |
52 | enum cachefiles_obj_ref_trace why) |
53 | { |
54 | trace_cachefiles_ref(object_debug_id: object->debug_id, cookie_debug_id: object->cookie->debug_id, |
55 | usage: refcount_read(r: &object->ref), why); |
56 | } |
57 | |
58 | /* |
59 | * Increment the usage count on an object; |
60 | */ |
61 | struct cachefiles_object *cachefiles_grab_object(struct cachefiles_object *object, |
62 | enum cachefiles_obj_ref_trace why) |
63 | { |
64 | int r; |
65 | |
66 | __refcount_inc(r: &object->ref, oldp: &r); |
67 | trace_cachefiles_ref(object_debug_id: object->debug_id, cookie_debug_id: object->cookie->debug_id, usage: r, why); |
68 | return object; |
69 | } |
70 | |
71 | /* |
72 | * dispose of a reference to an object |
73 | */ |
74 | void cachefiles_put_object(struct cachefiles_object *object, |
75 | enum cachefiles_obj_ref_trace why) |
76 | { |
77 | unsigned int object_debug_id = object->debug_id; |
78 | unsigned int cookie_debug_id = object->cookie->debug_id; |
79 | struct fscache_cache *cache; |
80 | bool done; |
81 | int r; |
82 | |
83 | done = __refcount_dec_and_test(r: &object->ref, oldp: &r); |
84 | trace_cachefiles_ref(object_debug_id, cookie_debug_id, usage: r, why); |
85 | if (done) { |
86 | _debug("- kill object OBJ%x" , object_debug_id); |
87 | |
88 | ASSERTCMP(object->file, ==, NULL); |
89 | |
90 | kfree(objp: object->d_name); |
91 | |
92 | cache = object->volume->cache->cache; |
93 | fscache_put_cookie(cookie: object->cookie, where: fscache_cookie_put_object); |
94 | object->cookie = NULL; |
95 | kmem_cache_free(s: cachefiles_object_jar, objp: object); |
96 | fscache_uncount_object(cache); |
97 | } |
98 | |
99 | _leave("" ); |
100 | } |
101 | |
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(inode: file_inode(f: file)); |
127 | if (oi_size == ni_size) |
128 | return 0; |
129 | |
130 | inode_lock(inode: file_inode(f: 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(&nop_mnt_idmap, 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(&nop_mnt_idmap, file->f_path.dentry, |
152 | &newattrs, NULL); |
153 | |
154 | truncate_failed: |
155 | inode_unlock(inode: file_inode(f: file)); |
156 | |
157 | if (ret < 0) |
158 | trace_cachefiles_io_error(NULL, backer: file_inode(f: file), error: ret, |
159 | where: 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: &saved_cred); |
191 | |
192 | success = cachefiles_look_up_object(object); |
193 | if (!success) |
194 | goto fail_withdraw; |
195 | |
196 | cachefiles_see_object(object, why: cachefiles_obj_see_lookup_cookie); |
197 | |
198 | spin_lock(lock: &cache->object_list_lock); |
199 | list_add(new: &object->cache_link, head: &cache->object_list); |
200 | spin_unlock(lock: &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, why: 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, why: cachefiles_obj_put_alloc_fail); |
219 | fail: |
220 | return false; |
221 | } |
222 | |
223 | /* |
224 | * Shorten the backing object to discard any dirty data and free up |
225 | * any unused granules. |
226 | */ |
227 | static bool cachefiles_shorten_object(struct cachefiles_object *object, |
228 | struct file *file, loff_t new_size) |
229 | { |
230 | struct cachefiles_cache *cache = object->volume->cache; |
231 | struct inode *inode = file_inode(f: file); |
232 | loff_t i_size, dio_size; |
233 | int ret; |
234 | |
235 | dio_size = round_up(new_size, CACHEFILES_DIO_BLOCK_SIZE); |
236 | i_size = i_size_read(inode); |
237 | |
238 | trace_cachefiles_trunc(obj: object, backer: inode, from: i_size, to: dio_size, |
239 | why: cachefiles_trunc_shrink); |
240 | ret = cachefiles_inject_remove_error(); |
241 | if (ret == 0) |
242 | ret = vfs_truncate(&file->f_path, dio_size); |
243 | if (ret < 0) { |
244 | trace_cachefiles_io_error(obj: object, backer: file_inode(f: file), error: ret, |
245 | where: cachefiles_trace_trunc_error); |
246 | cachefiles_io_error_obj(object, "Trunc-to-size failed %d" , ret); |
247 | cachefiles_remove_object_xattr(cache, object, dentry: file->f_path.dentry); |
248 | return false; |
249 | } |
250 | |
251 | if (new_size < dio_size) { |
252 | trace_cachefiles_trunc(obj: object, backer: inode, from: dio_size, to: new_size, |
253 | why: cachefiles_trunc_dio_adjust); |
254 | ret = cachefiles_inject_write_error(); |
255 | if (ret == 0) |
256 | ret = vfs_fallocate(file, FALLOC_FL_ZERO_RANGE, |
257 | offset: new_size, len: dio_size - new_size); |
258 | if (ret < 0) { |
259 | trace_cachefiles_io_error(obj: object, backer: file_inode(f: file), error: ret, |
260 | where: cachefiles_trace_fallocate_error); |
261 | cachefiles_io_error_obj(object, "Trunc-to-dio-size failed %d" , ret); |
262 | cachefiles_remove_object_xattr(cache, object, dentry: file->f_path.dentry); |
263 | return false; |
264 | } |
265 | } |
266 | |
267 | return true; |
268 | } |
269 | |
270 | /* |
271 | * Resize the backing object. |
272 | */ |
273 | static void cachefiles_resize_cookie(struct netfs_cache_resources *cres, |
274 | loff_t new_size) |
275 | { |
276 | struct cachefiles_object *object = cachefiles_cres_object(cres); |
277 | struct cachefiles_cache *cache = object->volume->cache; |
278 | struct fscache_cookie *cookie = object->cookie; |
279 | const struct cred *saved_cred; |
280 | struct file *file = cachefiles_cres_file(cres); |
281 | loff_t old_size = cookie->object_size; |
282 | |
283 | _enter("%llu->%llu" , old_size, new_size); |
284 | |
285 | if (new_size < old_size) { |
286 | cachefiles_begin_secure(cache, saved_cred: &saved_cred); |
287 | cachefiles_shorten_object(object, file, new_size); |
288 | cachefiles_end_secure(cache, saved_cred); |
289 | object->cookie->object_size = new_size; |
290 | return; |
291 | } |
292 | |
293 | /* The file is being expanded. We don't need to do anything |
294 | * particularly. cookie->initial_size doesn't change and so the point |
295 | * at which we have to download before doesn't change. |
296 | */ |
297 | cookie->object_size = new_size; |
298 | } |
299 | |
300 | /* |
301 | * Commit changes to the object as we drop it. |
302 | */ |
303 | static void cachefiles_commit_object(struct cachefiles_object *object, |
304 | struct cachefiles_cache *cache) |
305 | { |
306 | bool update = false; |
307 | |
308 | if (test_and_clear_bit(FSCACHE_COOKIE_LOCAL_WRITE, addr: &object->cookie->flags)) |
309 | update = true; |
310 | if (test_and_clear_bit(FSCACHE_COOKIE_NEEDS_UPDATE, addr: &object->cookie->flags)) |
311 | update = true; |
312 | if (update) |
313 | cachefiles_set_object_xattr(object); |
314 | |
315 | if (test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) |
316 | cachefiles_commit_tmpfile(cache, object); |
317 | } |
318 | |
319 | /* |
320 | * Finalise and object and close the VFS structs that we have. |
321 | */ |
322 | static void cachefiles_clean_up_object(struct cachefiles_object *object, |
323 | struct cachefiles_cache *cache) |
324 | { |
325 | if (test_bit(FSCACHE_COOKIE_RETIRED, &object->cookie->flags)) { |
326 | if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) { |
327 | cachefiles_see_object(object, why: cachefiles_obj_see_clean_delete); |
328 | _debug("- inval object OBJ%x" , object->debug_id); |
329 | cachefiles_delete_object(object, why: FSCACHE_OBJECT_WAS_RETIRED); |
330 | } else { |
331 | cachefiles_see_object(object, why: cachefiles_obj_see_clean_drop_tmp); |
332 | _debug("- inval object OBJ%x tmpfile" , object->debug_id); |
333 | } |
334 | } else { |
335 | cachefiles_see_object(object, why: cachefiles_obj_see_clean_commit); |
336 | cachefiles_commit_object(object, cache); |
337 | } |
338 | |
339 | cachefiles_unmark_inode_in_use(object, file: object->file); |
340 | if (object->file) { |
341 | fput(object->file); |
342 | object->file = NULL; |
343 | } |
344 | } |
345 | |
346 | /* |
347 | * Withdraw caching for a cookie. |
348 | */ |
349 | static void cachefiles_withdraw_cookie(struct fscache_cookie *cookie) |
350 | { |
351 | struct cachefiles_object *object = cookie->cache_priv; |
352 | struct cachefiles_cache *cache = object->volume->cache; |
353 | const struct cred *saved_cred; |
354 | |
355 | _enter("o=%x" , object->debug_id); |
356 | cachefiles_see_object(object, why: cachefiles_obj_see_withdraw_cookie); |
357 | |
358 | if (!list_empty(head: &object->cache_link)) { |
359 | spin_lock(lock: &cache->object_list_lock); |
360 | cachefiles_see_object(object, why: cachefiles_obj_see_withdrawal); |
361 | list_del_init(entry: &object->cache_link); |
362 | spin_unlock(lock: &cache->object_list_lock); |
363 | } |
364 | |
365 | cachefiles_ondemand_clean_object(object); |
366 | |
367 | if (object->file) { |
368 | cachefiles_begin_secure(cache, saved_cred: &saved_cred); |
369 | cachefiles_clean_up_object(object, cache); |
370 | cachefiles_end_secure(cache, saved_cred); |
371 | } |
372 | |
373 | cookie->cache_priv = NULL; |
374 | cachefiles_put_object(object, why: cachefiles_obj_put_detach); |
375 | } |
376 | |
377 | /* |
378 | * Invalidate the storage associated with a cookie. |
379 | */ |
380 | static bool cachefiles_invalidate_cookie(struct fscache_cookie *cookie) |
381 | { |
382 | struct cachefiles_object *object = cookie->cache_priv; |
383 | struct file *new_file, *old_file; |
384 | bool old_tmpfile; |
385 | |
386 | _enter("o=%x,[%llu]" , object->debug_id, object->cookie->object_size); |
387 | |
388 | old_tmpfile = test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags); |
389 | |
390 | if (!object->file) { |
391 | fscache_resume_after_invalidation(cookie); |
392 | _leave(" = t [light]" ); |
393 | return true; |
394 | } |
395 | |
396 | new_file = cachefiles_create_tmpfile(object); |
397 | if (IS_ERR(ptr: new_file)) |
398 | goto failed; |
399 | |
400 | /* Substitute the VFS target */ |
401 | _debug("sub" ); |
402 | spin_lock(lock: &object->lock); |
403 | |
404 | old_file = object->file; |
405 | object->file = new_file; |
406 | object->content_info = CACHEFILES_CONTENT_NO_DATA; |
407 | set_bit(CACHEFILES_OBJECT_USING_TMPFILE, addr: &object->flags); |
408 | set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, addr: &object->cookie->flags); |
409 | |
410 | spin_unlock(lock: &object->lock); |
411 | _debug("subbed" ); |
412 | |
413 | /* Allow I/O to take place again */ |
414 | fscache_resume_after_invalidation(cookie); |
415 | |
416 | if (old_file) { |
417 | if (!old_tmpfile) { |
418 | struct cachefiles_volume *volume = object->volume; |
419 | struct dentry *fan = volume->fanout[(u8)cookie->key_hash]; |
420 | |
421 | inode_lock_nested(inode: d_inode(dentry: fan), subclass: I_MUTEX_PARENT); |
422 | cachefiles_bury_object(cache: volume->cache, object, dir: fan, |
423 | rep: old_file->f_path.dentry, |
424 | why: FSCACHE_OBJECT_INVALIDATED); |
425 | } |
426 | fput(old_file); |
427 | } |
428 | |
429 | _leave(" = t" ); |
430 | return true; |
431 | |
432 | failed: |
433 | _leave(" = f" ); |
434 | return false; |
435 | } |
436 | |
437 | const struct fscache_cache_ops cachefiles_cache_ops = { |
438 | .name = "cachefiles" , |
439 | .acquire_volume = cachefiles_acquire_volume, |
440 | .free_volume = cachefiles_free_volume, |
441 | .lookup_cookie = cachefiles_lookup_cookie, |
442 | .withdraw_cookie = cachefiles_withdraw_cookie, |
443 | .invalidate_cookie = cachefiles_invalidate_cookie, |
444 | .begin_operation = cachefiles_begin_operation, |
445 | .resize_cookie = cachefiles_resize_cookie, |
446 | .prepare_to_write = cachefiles_prepare_to_write, |
447 | }; |
448 | |