Skip to content

Commit 5d00e42

Browse files
committed
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. Signed-off-by: David Howells <dhowells@redhat.com> Reviewed-by: Jeff Layton <jlayton@kernel.org> cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/599331.1639410068@warthog.procyon.org.uk/ [1] Link: https://lore.kernel.org/r/163819599657.215744.15799615296912341745.stgit@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906903925.143852.1805855338154353867.stgit@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/163967105456.1823006.14730395299835841776.stgit@warthog.procyon.org.uk/ # v3 Link: https://lore.kernel.org/r/164021510706.640689.7961423370243272583.stgit@warthog.procyon.org.uk/ # v4
1 parent 29f18e7 commit 5d00e42

File tree

3 files changed

+300
-44
lines changed

3 files changed

+300
-44
lines changed

fs/fscache/cookie.c

Lines changed: 271 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
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)
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,
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)
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(
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(
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)
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)
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);

0 commit comments

Comments
 (0)