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 | if (cachefiles_ondemand_init_obj_info(obj: object, volume)) { |
35 | kmem_cache_free(s: cachefiles_object_jar, objp: object); |
36 | return NULL; |
37 | } |
38 | |
39 | refcount_set(r: &object->ref, n: 1); |
40 | |
41 | spin_lock_init(&object->lock); |
42 | INIT_LIST_HEAD(list: &object->cache_link); |
43 | object->volume = volume; |
44 | object->debug_id = atomic_inc_return(v: &cachefiles_object_debug_id); |
45 | object->cookie = fscache_get_cookie(cookie, where: fscache_cookie_get_attach_object); |
46 | |
47 | fscache_count_object(cache: vcookie->cache); |
48 | trace_cachefiles_ref(object_debug_id: object->debug_id, cookie_debug_id: cookie->debug_id, usage: 1, |
49 | why: cachefiles_obj_new); |
50 | return object; |
51 | } |
52 | |
53 | /* |
54 | * Note that an object has been seen. |
55 | */ |
56 | void cachefiles_see_object(struct cachefiles_object *object, |
57 | enum cachefiles_obj_ref_trace why) |
58 | { |
59 | trace_cachefiles_ref(object_debug_id: object->debug_id, cookie_debug_id: object->cookie->debug_id, |
60 | usage: refcount_read(r: &object->ref), why); |
61 | } |
62 | |
63 | /* |
64 | * Increment the usage count on an object; |
65 | */ |
66 | struct cachefiles_object *cachefiles_grab_object(struct cachefiles_object *object, |
67 | enum cachefiles_obj_ref_trace why) |
68 | { |
69 | int r; |
70 | |
71 | __refcount_inc(r: &object->ref, oldp: &r); |
72 | trace_cachefiles_ref(object_debug_id: object->debug_id, cookie_debug_id: object->cookie->debug_id, usage: r, why); |
73 | return object; |
74 | } |
75 | |
76 | /* |
77 | * dispose of a reference to an object |
78 | */ |
79 | void cachefiles_put_object(struct cachefiles_object *object, |
80 | enum cachefiles_obj_ref_trace why) |
81 | { |
82 | unsigned int object_debug_id = object->debug_id; |
83 | unsigned int cookie_debug_id = object->cookie->debug_id; |
84 | struct fscache_cache *cache; |
85 | bool done; |
86 | int r; |
87 | |
88 | done = __refcount_dec_and_test(r: &object->ref, oldp: &r); |
89 | trace_cachefiles_ref(object_debug_id, cookie_debug_id, usage: r, why); |
90 | if (done) { |
91 | _debug("- kill object OBJ%x" , object_debug_id); |
92 | |
93 | ASSERTCMP(object->file, ==, NULL); |
94 | |
95 | kfree(objp: object->d_name); |
96 | cachefiles_ondemand_deinit_obj_info(obj: object); |
97 | cache = object->volume->cache->cache; |
98 | fscache_put_cookie(cookie: object->cookie, where: fscache_cookie_put_object); |
99 | object->cookie = NULL; |
100 | kmem_cache_free(s: cachefiles_object_jar, objp: object); |
101 | fscache_uncount_object(cache); |
102 | } |
103 | |
104 | _leave("" ); |
105 | } |
106 | |
107 | /* |
108 | * Adjust the size of a cache file if necessary to match the DIO size. We keep |
109 | * the EOF marker a multiple of DIO blocks so that we don't fall back to doing |
110 | * non-DIO for a partial block straddling the EOF, but we also have to be |
111 | * careful of someone expanding the file and accidentally accreting the |
112 | * padding. |
113 | */ |
114 | static int cachefiles_adjust_size(struct cachefiles_object *object) |
115 | { |
116 | struct iattr newattrs; |
117 | struct file *file = object->file; |
118 | uint64_t ni_size; |
119 | loff_t oi_size; |
120 | int ret; |
121 | |
122 | ni_size = object->cookie->object_size; |
123 | ni_size = round_up(ni_size, CACHEFILES_DIO_BLOCK_SIZE); |
124 | |
125 | _enter("{OBJ%x},[%llu]" , |
126 | object->debug_id, (unsigned long long) ni_size); |
127 | |
128 | if (!file) |
129 | return -ENOBUFS; |
130 | |
131 | oi_size = i_size_read(inode: file_inode(f: file)); |
132 | if (oi_size == ni_size) |
133 | return 0; |
134 | |
135 | inode_lock(inode: file_inode(f: file)); |
136 | |
137 | /* if there's an extension to a partial page at the end of the backing |
138 | * file, we need to discard the partial page so that we pick up new |
139 | * data after it */ |
140 | if (oi_size & ~PAGE_MASK && ni_size > oi_size) { |
141 | _debug("discard tail %llx" , oi_size); |
142 | newattrs.ia_valid = ATTR_SIZE; |
143 | newattrs.ia_size = oi_size & PAGE_MASK; |
144 | ret = cachefiles_inject_remove_error(); |
145 | if (ret == 0) |
146 | ret = notify_change(&nop_mnt_idmap, file->f_path.dentry, |
147 | &newattrs, NULL); |
148 | if (ret < 0) |
149 | goto truncate_failed; |
150 | } |
151 | |
152 | newattrs.ia_valid = ATTR_SIZE; |
153 | newattrs.ia_size = ni_size; |
154 | ret = cachefiles_inject_write_error(); |
155 | if (ret == 0) |
156 | ret = notify_change(&nop_mnt_idmap, file->f_path.dentry, |
157 | &newattrs, NULL); |
158 | |
159 | truncate_failed: |
160 | inode_unlock(inode: file_inode(f: file)); |
161 | |
162 | if (ret < 0) |
163 | trace_cachefiles_io_error(NULL, backer: file_inode(f: file), error: ret, |
164 | where: cachefiles_trace_notify_change_error); |
165 | if (ret == -EIO) { |
166 | cachefiles_io_error_obj(object, "Size set failed" ); |
167 | ret = -ENOBUFS; |
168 | } |
169 | |
170 | _leave(" = %d" , ret); |
171 | return ret; |
172 | } |
173 | |
174 | /* |
175 | * Attempt to look up the nominated node in this cache |
176 | */ |
177 | static bool cachefiles_lookup_cookie(struct fscache_cookie *cookie) |
178 | { |
179 | struct cachefiles_object *object; |
180 | struct cachefiles_cache *cache = cookie->volume->cache->cache_priv; |
181 | const struct cred *saved_cred; |
182 | bool success; |
183 | |
184 | object = cachefiles_alloc_object(cookie); |
185 | if (!object) |
186 | goto fail; |
187 | |
188 | _enter("{OBJ%x}" , object->debug_id); |
189 | |
190 | if (!cachefiles_cook_key(object)) |
191 | goto fail_put; |
192 | |
193 | cookie->cache_priv = object; |
194 | |
195 | cachefiles_begin_secure(cache, saved_cred: &saved_cred); |
196 | |
197 | success = cachefiles_look_up_object(object); |
198 | if (!success) |
199 | goto fail_withdraw; |
200 | |
201 | cachefiles_see_object(object, why: cachefiles_obj_see_lookup_cookie); |
202 | |
203 | spin_lock(lock: &cache->object_list_lock); |
204 | list_add(new: &object->cache_link, head: &cache->object_list); |
205 | spin_unlock(lock: &cache->object_list_lock); |
206 | cachefiles_adjust_size(object); |
207 | |
208 | cachefiles_end_secure(cache, saved_cred); |
209 | _leave(" = t" ); |
210 | return true; |
211 | |
212 | fail_withdraw: |
213 | cachefiles_end_secure(cache, saved_cred); |
214 | cachefiles_see_object(object, why: cachefiles_obj_see_lookup_failed); |
215 | fscache_caching_failed(cookie); |
216 | _debug("failed c=%08x o=%08x" , cookie->debug_id, object->debug_id); |
217 | /* The caller holds an access count on the cookie, so we need them to |
218 | * drop it before we can withdraw the object. |
219 | */ |
220 | return false; |
221 | |
222 | fail_put: |
223 | cachefiles_put_object(object, why: cachefiles_obj_put_alloc_fail); |
224 | fail: |
225 | return false; |
226 | } |
227 | |
228 | /* |
229 | * Shorten the backing object to discard any dirty data and free up |
230 | * any unused granules. |
231 | */ |
232 | static bool cachefiles_shorten_object(struct cachefiles_object *object, |
233 | struct file *file, loff_t new_size) |
234 | { |
235 | struct cachefiles_cache *cache = object->volume->cache; |
236 | struct inode *inode = file_inode(f: file); |
237 | loff_t i_size, dio_size; |
238 | int ret; |
239 | |
240 | dio_size = round_up(new_size, CACHEFILES_DIO_BLOCK_SIZE); |
241 | i_size = i_size_read(inode); |
242 | |
243 | trace_cachefiles_trunc(obj: object, backer: inode, from: i_size, to: dio_size, |
244 | why: cachefiles_trunc_shrink); |
245 | ret = cachefiles_inject_remove_error(); |
246 | if (ret == 0) |
247 | ret = vfs_truncate(&file->f_path, dio_size); |
248 | if (ret < 0) { |
249 | trace_cachefiles_io_error(obj: object, backer: file_inode(f: file), error: ret, |
250 | where: cachefiles_trace_trunc_error); |
251 | cachefiles_io_error_obj(object, "Trunc-to-size failed %d" , ret); |
252 | cachefiles_remove_object_xattr(cache, object, dentry: file->f_path.dentry); |
253 | return false; |
254 | } |
255 | |
256 | if (new_size < dio_size) { |
257 | trace_cachefiles_trunc(obj: object, backer: inode, from: dio_size, to: new_size, |
258 | why: cachefiles_trunc_dio_adjust); |
259 | ret = cachefiles_inject_write_error(); |
260 | if (ret == 0) |
261 | ret = vfs_fallocate(file, FALLOC_FL_ZERO_RANGE, |
262 | offset: new_size, len: dio_size - new_size); |
263 | if (ret < 0) { |
264 | trace_cachefiles_io_error(obj: object, backer: file_inode(f: file), error: ret, |
265 | where: cachefiles_trace_fallocate_error); |
266 | cachefiles_io_error_obj(object, "Trunc-to-dio-size failed %d" , ret); |
267 | cachefiles_remove_object_xattr(cache, object, dentry: file->f_path.dentry); |
268 | return false; |
269 | } |
270 | } |
271 | |
272 | return true; |
273 | } |
274 | |
275 | /* |
276 | * Resize the backing object. |
277 | */ |
278 | static void cachefiles_resize_cookie(struct netfs_cache_resources *cres, |
279 | loff_t new_size) |
280 | { |
281 | struct cachefiles_object *object = cachefiles_cres_object(cres); |
282 | struct cachefiles_cache *cache = object->volume->cache; |
283 | struct fscache_cookie *cookie = object->cookie; |
284 | const struct cred *saved_cred; |
285 | struct file *file = cachefiles_cres_file(cres); |
286 | loff_t old_size = cookie->object_size; |
287 | |
288 | _enter("%llu->%llu" , old_size, new_size); |
289 | |
290 | if (new_size < old_size) { |
291 | cachefiles_begin_secure(cache, saved_cred: &saved_cred); |
292 | cachefiles_shorten_object(object, file, new_size); |
293 | cachefiles_end_secure(cache, saved_cred); |
294 | object->cookie->object_size = new_size; |
295 | return; |
296 | } |
297 | |
298 | /* The file is being expanded. We don't need to do anything |
299 | * particularly. cookie->initial_size doesn't change and so the point |
300 | * at which we have to download before doesn't change. |
301 | */ |
302 | cookie->object_size = new_size; |
303 | } |
304 | |
305 | /* |
306 | * Commit changes to the object as we drop it. |
307 | */ |
308 | static void cachefiles_commit_object(struct cachefiles_object *object, |
309 | struct cachefiles_cache *cache) |
310 | { |
311 | bool update = false; |
312 | |
313 | if (test_and_clear_bit(FSCACHE_COOKIE_LOCAL_WRITE, addr: &object->cookie->flags)) |
314 | update = true; |
315 | if (test_and_clear_bit(FSCACHE_COOKIE_NEEDS_UPDATE, addr: &object->cookie->flags)) |
316 | update = true; |
317 | if (update) |
318 | cachefiles_set_object_xattr(object); |
319 | |
320 | if (test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) |
321 | cachefiles_commit_tmpfile(cache, object); |
322 | } |
323 | |
324 | /* |
325 | * Finalise and object and close the VFS structs that we have. |
326 | */ |
327 | static void cachefiles_clean_up_object(struct cachefiles_object *object, |
328 | struct cachefiles_cache *cache) |
329 | { |
330 | if (test_bit(FSCACHE_COOKIE_RETIRED, &object->cookie->flags)) { |
331 | if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) { |
332 | cachefiles_see_object(object, why: cachefiles_obj_see_clean_delete); |
333 | _debug("- inval object OBJ%x" , object->debug_id); |
334 | cachefiles_delete_object(object, why: FSCACHE_OBJECT_WAS_RETIRED); |
335 | } else { |
336 | cachefiles_see_object(object, why: cachefiles_obj_see_clean_drop_tmp); |
337 | _debug("- inval object OBJ%x tmpfile" , object->debug_id); |
338 | } |
339 | } else { |
340 | cachefiles_see_object(object, why: cachefiles_obj_see_clean_commit); |
341 | cachefiles_commit_object(object, cache); |
342 | } |
343 | |
344 | cachefiles_unmark_inode_in_use(object, file: object->file); |
345 | if (object->file) { |
346 | fput(object->file); |
347 | object->file = NULL; |
348 | } |
349 | } |
350 | |
351 | /* |
352 | * Withdraw caching for a cookie. |
353 | */ |
354 | static void cachefiles_withdraw_cookie(struct fscache_cookie *cookie) |
355 | { |
356 | struct cachefiles_object *object = cookie->cache_priv; |
357 | struct cachefiles_cache *cache = object->volume->cache; |
358 | const struct cred *saved_cred; |
359 | |
360 | _enter("o=%x" , object->debug_id); |
361 | cachefiles_see_object(object, why: cachefiles_obj_see_withdraw_cookie); |
362 | |
363 | if (!list_empty(head: &object->cache_link)) { |
364 | spin_lock(lock: &cache->object_list_lock); |
365 | cachefiles_see_object(object, why: cachefiles_obj_see_withdrawal); |
366 | list_del_init(entry: &object->cache_link); |
367 | spin_unlock(lock: &cache->object_list_lock); |
368 | } |
369 | |
370 | cachefiles_ondemand_clean_object(object); |
371 | |
372 | if (object->file) { |
373 | cachefiles_begin_secure(cache, saved_cred: &saved_cred); |
374 | cachefiles_clean_up_object(object, cache); |
375 | cachefiles_end_secure(cache, saved_cred); |
376 | } |
377 | |
378 | cookie->cache_priv = NULL; |
379 | cachefiles_put_object(object, why: cachefiles_obj_put_detach); |
380 | } |
381 | |
382 | /* |
383 | * Invalidate the storage associated with a cookie. |
384 | */ |
385 | static bool cachefiles_invalidate_cookie(struct fscache_cookie *cookie) |
386 | { |
387 | struct cachefiles_object *object = cookie->cache_priv; |
388 | struct file *new_file, *old_file; |
389 | bool old_tmpfile; |
390 | |
391 | _enter("o=%x,[%llu]" , object->debug_id, object->cookie->object_size); |
392 | |
393 | old_tmpfile = test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags); |
394 | |
395 | if (!object->file) { |
396 | fscache_resume_after_invalidation(cookie); |
397 | _leave(" = t [light]" ); |
398 | return true; |
399 | } |
400 | |
401 | new_file = cachefiles_create_tmpfile(object); |
402 | if (IS_ERR(ptr: new_file)) |
403 | goto failed; |
404 | |
405 | /* Substitute the VFS target */ |
406 | _debug("sub" ); |
407 | spin_lock(lock: &object->lock); |
408 | |
409 | old_file = object->file; |
410 | object->file = new_file; |
411 | object->content_info = CACHEFILES_CONTENT_NO_DATA; |
412 | set_bit(CACHEFILES_OBJECT_USING_TMPFILE, addr: &object->flags); |
413 | set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, addr: &object->cookie->flags); |
414 | |
415 | spin_unlock(lock: &object->lock); |
416 | _debug("subbed" ); |
417 | |
418 | /* Allow I/O to take place again */ |
419 | fscache_resume_after_invalidation(cookie); |
420 | |
421 | if (old_file) { |
422 | if (!old_tmpfile) { |
423 | struct cachefiles_volume *volume = object->volume; |
424 | struct dentry *fan = volume->fanout[(u8)cookie->key_hash]; |
425 | |
426 | inode_lock_nested(inode: d_inode(dentry: fan), subclass: I_MUTEX_PARENT); |
427 | cachefiles_bury_object(cache: volume->cache, object, dir: fan, |
428 | rep: old_file->f_path.dentry, |
429 | why: FSCACHE_OBJECT_INVALIDATED); |
430 | } |
431 | fput(old_file); |
432 | } |
433 | |
434 | _leave(" = t" ); |
435 | return true; |
436 | |
437 | failed: |
438 | _leave(" = f" ); |
439 | return false; |
440 | } |
441 | |
442 | const struct fscache_cache_ops cachefiles_cache_ops = { |
443 | .name = "cachefiles" , |
444 | .acquire_volume = cachefiles_acquire_volume, |
445 | .free_volume = cachefiles_free_volume, |
446 | .lookup_cookie = cachefiles_lookup_cookie, |
447 | .withdraw_cookie = cachefiles_withdraw_cookie, |
448 | .invalidate_cookie = cachefiles_invalidate_cookie, |
449 | .begin_operation = cachefiles_begin_operation, |
450 | .resize_cookie = cachefiles_resize_cookie, |
451 | .prepare_to_write = cachefiles_prepare_to_write, |
452 | }; |
453 | |