~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/netfs/fscache_volume.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php