1 // SPDX-License-Identifier: GPL-2.0-or-later 1 2 /* Manage high-level VFS aspects of a cache. 3 * 4 * Copyright (C) 2007, 2021 Red Hat, Inc. All 5 * Written by David Howells (dhowells@redhat.c 6 */ 7 8 #include <linux/slab.h> 9 #include <linux/statfs.h> 10 #include <linux/namei.h> 11 #include <trace/events/fscache.h> 12 #include "internal.h" 13 14 /* 15 * Bring a cache online. 16 */ 17 int cachefiles_add_cache(struct cachefiles_cac 18 { 19 struct fscache_cache *cache_cookie; 20 struct path path; 21 struct kstatfs stats; 22 struct dentry *graveyard, *cachedir, * 23 const struct cred *saved_cred; 24 int ret; 25 26 _enter(""); 27 28 cache_cookie = fscache_acquire_cache(c 29 if (IS_ERR(cache_cookie)) 30 return PTR_ERR(cache_cookie); 31 32 /* we want to work under the module's 33 ret = cachefiles_get_security_ID(cache 34 if (ret < 0) 35 goto error_getsec; 36 37 cachefiles_begin_secure(cache, &saved_ 38 39 /* look up the directory at the root o 40 ret = kern_path(cache->rootdirname, LO 41 if (ret < 0) 42 goto error_open_root; 43 44 cache->mnt = path.mnt; 45 root = path.dentry; 46 47 ret = -EINVAL; 48 if (is_idmapped_mnt(path.mnt)) { 49 pr_warn("File cache on idmappe 50 goto error_unsupported; 51 } 52 53 /* Check features of the backing files 54 * - Directories must support looking 55 * - We create tmpfiles to handle inva 56 * - We use xattrs to store metadata 57 * - We need to be able to query the a 58 * - We want to be able to sync the fi 59 * - We use DIO to/from pages, so the 60 */ 61 ret = -EOPNOTSUPP; 62 if (d_is_negative(root) || 63 !d_backing_inode(root)->i_op->look 64 !d_backing_inode(root)->i_op->mkdi 65 !d_backing_inode(root)->i_op->tmpf 66 !(d_backing_inode(root)->i_opflags 67 !root->d_sb->s_op->statfs || 68 !root->d_sb->s_op->sync_fs || 69 root->d_sb->s_blocksize > PAGE_SIZ 70 goto error_unsupported; 71 72 ret = -EROFS; 73 if (sb_rdonly(root->d_sb)) 74 goto error_unsupported; 75 76 /* determine the security of the on-di 77 * security ID of files we create */ 78 ret = cachefiles_determine_cache_secur 79 if (ret < 0) 80 goto error_unsupported; 81 82 /* get the cache size and blocksize */ 83 ret = vfs_statfs(&path, &stats); 84 if (ret < 0) 85 goto error_unsupported; 86 87 ret = -ERANGE; 88 if (stats.f_bsize <= 0) 89 goto error_unsupported; 90 91 ret = -EOPNOTSUPP; 92 if (stats.f_bsize > PAGE_SIZE) 93 goto error_unsupported; 94 95 cache->bsize = stats.f_bsize; 96 cache->bshift = ilog2(stats.f_bsize); 97 98 _debug("blksize %u (shift %u)", 99 cache->bsize, cache->bshift); 100 101 _debug("size %llu, avail %llu", 102 (unsigned long long) stats.f_bl 103 (unsigned long long) stats.f_ba 104 105 /* set up caching limits */ 106 do_div(stats.f_files, 100); 107 cache->fstop = stats.f_files * cache-> 108 cache->fcull = stats.f_files * cache-> 109 cache->frun = stats.f_files * cache-> 110 111 _debug("limits {%llu,%llu,%llu} files" 112 (unsigned long long) cache->fru 113 (unsigned long long) cache->fcu 114 (unsigned long long) cache->fst 115 116 do_div(stats.f_blocks, 100); 117 cache->bstop = stats.f_blocks * cache- 118 cache->bcull = stats.f_blocks * cache- 119 cache->brun = stats.f_blocks * cache- 120 121 _debug("limits {%llu,%llu,%llu} blocks 122 (unsigned long long) cache->bru 123 (unsigned long long) cache->bcu 124 (unsigned long long) cache->bst 125 126 /* get the cache directory and check i 127 cachedir = cachefiles_get_directory(ca 128 if (IS_ERR(cachedir)) { 129 ret = PTR_ERR(cachedir); 130 goto error_unsupported; 131 } 132 133 cache->store = cachedir; 134 135 /* get the graveyard directory */ 136 graveyard = cachefiles_get_directory(c 137 if (IS_ERR(graveyard)) { 138 ret = PTR_ERR(graveyard); 139 goto error_unsupported; 140 } 141 142 cache->graveyard = graveyard; 143 cache->cache = cache_cookie; 144 145 ret = fscache_add_cache(cache_cookie, 146 if (ret < 0) 147 goto error_add_cache; 148 149 /* done */ 150 set_bit(CACHEFILES_READY, &cache->flag 151 dput(root); 152 153 pr_info("File cache on %s registered\n 154 155 /* check how much space the cache has 156 cachefiles_has_space(cache, 0, 0, cach 157 cachefiles_end_secure(cache, saved_cre 158 _leave(" = 0 [%px]", cache->cache); 159 return 0; 160 161 error_add_cache: 162 cachefiles_put_directory(cache->gravey 163 cache->graveyard = NULL; 164 error_unsupported: 165 cachefiles_put_directory(cache->store) 166 cache->store = NULL; 167 mntput(cache->mnt); 168 cache->mnt = NULL; 169 dput(root); 170 error_open_root: 171 cachefiles_end_secure(cache, saved_cre 172 put_cred(cache->cache_cred); 173 cache->cache_cred = NULL; 174 error_getsec: 175 fscache_relinquish_cache(cache_cookie) 176 cache->cache = NULL; 177 pr_err("Failed to register: %d\n", ret 178 return ret; 179 } 180 181 /* 182 * See if we have space for a number of pages 183 * cache 184 */ 185 int cachefiles_has_space(struct cachefiles_cac 186 unsigned fnr, unsigne 187 enum cachefiles_has_s 188 { 189 struct kstatfs stats; 190 u64 b_avail, b_writing; 191 int ret; 192 193 struct path path = { 194 .mnt = cache->mnt, 195 .dentry = cache->mnt->mnt_root 196 }; 197 198 //_enter("{%llu,%llu,%llu,%llu,%llu,%l 199 // (unsigned long long) cache->f 200 // (unsigned long long) cache->f 201 // (unsigned long long) cache->f 202 // (unsigned long long) cache->b 203 // (unsigned long long) cache->b 204 // (unsigned long long) cache->b 205 // fnr, bnr); 206 207 /* find out how many pages of blockdev 208 memset(&stats, 0, sizeof(stats)); 209 210 ret = vfs_statfs(&path, &stats); 211 if (ret < 0) { 212 trace_cachefiles_vfs_error(NUL 213 cac 214 if (ret == -EIO) 215 cachefiles_io_error(ca 216 _leave(" = %d", ret); 217 return ret; 218 } 219 220 b_avail = stats.f_bavail; 221 b_writing = atomic_long_read(&cache->b 222 if (b_avail > b_writing) 223 b_avail -= b_writing; 224 else 225 b_avail = 0; 226 227 //_debug("avail %llu,%llu", 228 // (unsigned long long)stats.f_f 229 // (unsigned long long)b_avail); 230 231 /* see if there is sufficient space */ 232 if (stats.f_ffree > fnr) 233 stats.f_ffree -= fnr; 234 else 235 stats.f_ffree = 0; 236 237 if (b_avail > bnr) 238 b_avail -= bnr; 239 else 240 b_avail = 0; 241 242 ret = -ENOBUFS; 243 if (stats.f_ffree < cache->fstop || 244 b_avail < cache->bstop) 245 goto stop_and_begin_cull; 246 247 ret = 0; 248 if (stats.f_ffree < cache->fcull || 249 b_avail < cache->bcull) 250 goto begin_cull; 251 252 if (test_bit(CACHEFILES_CULLING, &cach 253 stats.f_ffree >= cache->frun && 254 b_avail >= cache->brun && 255 test_and_clear_bit(CACHEFILES_CULL 256 ) { 257 _debug("cease culling"); 258 cachefiles_state_changed(cache 259 } 260 261 //_leave(" = 0"); 262 return 0; 263 264 stop_and_begin_cull: 265 switch (reason) { 266 case cachefiles_has_space_for_write: 267 fscache_count_no_write_space() 268 break; 269 case cachefiles_has_space_for_create: 270 fscache_count_no_create_space( 271 break; 272 default: 273 break; 274 } 275 begin_cull: 276 if (!test_and_set_bit(CACHEFILES_CULLI 277 _debug("### CULL CACHE ###"); 278 cachefiles_state_changed(cache 279 } 280 281 _leave(" = %d", ret); 282 return ret; 283 } 284 285 /* 286 * Mark all the objects as being out of servic 287 */ 288 static void cachefiles_withdraw_objects(struct 289 { 290 struct cachefiles_object *object; 291 unsigned int count = 0; 292 293 _enter(""); 294 295 spin_lock(&cache->object_list_lock); 296 297 while (!list_empty(&cache->object_list 298 object = list_first_entry(&cac 299 stru 300 cachefiles_see_object(object, 301 list_del_init(&object->cache_l 302 fscache_withdraw_cookie(object 303 count++; 304 if ((count & 63) == 0) { 305 spin_unlock(&cache->ob 306 cond_resched(); 307 spin_lock(&cache->obje 308 } 309 } 310 311 spin_unlock(&cache->object_list_lock); 312 _leave(" [%u objs]", count); 313 } 314 315 /* 316 * Withdraw fscache volumes. 317 */ 318 static void cachefiles_withdraw_fscache_volume 319 { 320 struct list_head *cur; 321 struct cachefiles_volume *volume; 322 struct fscache_volume *vcookie; 323 324 _enter(""); 325 retry: 326 spin_lock(&cache->object_list_lock); 327 list_for_each(cur, &cache->volumes) { 328 volume = list_entry(cur, struc 329 330 if (atomic_read(&volume->vcook 331 continue; 332 333 vcookie = fscache_try_get_volu 334 335 if (vcookie) { 336 spin_unlock(&cache->ob 337 fscache_withdraw_volum 338 fscache_put_volume(vco 339 goto retry; 340 } 341 } 342 spin_unlock(&cache->object_list_lock); 343 344 _leave(""); 345 } 346 347 /* 348 * Withdraw cachefiles volumes. 349 */ 350 static void cachefiles_withdraw_volumes(struct 351 { 352 _enter(""); 353 354 for (;;) { 355 struct fscache_volume *vcookie 356 struct cachefiles_volume *volu 357 358 spin_lock(&cache->object_list_ 359 if (!list_empty(&cache->volume 360 volume = list_first_en 361 362 vcookie = fscache_try_ 363 364 if (!vcookie) { 365 spin_unlock(&c 366 cpu_relax(); 367 continue; 368 } 369 list_del_init(&volume- 370 } 371 spin_unlock(&cache->object_lis 372 if (!volume) 373 break; 374 375 cachefiles_withdraw_volume(vol 376 fscache_put_volume(vcookie, fs 377 } 378 379 _leave(""); 380 } 381 382 /* 383 * Sync a cache to backing disk. 384 */ 385 static void cachefiles_sync_cache(struct cache 386 { 387 const struct cred *saved_cred; 388 int ret; 389 390 _enter("%s", cache->cache->name); 391 392 /* make sure all pages pinned by opera 393 * written to disc */ 394 cachefiles_begin_secure(cache, &saved_ 395 down_read(&cache->mnt->mnt_sb->s_umoun 396 ret = sync_filesystem(cache->mnt->mnt_ 397 up_read(&cache->mnt->mnt_sb->s_umount) 398 cachefiles_end_secure(cache, saved_cre 399 400 if (ret == -EIO) 401 cachefiles_io_error(cache, 402 "Attempt t 403 ret); 404 } 405 406 /* 407 * Withdraw cache objects. 408 */ 409 void cachefiles_withdraw_cache(struct cachefil 410 { 411 struct fscache_cache *fscache = cache- 412 413 pr_info("File cache on %s unregisterin 414 415 fscache_withdraw_cache(fscache); 416 cachefiles_withdraw_fscache_volumes(ca 417 418 /* we now have to destroy all the acti 419 * cache - which we do by passing them 420 * disposed of */ 421 cachefiles_withdraw_objects(cache); 422 fscache_wait_for_objects(fscache); 423 424 cachefiles_withdraw_volumes(cache); 425 cachefiles_sync_cache(cache); 426 cache->cache = NULL; 427 fscache_relinquish_cache(fscache); 428 } 429
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.