volume.c (15008B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* Volume-level cache cookie handling. 3 * 4 * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. 5 * Written by David Howells (dhowells@redhat.com) 6 */ 7 8#define FSCACHE_DEBUG_LEVEL COOKIE 9#include <linux/export.h> 10#include <linux/slab.h> 11#include "internal.h" 12 13#define fscache_volume_hash_shift 10 14static struct hlist_bl_head fscache_volume_hash[1 << fscache_volume_hash_shift]; 15static atomic_t fscache_volume_debug_id; 16static LIST_HEAD(fscache_volumes); 17 18static void fscache_create_volume_work(struct work_struct *work); 19 20struct fscache_volume *fscache_get_volume(struct fscache_volume *volume, 21 enum fscache_volume_trace where) 22{ 23 int ref; 24 25 __refcount_inc(&volume->ref, &ref); 26 trace_fscache_volume(volume->debug_id, ref + 1, where); 27 return volume; 28} 29 30static void fscache_see_volume(struct fscache_volume *volume, 31 enum fscache_volume_trace where) 32{ 33 int ref = refcount_read(&volume->ref); 34 35 trace_fscache_volume(volume->debug_id, ref, where); 36} 37 38/* 39 * Pin the cache behind a volume so that we can access it. 40 */ 41static void __fscache_begin_volume_access(struct fscache_volume *volume, 42 struct fscache_cookie *cookie, 43 enum fscache_access_trace why) 44{ 45 int n_accesses; 46 47 n_accesses = atomic_inc_return(&volume->n_accesses); 48 smp_mb__after_atomic(); 49 trace_fscache_access_volume(volume->debug_id, cookie ? cookie->debug_id : 0, 50 refcount_read(&volume->ref), 51 n_accesses, why); 52} 53 54/** 55 * fscache_begin_volume_access - Pin a cache so a volume can be accessed 56 * @volume: The volume cookie 57 * @cookie: A datafile cookie for a tracing reference (or NULL) 58 * @why: An indication of the circumstances of the access for tracing 59 * 60 * Attempt to pin the cache to prevent it from going away whilst we're 61 * accessing a volume and returns true if successful. This works as follows: 62 * 63 * (1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTIVE), 64 * then we return false to indicate access was not permitted. 65 * 66 * (2) If the cache tests as live, then we increment the volume's n_accesses 67 * count and then recheck the cache liveness, ending the access if it 68 * ceased to be live. 69 * 70 * (3) When we end the access, we decrement the volume's n_accesses and wake 71 * up the any waiters if it reaches 0. 72 * 73 * (4) Whilst the cache is caching, the volume's n_accesses is kept 74 * artificially incremented to prevent wakeups from happening. 75 * 76 * (5) When the cache is taken offline, the state is changed to prevent new 77 * accesses, the volume's n_accesses is decremented and we wait for it to 78 * become 0. 79 * 80 * The datafile @cookie and the @why indicator are merely provided for tracing 81 * purposes. 82 */ 83bool fscache_begin_volume_access(struct fscache_volume *volume, 84 struct fscache_cookie *cookie, 85 enum fscache_access_trace why) 86{ 87 if (!fscache_cache_is_live(volume->cache)) 88 return false; 89 __fscache_begin_volume_access(volume, cookie, why); 90 if (!fscache_cache_is_live(volume->cache)) { 91 fscache_end_volume_access(volume, cookie, fscache_access_unlive); 92 return false; 93 } 94 return true; 95} 96 97/** 98 * fscache_end_volume_access - Unpin a cache at the end of an access. 99 * @volume: The volume cookie 100 * @cookie: A datafile cookie for a tracing reference (or NULL) 101 * @why: An indication of the circumstances of the access for tracing 102 * 103 * Unpin a cache volume after we've accessed it. The datafile @cookie and the 104 * @why indicator are merely provided for tracing purposes. 105 */ 106void fscache_end_volume_access(struct fscache_volume *volume, 107 struct fscache_cookie *cookie, 108 enum fscache_access_trace why) 109{ 110 int n_accesses; 111 112 smp_mb__before_atomic(); 113 n_accesses = atomic_dec_return(&volume->n_accesses); 114 trace_fscache_access_volume(volume->debug_id, cookie ? cookie->debug_id : 0, 115 refcount_read(&volume->ref), 116 n_accesses, why); 117 if (n_accesses == 0) 118 wake_up_var(&volume->n_accesses); 119} 120EXPORT_SYMBOL(fscache_end_volume_access); 121 122static bool fscache_volume_same(const struct fscache_volume *a, 123 const struct fscache_volume *b) 124{ 125 size_t klen; 126 127 if (a->key_hash != b->key_hash || 128 a->cache != b->cache || 129 a->key[0] != b->key[0]) 130 return false; 131 132 klen = round_up(a->key[0] + 1, sizeof(__le32)); 133 return memcmp(a->key, b->key, klen) == 0; 134} 135 136static bool fscache_is_acquire_pending(struct fscache_volume *volume) 137{ 138 return test_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &volume->flags); 139} 140 141static void fscache_wait_on_volume_collision(struct fscache_volume *candidate, 142 unsigned int collidee_debug_id) 143{ 144 wait_var_event_timeout(&candidate->flags, 145 !fscache_is_acquire_pending(candidate), 20 * HZ); 146 if (fscache_is_acquire_pending(candidate)) { 147 pr_notice("Potential volume collision new=%08x old=%08x", 148 candidate->debug_id, collidee_debug_id); 149 fscache_stat(&fscache_n_volumes_collision); 150 wait_var_event(&candidate->flags, !fscache_is_acquire_pending(candidate)); 151 } 152} 153 154/* 155 * Attempt to insert the new volume into the hash. If there's a collision, we 156 * wait for the old volume to complete if it's being relinquished and an error 157 * otherwise. 158 */ 159static bool fscache_hash_volume(struct fscache_volume *candidate) 160{ 161 struct fscache_volume *cursor; 162 struct hlist_bl_head *h; 163 struct hlist_bl_node *p; 164 unsigned int bucket, collidee_debug_id = 0; 165 166 bucket = candidate->key_hash & (ARRAY_SIZE(fscache_volume_hash) - 1); 167 h = &fscache_volume_hash[bucket]; 168 169 hlist_bl_lock(h); 170 hlist_bl_for_each_entry(cursor, p, h, hash_link) { 171 if (fscache_volume_same(candidate, cursor)) { 172 if (!test_bit(FSCACHE_VOLUME_RELINQUISHED, &cursor->flags)) 173 goto collision; 174 fscache_see_volume(cursor, fscache_volume_get_hash_collision); 175 set_bit(FSCACHE_VOLUME_COLLIDED_WITH, &cursor->flags); 176 set_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &candidate->flags); 177 collidee_debug_id = cursor->debug_id; 178 break; 179 } 180 } 181 182 hlist_bl_add_head(&candidate->hash_link, h); 183 hlist_bl_unlock(h); 184 185 if (fscache_is_acquire_pending(candidate)) 186 fscache_wait_on_volume_collision(candidate, collidee_debug_id); 187 return true; 188 189collision: 190 fscache_see_volume(cursor, fscache_volume_collision); 191 hlist_bl_unlock(h); 192 return false; 193} 194 195/* 196 * Allocate and initialise a volume representation cookie. 197 */ 198static struct fscache_volume *fscache_alloc_volume(const char *volume_key, 199 const char *cache_name, 200 const void *coherency_data, 201 size_t coherency_len) 202{ 203 struct fscache_volume *volume; 204 struct fscache_cache *cache; 205 size_t klen, hlen; 206 char *key; 207 208 if (!coherency_data) 209 coherency_len = 0; 210 211 cache = fscache_lookup_cache(cache_name, false); 212 if (IS_ERR(cache)) 213 return NULL; 214 215 volume = kzalloc(struct_size(volume, coherency, coherency_len), 216 GFP_KERNEL); 217 if (!volume) 218 goto err_cache; 219 220 volume->cache = cache; 221 volume->coherency_len = coherency_len; 222 if (coherency_data) 223 memcpy(volume->coherency, coherency_data, coherency_len); 224 INIT_LIST_HEAD(&volume->proc_link); 225 INIT_WORK(&volume->work, fscache_create_volume_work); 226 refcount_set(&volume->ref, 1); 227 spin_lock_init(&volume->lock); 228 229 /* Stick the length on the front of the key and pad it out to make 230 * hashing easier. 231 */ 232 klen = strlen(volume_key); 233 hlen = round_up(1 + klen + 1, sizeof(__le32)); 234 key = kzalloc(hlen, GFP_KERNEL); 235 if (!key) 236 goto err_vol; 237 key[0] = klen; 238 memcpy(key + 1, volume_key, klen); 239 240 volume->key = key; 241 volume->key_hash = fscache_hash(0, key, hlen); 242 243 volume->debug_id = atomic_inc_return(&fscache_volume_debug_id); 244 down_write(&fscache_addremove_sem); 245 atomic_inc(&cache->n_volumes); 246 list_add_tail(&volume->proc_link, &fscache_volumes); 247 fscache_see_volume(volume, fscache_volume_new_acquire); 248 fscache_stat(&fscache_n_volumes); 249 up_write(&fscache_addremove_sem); 250 _leave(" = v=%x", volume->debug_id); 251 return volume; 252 253err_vol: 254 kfree(volume); 255err_cache: 256 fscache_put_cache(cache, fscache_cache_put_alloc_volume); 257 fscache_stat(&fscache_n_volumes_nomem); 258 return NULL; 259} 260 261/* 262 * Create a volume's representation on disk. Have a volume ref and a cache 263 * access we have to release. 264 */ 265static void fscache_create_volume_work(struct work_struct *work) 266{ 267 const struct fscache_cache_ops *ops; 268 struct fscache_volume *volume = 269 container_of(work, struct fscache_volume, work); 270 271 fscache_see_volume(volume, fscache_volume_see_create_work); 272 273 ops = volume->cache->ops; 274 if (ops->acquire_volume) 275 ops->acquire_volume(volume); 276 fscache_end_cache_access(volume->cache, 277 fscache_access_acquire_volume_end); 278 279 clear_bit_unlock(FSCACHE_VOLUME_CREATING, &volume->flags); 280 wake_up_bit(&volume->flags, FSCACHE_VOLUME_CREATING); 281 fscache_put_volume(volume, fscache_volume_put_create_work); 282} 283 284/* 285 * Dispatch a worker thread to create a volume's representation on disk. 286 */ 287void fscache_create_volume(struct fscache_volume *volume, bool wait) 288{ 289 if (test_and_set_bit(FSCACHE_VOLUME_CREATING, &volume->flags)) 290 goto maybe_wait; 291 if (volume->cache_priv) 292 goto no_wait; /* We raced */ 293 if (!fscache_begin_cache_access(volume->cache, 294 fscache_access_acquire_volume)) 295 goto no_wait; 296 297 fscache_get_volume(volume, fscache_volume_get_create_work); 298 if (!schedule_work(&volume->work)) 299 fscache_put_volume(volume, fscache_volume_put_create_work); 300 301maybe_wait: 302 if (wait) { 303 fscache_see_volume(volume, fscache_volume_wait_create_work); 304 wait_on_bit(&volume->flags, FSCACHE_VOLUME_CREATING, 305 TASK_UNINTERRUPTIBLE); 306 } 307 return; 308no_wait: 309 clear_bit_unlock(FSCACHE_VOLUME_CREATING, &volume->flags); 310 wake_up_bit(&volume->flags, FSCACHE_VOLUME_CREATING); 311} 312 313/* 314 * Acquire a volume representation cookie and link it to a (proposed) cache. 315 */ 316struct fscache_volume *__fscache_acquire_volume(const char *volume_key, 317 const char *cache_name, 318 const void *coherency_data, 319 size_t coherency_len) 320{ 321 struct fscache_volume *volume; 322 323 volume = fscache_alloc_volume(volume_key, cache_name, 324 coherency_data, coherency_len); 325 if (!volume) 326 return ERR_PTR(-ENOMEM); 327 328 if (!fscache_hash_volume(volume)) { 329 fscache_put_volume(volume, fscache_volume_put_hash_collision); 330 return ERR_PTR(-EBUSY); 331 } 332 333 fscache_create_volume(volume, false); 334 return volume; 335} 336EXPORT_SYMBOL(__fscache_acquire_volume); 337 338static void fscache_wake_pending_volume(struct fscache_volume *volume, 339 struct hlist_bl_head *h) 340{ 341 struct fscache_volume *cursor; 342 struct hlist_bl_node *p; 343 344 hlist_bl_for_each_entry(cursor, p, h, hash_link) { 345 if (fscache_volume_same(cursor, volume)) { 346 fscache_see_volume(cursor, fscache_volume_see_hash_wake); 347 clear_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &cursor->flags); 348 wake_up_bit(&cursor->flags, FSCACHE_VOLUME_ACQUIRE_PENDING); 349 return; 350 } 351 } 352} 353 354/* 355 * Remove a volume cookie from the hash table. 356 */ 357static void fscache_unhash_volume(struct fscache_volume *volume) 358{ 359 struct hlist_bl_head *h; 360 unsigned int bucket; 361 362 bucket = volume->key_hash & (ARRAY_SIZE(fscache_volume_hash) - 1); 363 h = &fscache_volume_hash[bucket]; 364 365 hlist_bl_lock(h); 366 hlist_bl_del(&volume->hash_link); 367 if (test_bit(FSCACHE_VOLUME_COLLIDED_WITH, &volume->flags)) 368 fscache_wake_pending_volume(volume, h); 369 hlist_bl_unlock(h); 370} 371 372/* 373 * Drop a cache's volume attachments. 374 */ 375static void fscache_free_volume(struct fscache_volume *volume) 376{ 377 struct fscache_cache *cache = volume->cache; 378 379 if (volume->cache_priv) { 380 __fscache_begin_volume_access(volume, NULL, 381 fscache_access_relinquish_volume); 382 if (volume->cache_priv) 383 cache->ops->free_volume(volume); 384 fscache_end_volume_access(volume, NULL, 385 fscache_access_relinquish_volume_end); 386 } 387 388 down_write(&fscache_addremove_sem); 389 list_del_init(&volume->proc_link); 390 atomic_dec(&volume->cache->n_volumes); 391 up_write(&fscache_addremove_sem); 392 393 if (!hlist_bl_unhashed(&volume->hash_link)) 394 fscache_unhash_volume(volume); 395 396 trace_fscache_volume(volume->debug_id, 0, fscache_volume_free); 397 kfree(volume->key); 398 kfree(volume); 399 fscache_stat_d(&fscache_n_volumes); 400 fscache_put_cache(cache, fscache_cache_put_volume); 401} 402 403/* 404 * Drop a reference to a volume cookie. 405 */ 406void fscache_put_volume(struct fscache_volume *volume, 407 enum fscache_volume_trace where) 408{ 409 if (volume) { 410 unsigned int debug_id = volume->debug_id; 411 bool zero; 412 int ref; 413 414 zero = __refcount_dec_and_test(&volume->ref, &ref); 415 trace_fscache_volume(debug_id, ref - 1, where); 416 if (zero) 417 fscache_free_volume(volume); 418 } 419} 420 421/* 422 * Relinquish a volume representation cookie. 423 */ 424void __fscache_relinquish_volume(struct fscache_volume *volume, 425 const void *coherency_data, 426 bool invalidate) 427{ 428 if (WARN_ON(test_and_set_bit(FSCACHE_VOLUME_RELINQUISHED, &volume->flags))) 429 return; 430 431 if (invalidate) { 432 set_bit(FSCACHE_VOLUME_INVALIDATE, &volume->flags); 433 } else if (coherency_data) { 434 memcpy(volume->coherency, coherency_data, volume->coherency_len); 435 } 436 437 fscache_put_volume(volume, fscache_volume_put_relinquish); 438} 439EXPORT_SYMBOL(__fscache_relinquish_volume); 440 441/** 442 * fscache_withdraw_volume - Withdraw a volume from being cached 443 * @volume: Volume cookie 444 * 445 * Withdraw a cache volume from service, waiting for all accesses to complete 446 * before returning. 447 */ 448void fscache_withdraw_volume(struct fscache_volume *volume) 449{ 450 int n_accesses; 451 452 _debug("withdraw V=%x", volume->debug_id); 453 454 /* Allow wakeups on dec-to-0 */ 455 n_accesses = atomic_dec_return(&volume->n_accesses); 456 trace_fscache_access_volume(volume->debug_id, 0, 457 refcount_read(&volume->ref), 458 n_accesses, fscache_access_cache_unpin); 459 460 wait_var_event(&volume->n_accesses, 461 atomic_read(&volume->n_accesses) == 0); 462} 463EXPORT_SYMBOL(fscache_withdraw_volume); 464 465#ifdef CONFIG_PROC_FS 466/* 467 * Generate a list of volumes in /proc/fs/fscache/volumes 468 */ 469static int fscache_volumes_seq_show(struct seq_file *m, void *v) 470{ 471 struct fscache_volume *volume; 472 473 if (v == &fscache_volumes) { 474 seq_puts(m, 475 "VOLUME REF nCOOK ACC FL CACHE KEY\n" 476 "======== ===== ===== === == =============== ================\n"); 477 return 0; 478 } 479 480 volume = list_entry(v, struct fscache_volume, proc_link); 481 seq_printf(m, 482 "%08x %5d %5d %3d %02lx %-15.15s %s\n", 483 volume->debug_id, 484 refcount_read(&volume->ref), 485 atomic_read(&volume->n_cookies), 486 atomic_read(&volume->n_accesses), 487 volume->flags, 488 volume->cache->name ?: "-", 489 volume->key + 1); 490 return 0; 491} 492 493static void *fscache_volumes_seq_start(struct seq_file *m, loff_t *_pos) 494 __acquires(&fscache_addremove_sem) 495{ 496 down_read(&fscache_addremove_sem); 497 return seq_list_start_head(&fscache_volumes, *_pos); 498} 499 500static void *fscache_volumes_seq_next(struct seq_file *m, void *v, loff_t *_pos) 501{ 502 return seq_list_next(v, &fscache_volumes, _pos); 503} 504 505static void fscache_volumes_seq_stop(struct seq_file *m, void *v) 506 __releases(&fscache_addremove_sem) 507{ 508 up_read(&fscache_addremove_sem); 509} 510 511const struct seq_operations fscache_volumes_seq_ops = { 512 .start = fscache_volumes_seq_start, 513 .next = fscache_volumes_seq_next, 514 .stop = fscache_volumes_seq_stop, 515 .show = fscache_volumes_seq_show, 516}; 517#endif /* CONFIG_PROC_FS */