453 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* FS-Cache interface to CacheFiles3 *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 16static atomic_t cachefiles_object_debug_id;17 18/*19 * Allocate a cache object record.20 */21static22struct 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(cachefiles_object_jar, GFP_KERNEL);31 if (!object)32 return NULL;33 34 if (cachefiles_ondemand_init_obj_info(object, volume)) {35 kmem_cache_free(cachefiles_object_jar, object);36 return NULL;37 }38 39 refcount_set(&object->ref, 1);40 41 spin_lock_init(&object->lock);42 INIT_LIST_HEAD(&object->cache_link);43 object->volume = volume;44 object->debug_id = atomic_inc_return(&cachefiles_object_debug_id);45 object->cookie = fscache_get_cookie(cookie, fscache_cookie_get_attach_object);46 47 fscache_count_object(vcookie->cache);48 trace_cachefiles_ref(object->debug_id, cookie->debug_id, 1,49 cachefiles_obj_new);50 return object;51}52 53/*54 * Note that an object has been seen.55 */56void cachefiles_see_object(struct cachefiles_object *object,57 enum cachefiles_obj_ref_trace why)58{59 trace_cachefiles_ref(object->debug_id, object->cookie->debug_id,60 refcount_read(&object->ref), why);61}62 63/*64 * Increment the usage count on an object;65 */66struct cachefiles_object *cachefiles_grab_object(struct cachefiles_object *object,67 enum cachefiles_obj_ref_trace why)68{69 int r;70 71 __refcount_inc(&object->ref, &r);72 trace_cachefiles_ref(object->debug_id, object->cookie->debug_id, r, why);73 return object;74}75 76/*77 * dispose of a reference to an object78 */79void 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(&object->ref, &r);89 trace_cachefiles_ref(object_debug_id, cookie_debug_id, r, why);90 if (done) {91 _debug("- kill object OBJ%x", object_debug_id);92 93 ASSERTCMP(object->file, ==, NULL);94 95 kfree(object->d_name);96 cachefiles_ondemand_deinit_obj_info(object);97 cache = object->volume->cache->cache;98 fscache_put_cookie(object->cookie, fscache_cookie_put_object);99 object->cookie = NULL;100 kmem_cache_free(cachefiles_object_jar, 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 keep109 * the EOF marker a multiple of DIO blocks so that we don't fall back to doing110 * non-DIO for a partial block straddling the EOF, but we also have to be111 * careful of someone expanding the file and accidentally accreting the112 * padding.113 */114static 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(file_inode(file));132 if (oi_size == ni_size)133 return 0;134 135 inode_lock(file_inode(file));136 137 /* if there's an extension to a partial page at the end of the backing138 * file, we need to discard the partial page so that we pick up new139 * 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 159truncate_failed:160 inode_unlock(file_inode(file));161 162 if (ret < 0)163 trace_cachefiles_io_error(NULL, file_inode(file), ret,164 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 cache176 */177static 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);196 197 success = cachefiles_look_up_object(object);198 if (!success)199 goto fail_withdraw;200 201 cachefiles_see_object(object, cachefiles_obj_see_lookup_cookie);202 203 spin_lock(&cache->object_list_lock);204 list_add(&object->cache_link, &cache->object_list);205 spin_unlock(&cache->object_list_lock);206 cachefiles_adjust_size(object);207 208 cachefiles_end_secure(cache, saved_cred);209 _leave(" = t");210 return true;211 212fail_withdraw:213 cachefiles_end_secure(cache, saved_cred);214 cachefiles_see_object(object, 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 to218 * drop it before we can withdraw the object.219 */220 return false;221 222fail_put:223 cachefiles_put_object(object, cachefiles_obj_put_alloc_fail);224fail:225 return false;226}227 228/*229 * Shorten the backing object to discard any dirty data and free up230 * any unused granules.231 */232static 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(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(object, inode, i_size, dio_size,244 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(object, file_inode(file), ret,250 cachefiles_trace_trunc_error);251 cachefiles_io_error_obj(object, "Trunc-to-size failed %d", ret);252 cachefiles_remove_object_xattr(cache, object, file->f_path.dentry);253 return false;254 }255 256 if (new_size < dio_size) {257 trace_cachefiles_trunc(object, inode, dio_size, new_size,258 cachefiles_trunc_dio_adjust);259 ret = cachefiles_inject_write_error();260 if (ret == 0)261 ret = vfs_fallocate(file, FALLOC_FL_ZERO_RANGE,262 new_size, dio_size - new_size);263 if (ret < 0) {264 trace_cachefiles_io_error(object, file_inode(file), ret,265 cachefiles_trace_fallocate_error);266 cachefiles_io_error_obj(object, "Trunc-to-dio-size failed %d", ret);267 cachefiles_remove_object_xattr(cache, object, file->f_path.dentry);268 return false;269 }270 }271 272 return true;273}274 275/*276 * Resize the backing object.277 */278static 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);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 anything299 * particularly. cookie->initial_size doesn't change and so the point300 * 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 */308static 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, &object->cookie->flags))314 update = true;315 if (test_and_clear_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &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 */327static 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, cachefiles_obj_see_clean_delete);333 _debug("- inval object OBJ%x", object->debug_id);334 cachefiles_delete_object(object, FSCACHE_OBJECT_WAS_RETIRED);335 } else {336 cachefiles_see_object(object, cachefiles_obj_see_clean_drop_tmp);337 _debug("- inval object OBJ%x tmpfile", object->debug_id);338 }339 } else {340 cachefiles_see_object(object, cachefiles_obj_see_clean_commit);341 cachefiles_commit_object(object, cache);342 }343 344 cachefiles_unmark_inode_in_use(object, 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 */354static 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, cachefiles_obj_see_withdraw_cookie);362 363 if (!list_empty(&object->cache_link)) {364 spin_lock(&cache->object_list_lock);365 cachefiles_see_object(object, cachefiles_obj_see_withdrawal);366 list_del_init(&object->cache_link);367 spin_unlock(&cache->object_list_lock);368 }369 370 cachefiles_ondemand_clean_object(object);371 372 if (object->file) {373 cachefiles_begin_secure(cache, &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, cachefiles_obj_put_detach);380}381 382/*383 * Invalidate the storage associated with a cookie.384 */385static 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(new_file))403 goto failed;404 405 /* Substitute the VFS target */406 _debug("sub");407 spin_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, &object->flags);413 set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &object->cookie->flags);414 415 spin_unlock(&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(d_inode(fan), I_MUTEX_PARENT);427 cachefiles_bury_object(volume->cache, object, fan,428 old_file->f_path.dentry,429 FSCACHE_OBJECT_INVALIDATED);430 }431 fput(old_file);432 }433 434 _leave(" = t");435 return true;436 437failed:438 _leave(" = f");439 return false;440}441 442const 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