History

fscache: Implement simple cookie state machine

Implement a very simple cookie state machine to handle lookup, invalidation, withdrawal, relinquishment and, to be added later, commit on LRU discard. Three cache methods are provided: ->lookup_cookie() to look up and, if necessary, create a data storage object; ->withdraw_cookie() to free the resources associated with that object and potentially delete it; and ->prepare_to_write(), to do prepare for changes to the cached data to be modified locally. Changes ======= ver #3: - Fix a race between LRU discard and relinquishment whereby the former would override the latter and thus the latter would never happen[1]. ver #2: - Don't hold n_accesses elevated whilst cache is bound to a cookie, but rather add a flag that prevents the state machine from being queued when n_accesses reaches 0.

David Howells committed 5 years ago

Commit 5d00e421 parent 29f18e73 files · +300 −44Browse files

Unified · Split

3 files
  1. fs/fscache/cookie.c +271 −42
  2. include/linux/fscache-cache.h +25 −2
  3. include/trace/events/fscache.h +4 −0
fs/fscache/cookie.c+271−42
@@ -15,7 +15,8 @@ · 14 unmodified lines
1515
1616 struct kmem_cache *fscache_cookie_jar;
1717
18−static void fscache_drop_cookie(struct fscache_cookie *cookie);
18+static void fscache_cookie_worker(struct work_struct *work);
19+static void fscache_unhash_cookie(struct fscache_cookie *cookie);
1920
2021 #define fscache_cookie_hash_shift 15
2122 static struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_shift];
@@ -62,6 +63,19 @@ static void fscache_free_cookie(struct fscache_cookie *cookie) · 40 unmodified lines
6263 kmem_cache_free(fscache_cookie_jar, cookie);
6364 }
6465
66+static void __fscache_queue_cookie(struct fscache_cookie *cookie)
67+{
68+ if (!queue_work(fscache_wq, &cookie->work))
69+ fscache_put_cookie(cookie, fscache_cookie_put_over_queued);
70+}
71+
72+static void fscache_queue_cookie(struct fscache_cookie *cookie,
73+ enum fscache_cookie_trace where)
74+{
75+ fscache_get_cookie(cookie, where);
76+ __fscache_queue_cookie(cookie);
77+}
78+
6579 /*
6680 * Initialise the access gate on a cookie by setting a flag to prevent the
6781 * state machine from being queued when the access counter transitions to 0.
@@ -98,9 +112,8 @@ void fscache_end_cookie_access(struct fscache_cookie *cookie, · 30 unmodified lines
98112 trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref),
99113 n_accesses, why);
100114 if (n_accesses == 0 &&
101− !test_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->flags)) {
102− // PLACEHOLDER: Need to poke the state machine
103− }
115+ !test_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->flags))
116+ fscache_queue_cookie(cookie, fscache_cookie_get_end_access);
104117 }
105118 EXPORT_SYMBOL(fscache_end_cookie_access);
106119
@@ -171,35 +184,58 @@ static inline void wake_up_cookie_state(struct fscache_cookie *cookie) · 64 unmodified lines
171184 wake_up_var(&cookie->state);
172185 }
173186
187+/*
188+ * Change the state a cookie is at and wake up anyone waiting for that. Impose
189+ * an ordering between the stuff stored in the cookie and the state member.
190+ * Paired with fscache_cookie_state().
191+ */
174192 static void __fscache_set_cookie_state(struct fscache_cookie *cookie,
175193 enum fscache_cookie_state state)
176194 {
177− cookie->state = state;
195+ smp_store_release(&cookie->state, state);
178196 }
179197
180−/*
181− * Change the state a cookie is at and wake up anyone waiting for that - but
182− * only if the cookie isn't already marked as being in a cleanup state.
183− */
184−void fscache_set_cookie_state(struct fscache_cookie *cookie,
185− enum fscache_cookie_state state)
198+static void fscache_set_cookie_state(struct fscache_cookie *cookie,
199+ enum fscache_cookie_state state)
186200 {
187− bool changed = false;
188−
189201 spin_lock(&cookie->lock);
190− switch (cookie->state) {
191− case FSCACHE_COOKIE_STATE_RELINQUISHING:
192− break;
193− default:
194− __fscache_set_cookie_state(cookie, state);
195− changed = true;
196− break;
197− }
202+ __fscache_set_cookie_state(cookie, state);
198203 spin_unlock(&cookie->lock);
199− if (changed)
200− wake_up_cookie_state(cookie);
204+ wake_up_cookie_state(cookie);
205+}
206+
207+/**
208+ * fscache_cookie_lookup_negative - Note negative lookup
209+ * @cookie: The cookie that was being looked up
210+ *
211+ * Note that some part of the metadata path in the cache doesn't exist and so
212+ * we can release any waiting readers in the certain knowledge that there's
213+ * nothing for them to actually read.
214+ *
215+ * This function uses no locking and must only be called from the state machine.
216+ */
217+void fscache_cookie_lookup_negative(struct fscache_cookie *cookie)
218+{
219+ set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags);
220+ fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_CREATING);
221+}
222+EXPORT_SYMBOL(fscache_cookie_lookup_negative);
223+
224+/**
225+ * fscache_caching_failed - Report that a failure stopped caching on a cookie
226+ * @cookie: The cookie that was affected
227+ *
228+ * Tell fscache that caching on a cookie needs to be stopped due to some sort
229+ * of failure.
230+ *
231+ * This function uses no locking and must only be called from the state machine.
232+ */
233+void fscache_caching_failed(struct fscache_cookie *cookie)
234+{
235+ clear_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags);
236+ fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_FAILED);
201237 }
202−EXPORT_SYMBOL(fscache_set_cookie_state);
238+EXPORT_SYMBOL(fscache_caching_failed);
203239
204240 /*
205241 * Set the index key in a cookie. The cookie struct has space for a 16-byte
@@ -291,10 +327,10 @@ static struct fscache_cookie *fscache_alloc_cookie( · 85 unmodified lines
291327
292328 refcount_set(&cookie->ref, 1);
293329 cookie->debug_id = atomic_inc_return(&fscache_cookie_debug_id);
294− cookie->state = FSCACHE_COOKIE_STATE_QUIESCENT;
295330 spin_lock_init(&cookie->lock);
296331 INIT_LIST_HEAD(&cookie->commit_link);
297− INIT_WORK(&cookie->work, NULL /* PLACEHOLDER */);
332+ INIT_WORK(&cookie->work, fscache_cookie_worker);
333+ __fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT);
298334
299335 write_lock(&fscache_cookies_lock);
300336 list_add_tail(&cookie->proc_link, &fscache_cookies);
@@ -417,6 +453,192 @@ struct fscache_cookie *__fscache_acquire_cookie( · 116 unmodified lines
417453 }
418454 EXPORT_SYMBOL(__fscache_acquire_cookie);
419455
456+/*
457+ * Prepare a cache object to be written to.
458+ */
459+static void fscache_prepare_to_write(struct fscache_cookie *cookie)
460+{
461+ cookie->volume->cache->ops->prepare_to_write(cookie);
462+}
463+
464+/*
465+ * Look up a cookie in the cache.
466+ */
467+static void fscache_perform_lookup(struct fscache_cookie *cookie)
468+{
469+ enum fscache_access_trace trace = fscache_access_lookup_cookie_end_failed;
470+ bool need_withdraw = false;
471+
472+ _enter("");
473+
474+ if (!cookie->volume->cache_priv) {
475+ fscache_create_volume(cookie->volume, true);
476+ if (!cookie->volume->cache_priv) {
477+ fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT);
478+ goto out;
479+ }
480+ }
481+
482+ if (!cookie->volume->cache->ops->lookup_cookie(cookie)) {
483+ if (cookie->state != FSCACHE_COOKIE_STATE_FAILED)
484+ fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT);
485+ need_withdraw = true;
486+ _leave(" [fail]");
487+ goto out;
488+ }
489+
490+ fscache_see_cookie(cookie, fscache_cookie_see_active);
491+ fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_ACTIVE);
492+ trace = fscache_access_lookup_cookie_end;
493+
494+out:
495+ fscache_end_cookie_access(cookie, trace);
496+ if (need_withdraw)
497+ fscache_withdraw_cookie(cookie);
498+ fscache_end_volume_access(cookie->volume, cookie, trace);
499+}
500+
501+/*
502+ * Perform work upon the cookie, such as committing its cache state,
503+ * relinquishing it or withdrawing the backing cache. We're protected from the
504+ * cache going away under us as object withdrawal must come through this
505+ * non-reentrant work item.
506+ */
507+static void fscache_cookie_state_machine(struct fscache_cookie *cookie)
508+{
509+ enum fscache_cookie_state state;
510+ bool wake = false;
511+
512+ _enter("c=%x", cookie->debug_id);
513+
514+again:
515+ spin_lock(&cookie->lock);
516+again_locked:
517+ state = cookie->state;
518+ switch (state) {
519+ case FSCACHE_COOKIE_STATE_QUIESCENT:
520+ /* The QUIESCENT state is jumped to the LOOKING_UP state by
521+ * fscache_use_cookie().
522+ */
523+
524+ if (atomic_read(&cookie->n_accesses) == 0 &&
525+ test_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags)) {
526+ __fscache_set_cookie_state(cookie,
527+ FSCACHE_COOKIE_STATE_RELINQUISHING);
528+ wake = true;
529+ goto again_locked;
530+ }
531+ break;
532+
533+ case FSCACHE_COOKIE_STATE_LOOKING_UP:
534+ spin_unlock(&cookie->lock);
535+ fscache_init_access_gate(cookie);
536+ fscache_perform_lookup(cookie);
537+ goto again;
538+
539+ case FSCACHE_COOKIE_STATE_ACTIVE:
540+ if (test_and_clear_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags)) {
541+ spin_unlock(&cookie->lock);
542+ fscache_prepare_to_write(cookie);
543+ spin_lock(&cookie->lock);
544+ }
545+ fallthrough;
546+
547+ case FSCACHE_COOKIE_STATE_FAILED:
548+ if (atomic_read(&cookie->n_accesses) != 0)
549+ break;
550+ if (test_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags)) {
551+ __fscache_set_cookie_state(cookie,
552+ FSCACHE_COOKIE_STATE_RELINQUISHING);
553+ wake = true;
554+ goto again_locked;
555+ }
556+ if (test_bit(FSCACHE_COOKIE_DO_WITHDRAW, &cookie->flags)) {
557+ __fscache_set_cookie_state(cookie,
558+ FSCACHE_COOKIE_STATE_WITHDRAWING);
559+ wake = true;
560+ goto again_locked;
561+ }
562+ break;
563+
564+ case FSCACHE_COOKIE_STATE_RELINQUISHING:
565+ case FSCACHE_COOKIE_STATE_WITHDRAWING:
566+ if (cookie->cache_priv) {
567+ spin_unlock(&cookie->lock);
568+ cookie->volume->cache->ops->withdraw_cookie(cookie);
569+ spin_lock(&cookie->lock);
570+ }
571+
572+ switch (state) {
573+ case FSCACHE_COOKIE_STATE_RELINQUISHING:
574+ fscache_see_cookie(cookie, fscache_cookie_see_relinquish);
575+ fscache_unhash_cookie(cookie);
576+ __fscache_set_cookie_state(cookie,
577+ FSCACHE_COOKIE_STATE_DROPPED);
578+ wake = true;
579+ goto out;
580+ case FSCACHE_COOKIE_STATE_WITHDRAWING:
581+ fscache_see_cookie(cookie, fscache_cookie_see_withdraw);
582+ break;
583+ default:
584+ BUG();
585+ }
586+
587+ clear_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &cookie->flags);
588+ clear_bit(FSCACHE_COOKIE_DO_WITHDRAW, &cookie->flags);
589+ clear_bit(FSCACHE_COOKIE_DO_LRU_DISCARD, &cookie->flags);
590+ clear_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags);
591+ set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags);
592+ __fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT);
593+ wake = true;
594+ goto again_locked;
595+
596+ case FSCACHE_COOKIE_STATE_DROPPED:
597+ break;
598+
599+ default:
600+ WARN_ONCE(1, "Cookie %x in unexpected state %u\n",
601+ cookie->debug_id, state);
602+ break;
603+ }
604+
605+out:
606+ spin_unlock(&cookie->lock);
607+ if (wake)
608+ wake_up_cookie_state(cookie);
609+ _leave("");
610+}
611+
612+static void fscache_cookie_worker(struct work_struct *work)
613+{
614+ struct fscache_cookie *cookie = container_of(work, struct fscache_cookie, work);
615+
616+ fscache_see_cookie(cookie, fscache_cookie_see_work);
617+ fscache_cookie_state_machine(cookie);
618+ fscache_put_cookie(cookie, fscache_cookie_put_work);
619+}
620+
621+/*
622+ * Wait for the object to become inactive. The cookie's work item will be
623+ * scheduled when someone transitions n_accesses to 0 - but if someone's
624+ * already done that, schedule it anyway.
625+ */
626+static void __fscache_withdraw_cookie(struct fscache_cookie *cookie)
627+{
628+ int n_accesses;
629+ bool unpinned;
630+
631+ unpinned = test_and_clear_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->flags);
632+
633+ /* Need to read the access count after unpinning */
634+ n_accesses = atomic_read(&cookie->n_accesses);
635+ if (unpinned)
636+ trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref),
637+ n_accesses, fscache_access_cache_unpin);
638+ if (n_accesses == 0)
639+ fscache_queue_cookie(cookie, fscache_cookie_get_end_access);
640+}
641+
420642 /*
421643 * Remove a cookie from the hash table.
422644 */
@@ -432,21 +654,27 @@ static void fscache_unhash_cookie(struct fscache_cookie *cookie) · 9 unmodified lines
432654 hlist_bl_del(&cookie->hash_link);
433655 clear_bit(FSCACHE_COOKIE_IS_HASHED, &cookie->flags);
434656 hlist_bl_unlock(h);
657+ fscache_stat(&fscache_n_relinquishes_dropped);
435658 }
436659
437−/*
438− * Finalise a cookie after all its resources have been disposed of.
439− */
440−static void fscache_drop_cookie(struct fscache_cookie *cookie)
660+static void fscache_drop_withdraw_cookie(struct fscache_cookie *cookie)
441661 {
442− spin_lock(&cookie->lock);
443− __fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_DROPPED);
444− spin_unlock(&cookie->lock);
445− wake_up_cookie_state(cookie);
662+ __fscache_withdraw_cookie(cookie);
663+}
446664
447− fscache_unhash_cookie(cookie);
448− fscache_stat(&fscache_n_relinquishes_dropped);
665+/**
666+ * fscache_withdraw_cookie - Mark a cookie for withdrawal
667+ * @cookie: The cookie to be withdrawn.
668+ *
669+ * Allow the cache backend to withdraw the backing for a cookie for its own
670+ * reasons, even if that cookie is in active use.
671+ */
672+void fscache_withdraw_cookie(struct fscache_cookie *cookie)
673+{
674+ set_bit(FSCACHE_COOKIE_DO_WITHDRAW, &cookie->flags);
675+ fscache_drop_withdraw_cookie(cookie);
449676 }
677+EXPORT_SYMBOL(fscache_withdraw_cookie);
450678
451679 /*
452680 * Allow the netfs to release a cookie back to the cache.
@@ -473,12 +701,13 @@ void __fscache_relinquish_cookie(struct fscache_cookie *cookie, bool retire) · 20 unmodified lines
473701 ASSERTCMP(atomic_read(&cookie->volume->n_cookies), >, 0);
474702 atomic_dec(&cookie->volume->n_cookies);
475703
476− set_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags);
477−
478− if (test_bit(FSCACHE_COOKIE_HAS_BEEN_CACHED, &cookie->flags))
479− ; // PLACEHOLDER: Do something here if the cookie was cached
480− else
481− fscache_drop_cookie(cookie);
704+ if (test_bit(FSCACHE_COOKIE_HAS_BEEN_CACHED, &cookie->flags)) {
705+ set_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags);
706+ fscache_drop_withdraw_cookie(cookie);
707+ } else {
708+ fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_DROPPED);
709+ fscache_unhash_cookie(cookie);
710+ }
482711 fscache_put_cookie(cookie, fscache_cookie_put_relinquish);
483712 }
484713 EXPORT_SYMBOL(__fscache_relinquish_cookie);
include/linux/fscache-cache.h+25−2
@@ -57,6 +57,15 @@ struct fscache_cache_ops { · 56 unmodified lines
5757
5858 /* Free the cache's data attached to a volume */
5959 void (*free_volume)(struct fscache_volume *volume);
60+
61+ /* Look up a cookie in the cache */
62+ bool (*lookup_cookie)(struct fscache_cookie *cookie);
63+
64+ /* Withdraw an object without any cookie access counts held */
65+ void (*withdraw_cookie)(struct fscache_cookie *cookie);
66+
67+ /* Prepare to write to a live cache object */
68+ void (*prepare_to_write)(struct fscache_cookie *cookie);
6069 };
6170
6271 extern struct workqueue_struct *fscache_wq;
@@ -72,6 +81,7 @@ extern int fscache_add_cache(struct fscache_cache *cache, · 9 unmodified lines
7281 void *cache_priv);
7382 extern void fscache_withdraw_cache(struct fscache_cache *cache);
7483 extern void fscache_withdraw_volume(struct fscache_volume *volume);
84+extern void fscache_withdraw_cookie(struct fscache_cookie *cookie);
7585
7686 extern void fscache_io_error(struct fscache_cache *cache);
7787
@@ -85,8 +95,21 @@ extern void fscache_put_cookie(struct fscache_cookie *cookie, · 7 unmodified lines
8595 enum fscache_cookie_trace where);
8696 extern void fscache_end_cookie_access(struct fscache_cookie *cookie,
8797 enum fscache_access_trace why);
88−extern void fscache_set_cookie_state(struct fscache_cookie *cookie,
89− enum fscache_cookie_state state);
98+extern void fscache_cookie_lookup_negative(struct fscache_cookie *cookie);
99+extern void fscache_caching_failed(struct fscache_cookie *cookie);
100+
101+/**
102+ * fscache_cookie_state - Read the state of a cookie
103+ * @cookie: The cookie to query
104+ *
105+ * Get the state of a cookie, imposing an ordering between the cookie contents
106+ * and the state value. Paired with fscache_set_cookie_state().
107+ */
108+static inline
109+enum fscache_cookie_state fscache_cookie_state(struct fscache_cookie *cookie)
110+{
111+ return smp_load_acquire(&cookie->state);
112+}
90113
91114 /**
92115 * fscache_get_key - Get a pointer to the cookie key
include/trace/events/fscache.h+4−0
@@ -68,6 +68,8 @@ enum fscache_access_trace { · 67 unmodified lines
6868 fscache_access_acquire_volume_end,
6969 fscache_access_cache_pin,
7070 fscache_access_cache_unpin,
71+ fscache_access_lookup_cookie_end,
72+ fscache_access_lookup_cookie_end_failed,
7173 fscache_access_relinquish_volume,
7274 fscache_access_relinquish_volume_end,
7375 fscache_access_unlive,
@@ -124,6 +126,8 @@ enum fscache_access_trace { · 50 unmodified lines
124126 EM(fscache_access_acquire_volume_end, "END acq_vol") \
125127 EM(fscache_access_cache_pin, "PIN cache ") \
126128 EM(fscache_access_cache_unpin, "UNPIN cache ") \
129+ EM(fscache_access_lookup_cookie_end, "END lookup ") \
130+ EM(fscache_access_lookup_cookie_end_failed,"END lookupf") \
127131 EM(fscache_access_relinquish_volume, "BEGIN rlq_vol") \
128132 EM(fscache_access_relinquish_volume_end,"END rlq_vol") \
129133 E_(fscache_access_unlive, "END unlive ")

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t